LibreOffice Module desktop (master) 1
init.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10#include <sfx2/lokhelper.hxx>
11#include <config_buildconfig.h>
12#include <config_features.h>
13
14#include <stdio.h>
15#include <string.h>
16#include <stdlib.h>
17
18#ifdef IOS
19#include <sys/mman.h>
20#include <sys/stat.h>
21#include <unicode/udata.h>
22#include <unicode/ucnv.h>
23#include <premac.h>
24#import <Foundation/Foundation.h>
25#import <CoreGraphics/CoreGraphics.h>
26#include <postmac.h>
27#endif
28
29#undef HAVE_MALLOC_TRIM
30
31#ifdef LINUX
32#include <fcntl.h>
33#if defined __GLIBC__
34# include <malloc.h>
35# define HAVE_MALLOC_TRIM
36#endif
37#endif
38
39#ifdef ANDROID
40#include <osl/detail/android-bootstrap.h>
41#endif
42
43#ifdef EMSCRIPTEN
44#include <osl/detail/emscripten-bootstrap.h>
45#endif
46
47#include <algorithm>
48#include <memory>
49#include <iostream>
50#include <string_view>
51
52#include <boost/property_tree/json_parser.hpp>
53#include <boost/algorithm/string.hpp>
54
55#include <LibreOfficeKit/LibreOfficeKit.h>
56#include <LibreOfficeKit/LibreOfficeKitEnums.h>
57
58#include <sal/log.hxx>
59#include <utility>
60#include <vcl/errinf.hxx>
61#include <vcl/lok.hxx>
62#include <o3tl/any.hxx>
64#include <o3tl/string_view.hxx>
65#include <osl/file.hxx>
66#include <osl/process.h>
67#include <osl/thread.h>
68#include <rtl/bootstrap.hxx>
69#include <rtl/strbuf.hxx>
70#include <rtl/uri.hxx>
71#include <svl/zforlist.hxx>
72#include <linguistic/misc.hxx>
74#include <comphelper/base64.hxx>
76#include <comphelper/lok.hxx>
78#include <comphelper/string.hxx>
86
87#include <com/sun/star/document/MacroExecMode.hpp>
88#include <com/sun/star/beans/XPropertySet.hpp>
89#include <com/sun/star/container/XNameAccess.hpp>
90#include <com/sun/star/document/XDocumentLanguages.hpp>
91#include <com/sun/star/frame/Desktop.hpp>
92#include <com/sun/star/frame/DispatchResultEvent.hpp>
93#include <com/sun/star/frame/DispatchResultState.hpp>
94#include <com/sun/star/frame/XDispatchProvider.hpp>
95#include <com/sun/star/frame/XDispatchResultListener.hpp>
96#include <com/sun/star/frame/XSynchronousDispatch.hpp>
97#include <com/sun/star/frame/XStorable.hpp>
98#include <com/sun/star/lang/Locale.hpp>
99#include <com/sun/star/lang/XComponent.hpp>
100#include <com/sun/star/lang/XMultiServiceFactory.hpp>
101#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
102#include <com/sun/star/util/URLTransformer.hpp>
103#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
104#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
105#include <com/sun/star/datatransfer/XTransferable2.hpp>
106#include <com/sun/star/text/TextContentAnchorType.hpp>
107#include <com/sun/star/document/XRedlinesSupplier.hpp>
108#include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
109
110#include <com/sun/star/xml/crypto/SEInitializer.hpp>
111#include <com/sun/star/xml/crypto/XSEInitializer.hpp>
112#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
113#include <com/sun/star/xml/crypto/XCertificateCreator.hpp>
114#include <com/sun/star/security/XCertificate.hpp>
115
116#include <com/sun/star/linguistic2/LanguageGuessing.hpp>
117#include <com/sun/star/linguistic2/LinguServiceManager.hpp>
118#include <com/sun/star/linguistic2/XSpellChecker.hpp>
119#include <com/sun/star/i18n/LocaleCalendar2.hpp>
120#include <com/sun/star/i18n/ScriptType.hpp>
121#include <com/sun/star/lang/DisposedException.hpp>
122
123#include <editeng/flstitem.hxx>
124#ifdef IOS
125#include <sfx2/app.hxx>
126#endif
127#include <sfx2/objsh.hxx>
128#include <sfx2/docfilt.hxx>
129#include <sfx2/docfile.hxx>
130#include <sfx2/viewsh.hxx>
131#include <sfx2/viewfrm.hxx>
132#include <sfx2/msgpool.hxx>
133#include <sfx2/dispatch.hxx>
138#include <svl/numformat.hxx>
139#include <svx/dialmgr.hxx>
140#include <svx/strings.hrc>
141#include <svx/svdview.hxx>
142#include <svx/svxids.hrc>
143#include <svx/ucsubset.hxx>
144#include <vcl/vclevent.hxx>
146#include <vcl/svapp.hxx>
147#include <unotools/resmgr.hxx>
148#include <tools/fract.hxx>
149#include <tools/json_writer.hxx>
150#include <svtools/ctrltool.hxx>
151#include <svtools/langtab.hxx>
152#include <vcl/fontcharmap.hxx>
153#ifdef IOS
154#include <vcl/sysdata.hxx>
155#endif
156#include <vcl/virdev.hxx>
157#include <vcl/ImageTree.hxx>
159#include <vcl/dialoghelper.hxx>
160#ifdef _WIN32
162#endif
163#include <unicode/uchar.h>
169#include <unotools/tempfile.hxx>
171#include <osl/module.hxx>
173#include <sfx2/sfxbasemodel.hxx>
174#include <svl/undo.hxx>
175#include <unotools/datetime.hxx>
178#include <vcl/abstdlg.hxx>
182
183// Needed for getUndoManager()
184#include <com/sun/star/document/XUndoManager.hpp>
185#include <com/sun/star/document/XUndoManagerSupplier.hpp>
186#include <com/sun/star/document/XLinkTargetSupplier.hpp>
187#include <editeng/sizeitem.hxx>
188#include <svx/rulritem.hxx>
189#include <svx/pageitem.hxx>
190
191#include <app.hxx>
192
193#include "../app/cmdlineargs.hxx"
194// We also need to hackily be able to start the main libreoffice thread:
195#include "../app/sofficemain.h"
196#include "../app/officeipcthread.hxx"
197#include <lib/init.hxx>
198
200#include "lokclipboard.hxx"
201#include <officecfg/Office/Common.hxx>
202#include <officecfg/Office/Impress.hxx>
203#include <officecfg/Office/Linguistic.hxx>
205#include <svl/ctloptions.hxx>
207#include <svtools/colorcfg.hxx>
208#include <svtools/miscopt.hxx>
212#include <unotools/fltrcfg.hxx>
213#include <unotools/lingucfg.hxx>
215#include <unotools/searchopt.hxx>
218#include <vcl/settings.hxx>
219
220#include <officecfg/Setup.hxx>
221#include <com/sun/star/ui/XAcceleratorConfiguration.hpp>
223
224using namespace css;
225using namespace vcl;
226using namespace desktop;
227using namespace utl;
228
229static LibLibreOffice_Impl *gImpl = nullptr;
230static bool lok_preinit_2_called = false;
231static std::weak_ptr< LibreOfficeKitClass > gOfficeClass;
232static std::weak_ptr< LibreOfficeKitDocumentClass > gDocumentClass;
233
234static void SetLastExceptionMsg(const OUString& s = OUString())
235{
236 SAL_WARN_IF(!s.isEmpty(), "lok", "lok exception '" + s + "'");
237 if (gImpl)
239}
240
241namespace {
242
243struct ExtensionMap
244{
245 const char *extn;
246 const char *filterName;
247};
248
249class TraceEventDumper : public AutoTimer
250{
251 static const int dumpTimeoutMS = 5000;
252
253public:
254 TraceEventDumper() : AutoTimer( "Trace Event dumper" )
255 {
256 SetTimeout(dumpTimeoutMS);
257 Start();
258 }
259
260 virtual void Invoke() override
261 {
262 flushRecordings();
263 }
264
265 static void flushRecordings()
266 {
267 const css::uno::Sequence<OUString> aEvents =
269 OStringBuffer aOutput;
270 for (const auto &s : aEvents)
271 {
272 aOutput.append(OUStringToOString(s, RTL_TEXTENCODING_UTF8)
273 + "\n");
274 }
275 if (aOutput.getLength() > 0)
276 {
277 OString aChunk = aOutput.makeStringAndClear();
278 if (gImpl && gImpl->mpCallback)
279 gImpl->mpCallback(LOK_CALLBACK_PROFILE_FRAME, aChunk.getStr(), gImpl->mpCallbackData);
280 }
281 }
282};
283
284} // unnamed namespace
285
286static TraceEventDumper *traceEventDumper = nullptr;
287
288const ExtensionMap aWriterExtensionMap[] =
289{
290 { "doc", "MS Word 97" },
291 { "docm", "MS Word 2007 XML VBA" },
292 { "docx", "MS Word 2007 XML" },
293 { "fodt", "OpenDocument Text Flat XML" },
294 { "html", "HTML (StarWriter)" },
295 { "odt", "writer8" },
296 { "ott", "writer8_template" },
297 { "pdf", "writer_pdf_Export" },
298 { "epub", "EPUB" },
299 { "rtf", "Rich Text Format" },
300 { "txt", "Text" },
301 { "xhtml", "XHTML Writer File" },
302 { "png", "writer_png_Export" },
303 { "xml", "writer_indexing_export" },
304 { nullptr, nullptr }
305};
306
307const ExtensionMap aCalcExtensionMap[] =
308{
309 { "csv", "Text - txt - csv (StarCalc)" },
310 { "fods", "OpenDocument Spreadsheet Flat XML" },
311 { "html", "HTML (StarCalc)" },
312 { "ods", "calc8" },
313 { "ots", "calc8_template" },
314 { "pdf", "calc_pdf_Export" },
315 { "xhtml", "XHTML Calc File" },
316 { "xls", "MS Excel 97" },
317 { "xlsm", "Calc MS Excel 2007 VBA XML" },
318 { "xlsx", "Calc MS Excel 2007 XML" },
319 { "png", "calc_png_Export" },
320 { nullptr, nullptr }
321};
322
323const ExtensionMap aImpressExtensionMap[] =
324{
325 { "fodp", "OpenDocument Presentation Flat XML" },
326 { "html", "impress_html_Export" },
327 { "odg", "impress8_draw" },
328 { "odp", "impress8" },
329 { "otp", "impress8_template" },
330 { "pdf", "impress_pdf_Export" },
331 { "potm", "Impress MS PowerPoint 2007 XML Template" },
332 { "pot", "MS PowerPoint 97 Vorlage" },
333 { "pptm", "Impress MS PowerPoint 2007 XML VBA" },
334 { "pptx", "Impress MS PowerPoint 2007 XML" },
335 { "pps", "MS PowerPoint 97 Autoplay" },
336 { "ppt", "MS PowerPoint 97" },
337 { "svg", "impress_svg_Export" },
338 { "xhtml", "XHTML Impress File" },
339 { "png", "impress_png_Export"},
340 { nullptr, nullptr }
341};
342
343const ExtensionMap aDrawExtensionMap[] =
344{
345 { "fodg", "draw_ODG_FlatXML" },
346 { "html", "draw_html_Export" },
347 { "odg", "draw8" },
348 { "pdf", "draw_pdf_Export" },
349 { "svg", "draw_svg_Export" },
350 { "xhtml", "XHTML Draw File" },
351 { "png", "draw_png_Export"},
352 { nullptr, nullptr }
353};
354
355static OUString getUString(const char* pString)
356{
357 if (pString == nullptr)
358 return OUString();
359
360 std::string_view sString(pString, strlen(pString));
361 return OStringToOUString(sString, RTL_TEXTENCODING_UTF8);
362}
363
364// Tolerate embedded \0s etc.
365static char *convertOString(const OString &rStr)
366{
367 char* pMemory = static_cast<char*>(malloc(rStr.getLength() + 1));
368 assert(pMemory); // don't tolerate failed allocations.
369 memcpy(pMemory, rStr.getStr(), rStr.getLength() + 1);
370 return pMemory;
371}
372
373static char *convertOUString(std::u16string_view aStr)
374{
375 return convertOString(OUStringToOString(aStr, RTL_TEXTENCODING_UTF8));
376}
377
379static OUString getAbsoluteURL(const char* pURL)
380{
381 OUString aURL(getUString(pURL));
382 if (aURL.isEmpty())
383 return aURL;
384
385 // convert relative paths to absolute ones
386 OUString aWorkingDir;
387 osl_getProcessWorkingDir(&aWorkingDir.pData);
388 if (!aWorkingDir.endsWith("/"))
389 aWorkingDir += "/";
390
391 try
392 {
393 return rtl::Uri::convertRelToAbs(aWorkingDir, aURL);
394 }
395 catch (const rtl::MalformedUriException &)
396 {
397 }
398
399 return OUString();
400}
401
402std::vector<beans::PropertyValue> desktop::jsonToPropertyValuesVector(const char* pJSON)
403{
404 std::vector<beans::PropertyValue> aArguments;
405 if (pJSON && pJSON[0] != '\0')
406 {
408 }
409 return aArguments;
410}
411
412static bool extractLinks(const uno::Reference< container::XNameAccess >& xLinks, bool subcontent, OUStringBuffer& jsonText)
413{
414 const uno::Sequence< OUString > aNames( xLinks->getElementNames() );
415
416 const sal_uLong nLinks = aNames.getLength();
417 const OUString* pNames = aNames.getConstArray();
418 const OUString aProp_LinkDisplayName( "LinkDisplayName" );
419 const OUString aProp_LinkTarget( "com.sun.star.document.LinkTarget" );
420 bool bIsTarget = false;
421 for( sal_uLong i = 0; i < nLinks; i++ )
422 {
423 uno::Any aAny;
424 OUString aLink( *pNames++ );
425
426 bool bError = false;
427 try
428 {
429 aAny = xLinks->getByName( aLink );
430 }
431 catch(const uno::Exception&)
432 {
433 // if the name of the target was invalid (like empty headings)
434 // no object can be provided
435 bError = true;
436 }
437 if(bError)
438 continue;
439
441 if( aAny >>= xTarget )
442 {
443 try
444 {
445 // get name to display
446 aAny = xTarget->getPropertyValue( aProp_LinkDisplayName );
447 OUString aDisplayName;
448 aAny >>= aDisplayName;
449 OUString aStrDisplayname ( aDisplayName );
450
451 if (subcontent)
452 {
453 jsonText.append("\"");
454 jsonText.append(aStrDisplayname);
455 jsonText.append("\": \"");
456 jsonText.append(aLink);
457 jsonText.append("\"");
458 if (i < nLinks-1)
459 {
460 jsonText.append(", ");
461 }
462 }
463 else
464 {
465 uno::Reference< lang::XServiceInfo > xSI( xTarget, uno::UNO_QUERY );
466 bIsTarget = xSI->supportsService( aProp_LinkTarget );
467 if (i != 0)
468 {
469 if (!bIsTarget)
470 jsonText.append("}");
471 if (i < nLinks)
472 {
473 jsonText.append(", ");
474 }
475 }
476 jsonText.append("\"");
477 jsonText.append(aStrDisplayname);
478 jsonText.append("\": ");
479
480 if (bIsTarget)
481 {
482 jsonText.append("true");
483 continue;
484 }
485 jsonText.append("{");
486 }
487
489 if( xLTS.is() )
490 {
491 extractLinks(xLTS->getLinks(), true, jsonText);
492 }
493 }
494 catch(...)
495 {
496 SAL_WARN("lok", "extractLinks: Exception");
497 }
498 }
499 }
500 return bIsTarget;
501}
502
503static void unoAnyToJson(tools::JsonWriter& rJson, std::string_view pNodeName, const uno::Any& anyItem)
504{
505 auto aNode = rJson.startNode(pNodeName);
506 OUString aType = anyItem.getValueTypeName();
507 rJson.put("type", aType);
508
509 if (aType == "string")
510 rJson.put("value", anyItem.get<OUString>());
511 else if (aType == "unsigned long")
512 rJson.put("value", OString::number(anyItem.get<sal_uInt32>()));
513 else if (aType == "long")
514 rJson.put("value", OString::number(anyItem.get<sal_Int32>()));
515 else if (aType == "[]any")
516 {
518 if (anyItem >>= aSeq)
519 {
520 auto valueNode = rJson.startNode("value");
521
522 for (auto i = 0; i < aSeq.getLength(); ++i)
523 {
524 unoAnyToJson(rJson, OString::number(i), aSeq[i]);
525 }
526 }
527 }
528}
529
530static int lcl_getViewId(std::string_view payload);
531
532namespace desktop {
533
535{
536 RectangleAndPart aRet;
537 if (rPayload.startsWith("EMPTY")) // payload starts with "EMPTY"
538 {
541 {
542 int nSeparatorPos = rPayload.indexOf(',', 6);
543 bool bHasMode = nSeparatorPos > 0;
544 if (bHasMode)
545 {
546 aRet.m_nPart = o3tl::toInt32(rPayload.subView(6, nSeparatorPos - 6));
547 assert(rPayload.getLength() > nSeparatorPos);
548 aRet.m_nMode = o3tl::toInt32(rPayload.subView(nSeparatorPos + 1));
549 }
550 else
551 {
552 aRet.m_nPart = o3tl::toInt32(rPayload.subView(6));
553 aRet.m_nMode = 0;
554 }
555 }
556
557 return aRet;
558 }
559
560 // Read '<left>, <top>, <width>, <height>[, <part>, <mode>]'. C++ streams are simpler but slower.
561 const char* pos = rPayload.getStr();
562 const char* end = rPayload.getStr() + rPayload.getLength();
563 tools::Long nLeft = rtl_str_toInt64_WithLength(pos, 10, end - pos);
564 while( *pos != ',' )
565 ++pos;
566 ++pos;
567 assert(pos < end);
568 tools::Long nTop = rtl_str_toInt64_WithLength(pos, 10, end - pos);
569 while( *pos != ',' )
570 ++pos;
571 ++pos;
572 assert(pos < end);
573 tools::Long nWidth = rtl_str_toInt64_WithLength(pos, 10, end - pos);
574 while( *pos != ',' )
575 ++pos;
576 ++pos;
577 assert(pos < end);
578 tools::Long nHeight = rtl_str_toInt64_WithLength(pos, 10, end - pos);
579 tools::Long nPart = INT_MIN;
580 tools::Long nMode = 0;
582 {
583 while( *pos != ',' )
584 ++pos;
585 ++pos;
586 assert(pos < end);
587 nPart = rtl_str_toInt64_WithLength(pos, 10, end - pos);
588
589 while( *pos && *pos != ',' )
590 ++pos;
591 if (*pos)
592 {
593 ++pos;
594 assert(pos < end);
595 nMode = rtl_str_toInt64_WithLength(pos, 10, end - pos);
596 }
597 }
598
599 aRet.m_aRectangle = SanitizedRectangle(nLeft, nTop, nWidth, nHeight);
600 aRet.m_nPart = nPart;
601 aRet.m_nMode = nMode;
602 return aRet;
603}
604
606{
607 if (nWidth <= 0 || nHeight <= 0)
608 return tools::Rectangle();
609
610 // The top-left corner starts at (0, 0).
611 // Anything negative is invalid.
612 if (nLeft < 0)
613 {
614 nWidth += nLeft;
615 nLeft = 0;
616 }
617
618 if (nTop < 0)
619 {
620 nHeight += nTop;
621 nTop = 0;
622 }
623
624 if (nWidth > 0 && nHeight > 0)
625 return tools::Rectangle(nLeft, nTop, nLeft + nWidth, nTop + nHeight);
626 // Else set empty rect.
627 return tools::Rectangle();
628}
629
631{
632 return SanitizedRectangle(rect.Left(), rect.Top(), rect.getOpenWidth(), rect.getOpenHeight());
633}
634
636{
637 if(PayloadString.isEmpty())
638 {
639 // Do to-string conversion on demand, as many calls will get dropped without
640 // needing the string.
641 if(PayloadObject.which() == 1)
643 }
644 return PayloadString;
645}
646
648{
649 PayloadObject = rRectAndPart;
650 PayloadString.clear(); // will be set on demand if needed
651}
652
654{
655 // TODO: In case of unittests, they do not pass invalidations in binary but as text messages.
656 // LO core should preferably always pass binary for performance.
657 if(PayloadObject.which() != 1)
658 PayloadObject = RectangleAndPart::Create(PayloadString);
659 return boost::get<RectangleAndPart>(PayloadObject);
660}
661
662boost::property_tree::ptree& CallbackFlushHandler::CallbackData::setJson(const std::string& payload)
663{
664 boost::property_tree::ptree aTree;
665 std::stringstream aStream(payload);
666 boost::property_tree::read_json(aStream, aTree);
667
668 // Let boost normalize the payload so it always matches the cache.
669 setJson(aTree);
670
671 // Return reference to the cached object.
672 return boost::get<boost::property_tree::ptree>(PayloadObject);
673}
674
675void CallbackFlushHandler::CallbackData::setJson(const boost::property_tree::ptree& rTree)
676{
677 std::stringstream aJSONStream;
678 constexpr bool bPretty = false; // Don't waste time and bloat logs.
679 boost::property_tree::write_json(aJSONStream, rTree, bPretty);
680 PayloadString = OString(o3tl::trim(aJSONStream.str()));
681
682 PayloadObject = rTree;
683}
684
685const boost::property_tree::ptree& CallbackFlushHandler::CallbackData::getJson() const
686{
687 assert(PayloadObject.which() == 2);
688 return boost::get<boost::property_tree::ptree>(PayloadObject);
689}
690
692{
693 if (isCached())
694 {
695 assert(PayloadObject.which() == 3);
696 return boost::get<int>(PayloadObject);
697 }
698 return lcl_getViewId(getPayload());
699}
700
702{
703 switch (PayloadObject.which())
704 {
705 // Not cached.
706 case 0:
707 return true;
708
709 // RectangleAndPart.
710 case 1:
711 return getRectangleAndPart().toString().getStr() == getPayload();
712
713 // Json.
714 case 2:
715 {
716 std::stringstream aJSONStream;
717 boost::property_tree::write_json(aJSONStream, getJson(), false);
718 const std::string aExpected = boost::trim_copy(aJSONStream.str());
719 return getPayload() == std::string_view(aExpected);
720 }
721
722 // View id.
723 case 3:
724 return getViewId() == lcl_getViewId( getPayload());
725
726 default:
727 assert(!"Unknown variant type; please add an entry to validate.");
728 }
729
730 return false;
731}
732
733} // namespace desktop
734
735static bool lcl_isViewCallbackType(const int type)
736{
737 switch (type)
738 {
739 case LOK_CALLBACK_CELL_VIEW_CURSOR:
740 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
741 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
742 case LOK_CALLBACK_TEXT_VIEW_SELECTION:
743 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
744 return true;
745
746 default:
747 return false;
748 }
749}
750
751static bool isUpdatedType(int type)
752{
753 switch (type)
754 {
755 case LOK_CALLBACK_TEXT_SELECTION:
756 case LOK_CALLBACK_TEXT_SELECTION_START:
757 case LOK_CALLBACK_TEXT_SELECTION_END:
758 return true;
759 default:
760 return false;
761 }
762}
763
764static bool isUpdatedTypePerViewId(int type)
765{
766 switch (type)
767 {
768 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
769 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
770 case LOK_CALLBACK_TEXT_VIEW_SELECTION:
771 return true;
772 default:
773 return false;
774 }
775}
776
777static int lcl_getViewId(std::string_view payload)
778{
779 // this is a cheap way how to get the viewId from a JSON message; proper
780 // parsing is terribly expensive, and we just need the viewId here
781 size_t viewIdPos = payload.find("viewId");
782 if (viewIdPos == std::string::npos)
783 return 0;
784
785 size_t numberPos = payload.find(":", viewIdPos + 6);
786 if (numberPos == std::string::npos)
787 return 0;
788
789 for (++numberPos; numberPos < payload.length(); ++numberPos)
790 {
791 if (payload[numberPos] == ',' || payload[numberPos] == '}' || (payload[numberPos] >= '0' && payload[numberPos] <= '9'))
792 break;
793 }
794
795 if (numberPos < payload.length() && payload[numberPos] >= '0' && payload[numberPos] <= '9')
796 return o3tl::toInt32(payload.substr(numberPos));
797
798 return 0;
799}
800
801namespace {
802
803std::string extractCertificate(const std::string & certificate)
804{
805 const std::string header("-----BEGIN CERTIFICATE-----");
806 const std::string footer("-----END CERTIFICATE-----");
807
808 std::string result;
809
810 size_t pos1 = certificate.find(header);
811 if (pos1 == std::string::npos)
812 return result;
813
814 size_t pos2 = certificate.find(footer, pos1 + 1);
815 if (pos2 == std::string::npos)
816 return result;
817
818 pos1 = pos1 + header.length();
819 pos2 = pos2 - pos1;
820
821 return certificate.substr(pos1, pos2);
822}
823
824std::string extractPrivateKey(const std::string & privateKey)
825{
826 const std::string header("-----BEGIN PRIVATE KEY-----");
827 const std::string footer("-----END PRIVATE KEY-----");
828
829 std::string result;
830
831 size_t pos1 = privateKey.find(header);
832 if (pos1 == std::string::npos)
833 return result;
834
835 size_t pos2 = privateKey.find(footer, pos1 + 1);
836 if (pos2 == std::string::npos)
837 return result;
838
839 pos1 = pos1 + header.length();
840 pos2 = pos2 - pos1;
841
842 return privateKey.substr(pos1, pos2);
843}
844
845OUString lcl_getCurrentDocumentMimeType(const LibLODocument_Impl* pDocument)
846{
847 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
848 if (!pBaseModel)
849 return "";
850
851 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
852 if (!pObjectShell)
853 return "";
854
855 SfxMedium* pMedium = pObjectShell->GetMedium();
856 if (!pMedium)
857 return "";
858
859 auto pFilter = pMedium->GetFilter();
860 if (!pFilter)
861 return "";
862
863 return pFilter->GetMimeType();
864}
865
866// Gets an undo manager to enter and exit undo context. Needed by ToggleOrientation
867css::uno::Reference< css::document::XUndoManager > getUndoManager( const css::uno::Reference< css::frame::XFrame >& rxFrame )
868{
869 const css::uno::Reference< css::frame::XController >& xController = rxFrame->getController();
870 if ( xController.is() )
871 {
872 const css::uno::Reference< css::frame::XModel >& xModel = xController->getModel();
873 if ( xModel.is() )
874 {
875 const css::uno::Reference< css::document::XUndoManagerSupplier > xSuppUndo( xModel, css::uno::UNO_QUERY_THROW );
876 return css::uno::Reference< css::document::XUndoManager >( xSuppUndo->getUndoManager(), css::uno::UNO_SET_THROW );
877 }
878 }
879
880 return css::uno::Reference< css::document::XUndoManager > ();
881}
882
883// Adjusts page margins for Writer doc. Needed by ToggleOrientation
884void ExecuteMarginLRChange(
885 const tools::Long nPageLeftMargin,
886 const tools::Long nPageRightMargin,
887 SvxLongLRSpaceItem* pPageLRMarginItem)
888{
889 pPageLRMarginItem->SetLeft( nPageLeftMargin );
890 pPageLRMarginItem->SetRight( nPageRightMargin );
891 SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_LRSPACE,
892 SfxCallMode::RECORD, { pPageLRMarginItem });
893}
894
895// Adjusts page margins for Writer doc. Needed by ToggleOrientation
896void ExecuteMarginULChange(
897 const tools::Long nPageTopMargin,
898 const tools::Long nPageBottomMargin,
899 SvxLongULSpaceItem* pPageULMarginItem)
900{
901 pPageULMarginItem->SetUpper( nPageTopMargin );
902 pPageULMarginItem->SetLower( nPageBottomMargin );
903 SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_ULSPACE,
904 SfxCallMode::RECORD, { pPageULMarginItem });
905}
906
907// Main function which toggles page orientation of the Writer doc. Needed by ToggleOrientation
908void ExecuteOrientationChange()
909{
911 if (!pViewFrm)
912 return;
913
914 std::unique_ptr<SvxPageItem> pPageItem(new SvxPageItem(SID_ATTR_PAGE));
915
916 // 1mm in twips rounded
917 // This should be in sync with MINBODY in sw/source/uibase/sidebar/PageMarginControl.hxx
919
920 css::uno::Reference< css::document::XUndoManager > mxUndoManager(
921 getUndoManager( pViewFrm->GetFrame().GetFrameInterface() ) );
922
923 if ( mxUndoManager.is() )
924 mxUndoManager->enterUndoContext( "" );
925
926
927 const SvxSizeItem* pSizeItem;
928 pViewFrm->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_SIZE, pSizeItem);
929 std::unique_ptr<SvxSizeItem> pPageSizeItem(pSizeItem->Clone());
930
931 const SvxLongLRSpaceItem* pLRSpaceItem;
932 pViewFrm->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_LRSPACE, pLRSpaceItem);
933 std::unique_ptr<SvxLongLRSpaceItem> pPageLRMarginItem(pLRSpaceItem->Clone());
934
935 const SvxLongULSpaceItem* pULSpaceItem;
936 pViewFrm->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_ULSPACE, pULSpaceItem);
937 std::unique_ptr<SvxLongULSpaceItem> pPageULMarginItem(pULSpaceItem->Clone());
938
939 {
940 bool bIsLandscape = false;
941 if ( pPageSizeItem->GetSize().Width() > pPageSizeItem->GetSize().Height())
942 bIsLandscape = true;
943
944 // toggle page orientation
945 pPageItem->SetLandscape(!bIsLandscape);
946
947
948 // swap the width and height of the page size
949 const tools::Long nRotatedWidth = pPageSizeItem->GetSize().Height();
950 const tools::Long nRotatedHeight = pPageSizeItem->GetSize().Width();
951 pPageSizeItem->SetSize(Size(nRotatedWidth, nRotatedHeight));
952
953
954 // apply changed attributes
956 {
957 SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_SIZE,
958 SfxCallMode::RECORD, { pPageSizeItem.get(), pPageItem.get() });
959 }
960 }
961
962
963 // check, if margin values still fit to the changed page size.
964 // if not, adjust margin values
965 {
966 const tools::Long nML = pPageLRMarginItem->GetLeft();
967 const tools::Long nMR = pPageLRMarginItem->GetRight();
968 const tools::Long nTmpPW = nML + nMR + MINBODY;
969
970 const tools::Long nPW = pPageSizeItem->GetSize().Width();
971
972 if ( nTmpPW > nPW )
973 {
974 if ( nML <= nMR )
975 {
976 ExecuteMarginLRChange( pPageLRMarginItem->GetLeft(), nMR - (nTmpPW - nPW ), pPageLRMarginItem.get() );
977 }
978 else
979 {
980 ExecuteMarginLRChange( nML - (nTmpPW - nPW ), pPageLRMarginItem->GetRight(), pPageLRMarginItem.get() );
981 }
982 }
983
984 const tools::Long nMT = pPageULMarginItem->GetUpper();
985 const tools::Long nMB = pPageULMarginItem->GetLower();
986 const tools::Long nTmpPH = nMT + nMB + MINBODY;
987
988 const tools::Long nPH = pPageSizeItem->GetSize().Height();
989
990 if ( nTmpPH > nPH )
991 {
992 if ( nMT <= nMB )
993 {
994 ExecuteMarginULChange( pPageULMarginItem->GetUpper(), nMB - ( nTmpPH - nPH ), pPageULMarginItem.get() );
995 }
996 else
997 {
998 ExecuteMarginULChange( nMT - ( nTmpPH - nPH ), pPageULMarginItem->GetLower(), pPageULMarginItem.get() );
999 }
1000 }
1001 }
1002
1003 if ( mxUndoManager.is() )
1004 mxUndoManager->leaveUndoContext();
1005}
1006
1007void setupSidebar(std::u16string_view sidebarDeckId = u"")
1008{
1009 SfxViewShell* pViewShell = SfxViewShell::Current();
1010 SfxViewFrame* pViewFrame = pViewShell ? &pViewShell->GetViewFrame() : nullptr;
1011 if (pViewFrame)
1012 {
1013 if (!pViewFrame->GetChildWindow(SID_SIDEBAR))
1014 pViewFrame->SetChildWindow(SID_SIDEBAR, false /* create it */, true /* focus */);
1015
1016 pViewFrame->ShowChildWindow(SID_SIDEBAR, true);
1017
1018 // Force synchronous population of panels
1019 SfxChildWindow *pChild = pViewFrame->GetChildWindow(SID_SIDEBAR);
1020 if (!pChild)
1021 return;
1022
1023 auto pDockingWin = dynamic_cast<sfx2::sidebar::SidebarDockingWindow *>(pChild->GetWindow());
1024 if (!pDockingWin)
1025 return;
1026
1027 pViewFrame->ShowChildWindow( SID_SIDEBAR );
1028
1030 = pDockingWin->GetOrCreateSidebarController();
1031
1032 xController->FadeIn();
1033 xController->RequestOpenDeck();
1034
1035 if (!sidebarDeckId.empty())
1036 {
1037 xController->SwitchToDeck(sidebarDeckId);
1038 }
1039 else
1040 {
1041 xController->SwitchToDefaultDeck();
1042 }
1043
1044 pDockingWin->SyncUpdate();
1045 }
1046 else
1047 SetLastExceptionMsg("No view shell or sidebar");
1048}
1049
1050void hideSidebar()
1051{
1052 SfxViewShell* pViewShell = SfxViewShell::Current();
1053 SfxViewFrame* pViewFrame = pViewShell ? &pViewShell->GetViewFrame() : nullptr;
1054 if (pViewFrame)
1055 pViewFrame->SetChildWindow(SID_SIDEBAR, false , false );
1056 else
1057 SetLastExceptionMsg("No view shell or sidebar");
1058}
1059
1060} // end anonymous namespace
1061
1062// Could be anonymous in principle, but for the unit testing purposes, we
1063// declare it in init.hxx.
1064OUString desktop::extractParameter(OUString& rOptions, std::u16string_view rName)
1065{
1066 OUString aValue;
1067
1068 OUString aNameEquals(OUString::Concat(rName) + "=");
1069 OUString aCommaNameEquals(OUString::Concat(",") + rName + "=");
1070
1071 int nIndex = -1;
1072 if (rOptions.startsWith(aNameEquals))
1073 {
1074 size_t nLen = aNameEquals.getLength();
1075 int nComma = rOptions.indexOf(",", nLen);
1076 if (nComma >= 0)
1077 {
1078 aValue = rOptions.copy(nLen, nComma - nLen);
1079 rOptions = rOptions.copy(nComma + 1);
1080 }
1081 else
1082 {
1083 aValue = rOptions.copy(nLen);
1084 rOptions.clear();
1085 }
1086 }
1087 else if ((nIndex = rOptions.indexOf(aCommaNameEquals)) >= 0)
1088 {
1089 size_t nLen = aCommaNameEquals.getLength();
1090 int nComma = rOptions.indexOf(",", nIndex + nLen);
1091 if (nComma >= 0)
1092 {
1093 aValue = rOptions.copy(nIndex + nLen, nComma - nIndex - nLen);
1094 rOptions = OUString::Concat(rOptions.subView(0, nIndex)) + rOptions.subView(nComma);
1095 }
1096 else
1097 {
1098 aValue = rOptions.copy(nIndex + nLen);
1099 rOptions = rOptions.copy(0, nIndex);
1100 }
1101 }
1102
1103 return aValue;
1104}
1105
1106extern "C"
1107{
1108
1109static void doc_destroy(LibreOfficeKitDocument* pThis);
1110static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* pUrl, const char* pFormat, const char* pFilterOptions);
1111static int doc_getDocumentType(LibreOfficeKitDocument* pThis);
1112static int doc_getParts(LibreOfficeKitDocument* pThis);
1113static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis);
1114static int doc_getPart(LibreOfficeKitDocument* pThis);
1115static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart);
1116static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect);
1117static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate);
1118static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart);
1119static void doc_setPartMode(LibreOfficeKitDocument* pThis, int nPartMode);
1120static int doc_getEditMode(LibreOfficeKitDocument* pThis);
1121static void doc_paintTile(LibreOfficeKitDocument* pThis,
1122 unsigned char* pBuffer,
1123 const int nCanvasWidth, const int nCanvasHeight,
1124 const int nTilePosX, const int nTilePosY,
1125 const int nTileWidth, const int nTileHeight);
1126static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
1127 unsigned char* pBuffer,
1128 const int nPart,
1129 const int nMode,
1130 const int nCanvasWidth, const int nCanvasHeight,
1131 const int nTilePosX, const int nTilePosY,
1132 const int nTileWidth, const int nTileHeight);
1133static int doc_getTileMode(LibreOfficeKitDocument* pThis);
1134static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
1135 long* pWidth,
1136 long* pHeight);
1137static void doc_getDataArea(LibreOfficeKitDocument* pThis,
1138 long nTab,
1139 long* pCol,
1140 long* pRow);
1141static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
1142 const char* pArguments);
1143
1144static void doc_registerCallback(LibreOfficeKitDocument* pThis,
1145 LibreOfficeKitCallback pCallback,
1146 void* pData);
1147static void doc_postKeyEvent(LibreOfficeKitDocument* pThis,
1148 int nType,
1149 int nCharCode,
1150 int nKeyCode);
1151static void doc_setBlockedCommandList(LibreOfficeKitDocument* pThis,
1152 int nViewId,
1153 const char* blockedCommandList);
1154
1155static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis,
1156 unsigned nWindowId,
1157 int nType,
1158 const char* pText);
1159static void doc_removeTextContext(LibreOfficeKitDocument* pThis,
1160 unsigned nLOKWindowId,
1161 int nCharBefore,
1162 int nCharAfter);
1163static void doc_sendDialogEvent(LibreOfficeKitDocument* pThis,
1164 unsigned long long int nLOKWindowId,
1165 const char* pArguments);
1166static void doc_postWindowKeyEvent(LibreOfficeKitDocument* pThis,
1167 unsigned nLOKWindowId,
1168 int nType,
1169 int nCharCode,
1170 int nKeyCode);
1171static void doc_postMouseEvent (LibreOfficeKitDocument* pThis,
1172 int nType,
1173 int nX,
1174 int nY,
1175 int nCount,
1176 int nButtons,
1177 int nModifier);
1178static void doc_postWindowMouseEvent (LibreOfficeKitDocument* pThis,
1179 unsigned nLOKWindowId,
1180 int nType,
1181 int nX,
1182 int nY,
1183 int nCount,
1184 int nButtons,
1185 int nModifier);
1186static void doc_postWindowGestureEvent(LibreOfficeKitDocument* pThis,
1187 unsigned nLOKWindowId,
1188 const char* pType,
1189 int nX,
1190 int nY,
1191 int nOffset);
1192static void doc_postUnoCommand(LibreOfficeKitDocument* pThis,
1193 const char* pCommand,
1194 const char* pArguments,
1195 bool bNotifyWhenFinished);
1196static void doc_setWindowTextSelection(LibreOfficeKitDocument* pThis,
1197 unsigned nLOKWindowId,
1198 bool swap,
1199 int nX,
1200 int nY);
1201static void doc_setTextSelection (LibreOfficeKitDocument* pThis,
1202 int nType,
1203 int nX,
1204 int nY);
1205static char* doc_getTextSelection(LibreOfficeKitDocument* pThis,
1206 const char* pMimeType,
1207 char** pUsedMimeType);
1208static int doc_getSelectionType(LibreOfficeKitDocument* pThis);
1209static int doc_getSelectionTypeAndText(LibreOfficeKitDocument* pThis,
1210 const char* pMimeType,
1211 char** pText,
1212 char** pUsedMimeType);
1213static int doc_getClipboard (LibreOfficeKitDocument* pThis,
1214 const char **pMimeTypes,
1215 size_t *pOutCount,
1216 char ***pOutMimeTypes,
1217 size_t **pOutSizes,
1218 char ***pOutStreams);
1219static int doc_setClipboard (LibreOfficeKitDocument* pThis,
1220 const size_t nInCount,
1221 const char **pInMimeTypes,
1222 const size_t *pInSizes,
1223 const char **pInStreams);
1224static bool doc_paste(LibreOfficeKitDocument* pThis,
1225 const char* pMimeType,
1226 const char* pData,
1227 size_t nSize);
1228static void doc_setGraphicSelection (LibreOfficeKitDocument* pThis,
1229 int nType,
1230 int nX,
1231 int nY);
1232static void doc_resetSelection (LibreOfficeKitDocument* pThis);
1233static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand);
1234static void doc_setClientZoom(LibreOfficeKitDocument* pThis,
1235 int nTilePixelWidth,
1236 int nTilePixelHeight,
1237 int nTileTwipWidth,
1238 int nTileTwipHeight);
1239static void doc_setClientVisibleArea(LibreOfficeKitDocument* pThis, int nX, int nY, int nWidth, int nHeight);
1240static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int nLevel, int nIndex, bool bHidden);
1241static int doc_createView(LibreOfficeKitDocument* pThis);
1242static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis, const char* pOptions);
1243static void doc_destroyView(LibreOfficeKitDocument* pThis, int nId);
1244static void doc_setView(LibreOfficeKitDocument* pThis, int nId);
1245static int doc_getView(LibreOfficeKitDocument* pThis);
1246static int doc_getViewsCount(LibreOfficeKitDocument* pThis);
1247static bool doc_getViewIds(LibreOfficeKitDocument* pThis, int* pArray, size_t nSize);
1248static void doc_setViewLanguage(LibreOfficeKitDocument* pThis, int nId, const char* language);
1249static unsigned char* doc_renderFontOrientation(LibreOfficeKitDocument* pThis,
1250 const char *pFontName,
1251 const char *pChar,
1252 int* pFontWidth,
1253 int* pFontHeight,
1254 int pOrientation);
1255static unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis,
1256 const char *pFontName,
1257 const char *pChar,
1258 int* pFontWidth,
1259 int* pFontHeight);
1260static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart);
1261
1262static void doc_paintWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
1263 const int nX, const int nY,
1264 const int nWidth, const int nHeight);
1265
1266static void doc_paintWindowDPI(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
1267 const int nX, const int nY,
1268 const int nWidth, const int nHeight,
1269 const double fDPIScale);
1270
1271static void doc_paintWindowForView(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
1272 const int nX, const int nY,
1273 const int nWidth, const int nHeight,
1274 const double fDPIScale, int viewId);
1275
1276static void doc_postWindow(LibreOfficeKitDocument* pThis, unsigned
1277 nLOKWindowId, int nAction, const char* pData);
1278
1279static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart);
1280
1281static bool doc_insertCertificate(LibreOfficeKitDocument* pThis,
1282 const unsigned char* pCertificateBinary,
1283 const int nCertificateBinarySize,
1284 const unsigned char* pPrivateKeyBinary,
1285 const int nPrivateKeyBinarySize);
1286
1287static bool doc_addCertificate(LibreOfficeKitDocument* pThis,
1288 const unsigned char* pCertificateBinary,
1289 const int nCertificateBinarySize);
1290
1291static int doc_getSignatureState(LibreOfficeKitDocument* pThis);
1292
1293static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput);
1294
1295static void doc_resizeWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
1296 const int nWidth, const int nHeight);
1297
1298static void doc_completeFunction(LibreOfficeKitDocument* pThis, const char*);
1299
1300
1301static void doc_sendFormFieldEvent(LibreOfficeKitDocument* pThis,
1302 const char* pArguments);
1303
1304static bool doc_renderSearchResult(LibreOfficeKitDocument* pThis,
1305 const char* pSearchResult, unsigned char** pBitmapBuffer,
1306 int* pWidth, int* pHeight, size_t* pByteSize);
1307
1308static void doc_sendContentControlEvent(LibreOfficeKitDocument* pThis, const char* pArguments);
1309
1310static void doc_setViewTimezone(LibreOfficeKitDocument* pThis, int nId, const char* timezone);
1311
1312static void doc_setAccessibilityState(LibreOfficeKitDocument* pThis, int nId, bool bEnabled);
1313
1314static char* doc_getA11yFocusedParagraph(LibreOfficeKitDocument* pThis);
1315
1316static int doc_getA11yCaretPosition(LibreOfficeKitDocument* pThis);
1317} // extern "C"
1318
1319namespace {
1320ITiledRenderable* getTiledRenderable(LibreOfficeKitDocument* pThis)
1321{
1322 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
1323 return dynamic_cast<ITiledRenderable*>(pDocument->mxComponent.get());
1324}
1325
1326#ifndef IOS
1327
1328/*
1329 * Unfortunately clipboard creation using UNO is insanely baroque.
1330 * we also need to ensure that this works for the first view which
1331 * has no clear 'createView' called for it (unfortunately).
1332 */
1333rtl::Reference<LOKClipboard> forceSetClipboardForCurrentView(LibreOfficeKitDocument *pThis)
1334{
1335 ITiledRenderable* pDoc = getTiledRenderable(pThis);
1337
1338 SAL_INFO("lok", "Set to clipboard for view " << xClip.get());
1339 // FIXME: using a hammer here - should not be necessary if all tests used createView.
1340 pDoc->setClipboard(uno::Reference<datatransfer::clipboard::XClipboard>(xClip->getXI(), UNO_QUERY));
1341
1342 return xClip;
1343}
1344
1345#endif
1346
1347const vcl::Font* FindFont(std::u16string_view rFontName)
1348{
1350 if (!pDocSh)
1351 return nullptr;
1352 const SvxFontListItem* pFonts
1353 = static_cast<const SvxFontListItem*>(pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
1354 const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
1355 if (pList && !rFontName.empty())
1356 if (sal_Handle hMetric = pList->GetFirstFontMetric(rFontName))
1357 return &FontList::GetFontMetric(hMetric);
1358 return nullptr;
1359}
1360
1361vcl::Font FindFont_FallbackToDefault(std::u16string_view rFontName)
1362{
1363 if (auto pFound = FindFont(rFontName))
1364 return *pFound;
1365
1366 return OutputDevice::GetDefaultFont(DefaultFontType::SANS_UNICODE, LANGUAGE_NONE,
1367 GetDefaultFontFlags::NONE);
1368}
1369
1370int getDocumentType (LibreOfficeKitDocument* pThis)
1371{
1373
1374 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
1375
1376 try
1377 {
1378 uno::Reference<lang::XServiceInfo> xDocument(pDocument->mxComponent, uno::UNO_QUERY_THROW);
1379
1380 if (xDocument->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
1381 {
1382 return LOK_DOCTYPE_SPREADSHEET;
1383 }
1384 else if (xDocument->supportsService("com.sun.star.presentation.PresentationDocument"))
1385 {
1386 return LOK_DOCTYPE_PRESENTATION;
1387 }
1388 else if (xDocument->supportsService("com.sun.star.drawing.DrawingDocument"))
1389 {
1390 return LOK_DOCTYPE_DRAWING;
1391 }
1392 else if (xDocument->supportsService("com.sun.star.text.TextDocument") || xDocument->supportsService("com.sun.star.text.WebDocument"))
1393 {
1394 return LOK_DOCTYPE_TEXT;
1395 }
1396 else
1397 {
1398 SetLastExceptionMsg("unknown document type");
1399 }
1400 }
1401 catch (const uno::Exception& exception)
1402 {
1403 SetLastExceptionMsg("exception: " + exception.Message);
1404 }
1405 return LOK_DOCTYPE_OTHER;
1406}
1407
1408} // anonymous namespace
1409
1411 : mxComponent(std::move(xComponent))
1412 , mnDocumentId(nDocumentId)
1413{
1414 assert(nDocumentId != -1 && "Cannot set mnDocumentId to -1");
1415
1417 if (!m_pDocumentClass)
1418 {
1419 m_pDocumentClass = std::make_shared<LibreOfficeKitDocumentClass>();
1420
1421 m_pDocumentClass->nSize = sizeof(LibreOfficeKitDocumentClass);
1422
1423 m_pDocumentClass->destroy = doc_destroy;
1424 m_pDocumentClass->saveAs = doc_saveAs;
1425 m_pDocumentClass->getDocumentType = doc_getDocumentType;
1426 m_pDocumentClass->getParts = doc_getParts;
1427 m_pDocumentClass->getPartPageRectangles = doc_getPartPageRectangles;
1428 m_pDocumentClass->getPart = doc_getPart;
1429 m_pDocumentClass->setPart = doc_setPart;
1430 m_pDocumentClass->selectPart = doc_selectPart;
1431 m_pDocumentClass->moveSelectedParts = doc_moveSelectedParts;
1432 m_pDocumentClass->getPartName = doc_getPartName;
1433 m_pDocumentClass->setPartMode = doc_setPartMode;
1434 m_pDocumentClass->getEditMode = doc_getEditMode;
1435 m_pDocumentClass->paintTile = doc_paintTile;
1436 m_pDocumentClass->paintPartTile = doc_paintPartTile;
1437 m_pDocumentClass->getTileMode = doc_getTileMode;
1438 m_pDocumentClass->getDocumentSize = doc_getDocumentSize;
1439 m_pDocumentClass->getDataArea = doc_getDataArea;
1440 m_pDocumentClass->initializeForRendering = doc_initializeForRendering;
1441 m_pDocumentClass->registerCallback = doc_registerCallback;
1442 m_pDocumentClass->postKeyEvent = doc_postKeyEvent;
1443 m_pDocumentClass->postWindowExtTextInputEvent = doc_postWindowExtTextInputEvent;
1444 m_pDocumentClass->removeTextContext = doc_removeTextContext;
1445 m_pDocumentClass->postWindowKeyEvent = doc_postWindowKeyEvent;
1446 m_pDocumentClass->postMouseEvent = doc_postMouseEvent;
1447 m_pDocumentClass->postWindowMouseEvent = doc_postWindowMouseEvent;
1448 m_pDocumentClass->sendDialogEvent = doc_sendDialogEvent;
1449 m_pDocumentClass->postUnoCommand = doc_postUnoCommand;
1450 m_pDocumentClass->setTextSelection = doc_setTextSelection;
1451 m_pDocumentClass->setWindowTextSelection = doc_setWindowTextSelection;
1452 m_pDocumentClass->getTextSelection = doc_getTextSelection;
1453 m_pDocumentClass->getSelectionType = doc_getSelectionType;
1454 m_pDocumentClass->getSelectionTypeAndText = doc_getSelectionTypeAndText;
1455 m_pDocumentClass->getClipboard = doc_getClipboard;
1456 m_pDocumentClass->setClipboard = doc_setClipboard;
1457 m_pDocumentClass->paste = doc_paste;
1458 m_pDocumentClass->setGraphicSelection = doc_setGraphicSelection;
1459 m_pDocumentClass->resetSelection = doc_resetSelection;
1460 m_pDocumentClass->getCommandValues = doc_getCommandValues;
1461 m_pDocumentClass->setClientZoom = doc_setClientZoom;
1462 m_pDocumentClass->setClientVisibleArea = doc_setClientVisibleArea;
1463 m_pDocumentClass->setOutlineState = doc_setOutlineState;
1464
1465 m_pDocumentClass->createView = doc_createView;
1466 m_pDocumentClass->destroyView = doc_destroyView;
1467 m_pDocumentClass->setView = doc_setView;
1468 m_pDocumentClass->getView = doc_getView;
1469 m_pDocumentClass->getViewsCount = doc_getViewsCount;
1470 m_pDocumentClass->getViewIds = doc_getViewIds;
1471
1472 m_pDocumentClass->renderFont = doc_renderFont;
1473 m_pDocumentClass->renderFontOrientation = doc_renderFontOrientation;
1474 m_pDocumentClass->getPartHash = doc_getPartHash;
1475
1476 m_pDocumentClass->paintWindow = doc_paintWindow;
1477 m_pDocumentClass->paintWindowDPI = doc_paintWindowDPI;
1478 m_pDocumentClass->paintWindowForView = doc_paintWindowForView;
1479 m_pDocumentClass->postWindow = doc_postWindow;
1480 m_pDocumentClass->resizeWindow = doc_resizeWindow;
1481
1482 m_pDocumentClass->setViewLanguage = doc_setViewLanguage;
1483
1484 m_pDocumentClass->getPartInfo = doc_getPartInfo;
1485
1486 m_pDocumentClass->insertCertificate = doc_insertCertificate;
1487 m_pDocumentClass->addCertificate = doc_addCertificate;
1488 m_pDocumentClass->getSignatureState = doc_getSignatureState;
1489
1490 m_pDocumentClass->renderShapeSelection = doc_renderShapeSelection;
1491 m_pDocumentClass->postWindowGestureEvent = doc_postWindowGestureEvent;
1492
1493 m_pDocumentClass->createViewWithOptions = doc_createViewWithOptions;
1494 m_pDocumentClass->completeFunction = doc_completeFunction;
1495
1496 m_pDocumentClass->sendFormFieldEvent = doc_sendFormFieldEvent;
1497 m_pDocumentClass->renderSearchResult = doc_renderSearchResult;
1498
1499 m_pDocumentClass->setBlockedCommandList = doc_setBlockedCommandList;
1500
1501 m_pDocumentClass->sendContentControlEvent = doc_sendContentControlEvent;
1502
1503 m_pDocumentClass->setViewTimezone = doc_setViewTimezone;
1504
1505 m_pDocumentClass->setAccessibilityState = doc_setAccessibilityState;
1506
1507 m_pDocumentClass->getA11yFocusedParagraph = doc_getA11yFocusedParagraph;
1508 m_pDocumentClass->getA11yCaretPosition = doc_getA11yCaretPosition;
1509
1511 }
1512 pClass = m_pDocumentClass.get();
1513
1514#ifndef IOS
1515 forceSetClipboardForCurrentView(this);
1516#endif
1517}
1518
1520{
1521 try
1522 {
1523 mxComponent->dispose();
1524 }
1525 catch (const css::lang::DisposedException&)
1526 {
1527 TOOLS_WARN_EXCEPTION("lok", "failed to dispose document");
1528 }
1529}
1530
1531static OUString getGenerator()
1532{
1533 OUString sGenerator(
1534 Translate::ExpandVariables("%PRODUCTNAME %PRODUCTVERSION%PRODUCTEXTENSION (%1)"));
1535 OUString os("$_OS");
1536 ::rtl::Bootstrap::expandMacros(os);
1537 return sGenerator.replaceFirst("%1", os);
1538}
1539
1540extern "C" {
1541
1543 : Timer( "lokit timer callback" )
1544 , mHandler( handler )
1545{
1546 // A second timer with higher priority, it'll ensure we flush in reasonable time if we get too busy
1547 // to get POST_PAINT priority processing. Otherwise it could take a long time to flush.
1548 SetPriority(TaskPriority::DEFAULT);
1549 SetTimeout( 100 ); // 100 ms
1550}
1551
1553{
1554 mHandler->Invoke();
1555}
1556
1557// One of these is created per view to handle events cf. doc_registerCallback
1558CallbackFlushHandler::CallbackFlushHandler(LibreOfficeKitDocument* pDocument, LibreOfficeKitCallback pCallback, void* pData)
1559 : Idle( "lokit idle callback" ),
1560 m_pDocument(pDocument),
1561 m_pCallback(pCallback),
1562 m_pData(pData),
1564 m_TimeoutIdle( this )
1565{
1566 SetPriority(TaskPriority::POST_PAINT);
1567
1568 // Add the states that are safe to skip duplicates on, even when
1569 // not consequent (i.e. do no emit them if unchanged from last).
1570 m_states.emplace(LOK_CALLBACK_TEXT_SELECTION, "NIL");
1571 m_states.emplace(LOK_CALLBACK_GRAPHIC_SELECTION, "NIL");
1572 m_states.emplace(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, "NIL");
1573 m_states.emplace(LOK_CALLBACK_STATE_CHANGED, "NIL");
1574 m_states.emplace(LOK_CALLBACK_MOUSE_POINTER, "NIL");
1575 m_states.emplace(LOK_CALLBACK_CELL_CURSOR, "NIL");
1576 m_states.emplace(LOK_CALLBACK_CELL_FORMULA, "NIL");
1577 m_states.emplace(LOK_CALLBACK_CELL_ADDRESS, "NIL");
1578 m_states.emplace(LOK_CALLBACK_CURSOR_VISIBLE, "NIL");
1579 m_states.emplace(LOK_CALLBACK_SET_PART, "NIL");
1580 m_states.emplace(LOK_CALLBACK_TABLE_SELECTED, "NIL");
1581 m_states.emplace(LOK_CALLBACK_TAB_STOP_LIST, "NIL");
1582 m_states.emplace(LOK_CALLBACK_RULER_UPDATE, "NIL");
1583 m_states.emplace(LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE, "NIL");
1584}
1585
1587{
1588 Stop();
1589}
1590
1591CallbackFlushHandler::queue_type2::iterator CallbackFlushHandler::toQueue2(CallbackFlushHandler::queue_type1::iterator pos)
1592{
1593 int delta = std::distance(m_queue1.begin(), pos);
1594 return m_queue2.begin() + delta;
1595}
1596
1597CallbackFlushHandler::queue_type2::reverse_iterator CallbackFlushHandler::toQueue2(CallbackFlushHandler::queue_type1::reverse_iterator pos)
1598{
1599 int delta = std::distance(m_queue1.rbegin(), pos);
1600 return m_queue2.rbegin() + delta;
1601}
1602
1603void CallbackFlushHandler::setUpdatedType( int nType, bool value )
1604{
1605 assert(isUpdatedType(nType));
1606 if( m_updatedTypes.size() <= o3tl::make_unsigned( nType ))
1607 m_updatedTypes.resize( nType + 1 ); // new are default-constructed, i.e. false
1609 if(value)
1610 startTimer();
1611}
1612
1614{
1615 setUpdatedType( nType, false );
1616}
1617
1618void CallbackFlushHandler::setUpdatedTypePerViewId( int nType, int nViewId, int nSourceViewId, bool value )
1619{
1621 std::vector<PerViewIdData>& types = m_updatedTypesPerViewId[ nViewId ];
1622 if( types.size() <= o3tl::make_unsigned( nType ))
1623 types.resize( nType + 1 ); // new are default-constructed, i.e. 'set' is false
1624 types[ nType ] = PerViewIdData{ value, nSourceViewId };
1625 if(value)
1626 startTimer();
1627}
1628
1630{
1632 bool allViewIds = false;
1633 // Handle specially messages that do not have viewId for backwards compatibility.
1634 if( nType == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR && !comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
1635 allViewIds = true;
1636 if( !allViewIds )
1637 {
1638 setUpdatedTypePerViewId( nType, nViewId, -1, false );
1639 return;
1640 }
1641 for( auto& it : m_updatedTypesPerViewId )
1642 {
1643 std::vector<PerViewIdData>& types = it.second;
1644 if( types.size() >= o3tl::make_unsigned( nType ))
1645 types[ nType ].set = false;
1646 }
1647}
1648
1649void CallbackFlushHandler::libreOfficeKitViewCallback(int nType, const OString& pPayload)
1650{
1651 CallbackData callbackData(pPayload);
1652 queue(nType, callbackData);
1653}
1654
1655void CallbackFlushHandler::libreOfficeKitViewCallbackWithViewId(int nType, const OString& pPayload, int nViewId)
1656{
1657 CallbackData callbackData(pPayload, nViewId);
1658 queue(nType, callbackData);
1659}
1660
1662{
1663 CallbackData callbackData(pRect, nPart, nMode);
1664 queue(LOK_CALLBACK_INVALIDATE_TILES, callbackData);
1665}
1666
1668{
1669 assert(isUpdatedType( nType ));
1670 std::unique_lock<std::recursive_mutex> lock(m_mutex);
1671 SAL_INFO("lok", "Updated: [" << nType << "]");
1672 setUpdatedType(nType, true);
1673}
1674
1675void CallbackFlushHandler::libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId, int nSourceViewId)
1676{
1677 assert(isUpdatedTypePerViewId( nType ));
1678 std::unique_lock<std::recursive_mutex> lock(m_mutex);
1679 SAL_INFO("lok", "Updated: [" << nType << "]");
1680 setUpdatedTypePerViewId(nType, nViewId, nSourceViewId, true);
1681}
1682
1683void CallbackFlushHandler::dumpState(rtl::OStringBuffer &rState)
1684{
1685 // NB. no locking
1686 rState.append("\nView:\t");
1687 rState.append(static_cast<sal_Int32>(m_viewId));
1688 rState.append("\n\tDisableCallbacks:\t");
1689 rState.append(static_cast<sal_Int32>(m_nDisableCallbacks));
1690 rState.append("\n\tStates:\n");
1691 for (const auto &i : m_states)
1692 {
1693 rState.append("\n\t\t");
1694 rState.append(static_cast<sal_Int32>(i.first));
1695 rState.append("\t");
1696 rState.append(i.second);
1697 }
1698}
1699
1701{
1702 // Invoke() will call flushPendingLOKInvalidateTiles(), so just make sure the timer is active.
1703 startTimer();
1704}
1705
1706void CallbackFlushHandler::queue(const int type, const OString& data)
1707{
1708 CallbackData callbackData(data);
1709 queue(type, callbackData);
1710}
1711
1712void CallbackFlushHandler::queue(const int type, CallbackData& aCallbackData)
1713{
1714 comphelper::ProfileZone aZone("CallbackFlushHandler::queue");
1715
1716 SAL_INFO("lok", "Queue: [" << type << "]: [" << aCallbackData.getPayload() << "] on " << m_queue1.size() << " entries.");
1717
1718 bool bIsChartActive = false;
1719 bool bIsComment = false;
1720 if (type == LOK_CALLBACK_GRAPHIC_SELECTION)
1721 {
1722 LokChartHelper aChartHelper(SfxViewShell::Current());
1723 bIsChartActive = aChartHelper.GetWindow() != nullptr;
1724 }
1725 else if (type == LOK_CALLBACK_COMMENT)
1726 {
1727 bIsComment = true;
1728 }
1729
1730 if (callbacksDisabled() && !bIsChartActive && !bIsComment)
1731 {
1732 // We drop notifications when this is set, except for important ones.
1733 // When we issue a complex command (such as .uno:InsertAnnotation)
1734 // there will be multiple notifications. On the first invalidation
1735 // we will start painting, but other events will get fired
1736 // while the complex command in question executes.
1737 // We don't want to suppress everything here on the wrong assumption
1738 // that no new events are fired during painting.
1739 if (type != LOK_CALLBACK_STATE_CHANGED &&
1740 type != LOK_CALLBACK_INVALIDATE_TILES &&
1741 type != LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR &&
1742 type != LOK_CALLBACK_CURSOR_VISIBLE &&
1743 type != LOK_CALLBACK_VIEW_CURSOR_VISIBLE &&
1744 type != LOK_CALLBACK_TEXT_SELECTION &&
1745 type != LOK_CALLBACK_TEXT_SELECTION_START &&
1746 type != LOK_CALLBACK_TEXT_SELECTION_END &&
1747 type != LOK_CALLBACK_MEDIA_SHAPE &&
1748 type != LOK_CALLBACK_REFERENCE_MARKS)
1749 {
1750 SAL_INFO("lok", "Skipping while painting [" << type << "]: [" << aCallbackData.getPayload() << "].");
1751 return;
1752 }
1753
1754 // In Writer we drop all notifications during painting.
1755 if (doc_getDocumentType(m_pDocument) == LOK_DOCTYPE_TEXT)
1756 return;
1757 }
1758
1759 // Suppress invalid payloads.
1760 if (type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR &&
1761 aCallbackData.getPayload().indexOf(", 0, 0, ") != -1 &&
1762 aCallbackData.getPayload().indexOf("\"hyperlink\":\"\"") == -1 &&
1763 aCallbackData.getPayload().indexOf("\"hyperlink\": {}") == -1)
1764 {
1765 // The cursor position is often the relative coordinates of the widget
1766 // issuing it, instead of the absolute one that we expect.
1767 // This is temporary however, and, once the control is created and initialized
1768 // correctly, it eventually emits the correct absolute coordinates.
1769 SAL_INFO("lok", "Skipping invalid event [" << type << "]: [" << aCallbackData.getPayload() << "].");
1770 return;
1771 }
1772
1773 std::unique_lock<std::recursive_mutex> lock(m_mutex);
1774
1775 // Update types should be received via the updated callbacks for performance,
1776 // getting them as normal callbacks is technically not wrong, but probably should be avoided.
1777 // Reset the updated flag if we get a normal message.
1778 if(isUpdatedType(type))
1779 {
1780 SAL_INFO("lok", "Received event with updated type [" << type << "] as normal callback");
1782 }
1784 {
1785 SAL_INFO("lok", "Received event with updated type [" << type << "] as normal callback");
1786 resetUpdatedTypePerViewId(type, aCallbackData.getViewId());
1787 }
1788
1789 // drop duplicate callbacks for the listed types
1790 switch (type)
1791 {
1792 case LOK_CALLBACK_TEXT_SELECTION_START:
1793 case LOK_CALLBACK_TEXT_SELECTION_END:
1794 case LOK_CALLBACK_TEXT_SELECTION:
1795 case LOK_CALLBACK_GRAPHIC_SELECTION:
1796 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
1797 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1798 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
1799 case LOK_CALLBACK_STATE_CHANGED:
1800 case LOK_CALLBACK_MOUSE_POINTER:
1801 case LOK_CALLBACK_CELL_CURSOR:
1802 case LOK_CALLBACK_CELL_VIEW_CURSOR:
1803 case LOK_CALLBACK_CELL_FORMULA:
1804 case LOK_CALLBACK_CELL_ADDRESS:
1805 case LOK_CALLBACK_CELL_SELECTION_AREA:
1806 case LOK_CALLBACK_CURSOR_VISIBLE:
1807 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
1808 case LOK_CALLBACK_SET_PART:
1809 case LOK_CALLBACK_TEXT_VIEW_SELECTION:
1810 case LOK_CALLBACK_INVALIDATE_HEADER:
1811 case LOK_CALLBACK_WINDOW:
1812 case LOK_CALLBACK_CALC_FUNCTION_LIST:
1813 case LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY:
1814 case LOK_CALLBACK_REFERENCE_MARKS:
1815 case LOK_CALLBACK_CELL_AUTO_FILL_AREA:
1816 case LOK_CALLBACK_A11Y_FOCUS_CHANGED:
1817 case LOK_CALLBACK_A11Y_CARET_CHANGED:
1818 case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED:
1819 {
1820 const auto& pos = std::find(m_queue1.rbegin(), m_queue1.rend(), type);
1821 auto pos2 = toQueue2(pos);
1822 if (pos != m_queue1.rend() && pos2->getPayload() == aCallbackData.getPayload())
1823 {
1824 SAL_INFO("lok", "Skipping queue duplicate [" << type << + "]: [" << aCallbackData.getPayload() << "].");
1825 return;
1826 }
1827 }
1828 break;
1829 }
1830
1831 if (type == LOK_CALLBACK_TEXT_SELECTION && aCallbackData.isEmpty())
1832 {
1833 const auto& posStart = std::find(m_queue1.rbegin(), m_queue1.rend(), LOK_CALLBACK_TEXT_SELECTION_START);
1834 auto posStart2 = toQueue2(posStart);
1835 if (posStart != m_queue1.rend())
1836 posStart2->clear();
1837
1838 const auto& posEnd = std::find(m_queue1.rbegin(), m_queue1.rend(), LOK_CALLBACK_TEXT_SELECTION_END);
1839 auto posEnd2 = toQueue2(posEnd);
1840 if (posEnd != m_queue1.rend())
1841 posEnd2->clear();
1842 }
1843
1844 // When payload is empty discards any previous state.
1845 if (aCallbackData.isEmpty())
1846 {
1847 switch (type)
1848 {
1849 case LOK_CALLBACK_TEXT_SELECTION_START:
1850 case LOK_CALLBACK_TEXT_SELECTION_END:
1851 case LOK_CALLBACK_TEXT_SELECTION:
1852 case LOK_CALLBACK_GRAPHIC_SELECTION:
1853 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1854 case LOK_CALLBACK_INVALIDATE_TILES:
1855 if (removeAll(type))
1856 SAL_INFO("lok", "Removed dups of [" << type << "]: [" << aCallbackData.getPayload() << "].");
1857 break;
1858 }
1859 }
1860 else
1861 {
1862 switch (type)
1863 {
1864 // These are safe to use the latest state and ignore previous
1865 // ones (if any) since the last overrides previous ones.
1866 case LOK_CALLBACK_TEXT_SELECTION_START:
1867 case LOK_CALLBACK_TEXT_SELECTION_END:
1868 case LOK_CALLBACK_TEXT_SELECTION:
1869 case LOK_CALLBACK_MOUSE_POINTER:
1870 case LOK_CALLBACK_CELL_CURSOR:
1871 case LOK_CALLBACK_CELL_FORMULA:
1872 case LOK_CALLBACK_CELL_ADDRESS:
1873 case LOK_CALLBACK_CURSOR_VISIBLE:
1874 case LOK_CALLBACK_SET_PART:
1875 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
1876 case LOK_CALLBACK_RULER_UPDATE:
1877 case LOK_CALLBACK_A11Y_FOCUS_CHANGED:
1878 case LOK_CALLBACK_A11Y_CARET_CHANGED:
1879 case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED:
1880 {
1881 if (removeAll(type))
1882 SAL_INFO("lok", "Removed dups of [" << type << "]: [" << aCallbackData.getPayload() << "].");
1883 }
1884 break;
1885
1886 // These are safe to use the latest state and ignore previous
1887 // ones (if any) since the last overrides previous ones,
1888 // but only if the view is the same.
1889 case LOK_CALLBACK_CELL_VIEW_CURSOR:
1890 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
1891 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
1892 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1893 case LOK_CALLBACK_TEXT_VIEW_SELECTION:
1894 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
1895 case LOK_CALLBACK_CALC_FUNCTION_LIST:
1896 case LOK_CALLBACK_FORM_FIELD_BUTTON:
1897 {
1898 // deleting the duplicate of visible cursor message can cause hyperlink popup not to show up on second/or more click on the same place.
1899 // If the hyperlink is not empty we can bypass that to show the popup
1900 const bool hyperLinkException = type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR &&
1901 aCallbackData.getPayload().indexOf("\"hyperlink\":\"\"") == -1 &&
1902 aCallbackData.getPayload().indexOf("\"hyperlink\": {}") == -1;
1903 if(!hyperLinkException)
1904 {
1905 const int nViewId = aCallbackData.getViewId();
1906 removeAll(type, [nViewId] (const CallbackData& elemData) {
1907 return (nViewId == elemData.getViewId());
1908 }
1909 );
1910 }
1911 }
1912 break;
1913
1914 case LOK_CALLBACK_INVALIDATE_TILES:
1915 if (processInvalidateTilesEvent(type, aCallbackData))
1916 return;
1917 break;
1918
1919 // State changes with same name override previous ones with a different value.
1920 // Ex. ".uno:PageStatus=Slide 20 of 83" overwrites any previous PageStatus.
1921 case LOK_CALLBACK_STATE_CHANGED:
1922 {
1923 // Compare the state name=value and overwrite earlier entries with same name.
1924 const auto pos = aCallbackData.getPayload().indexOf('=');
1925 if (pos != -1)
1926 {
1927 const std::string_view name = aCallbackData.getPayload().subView(0, pos + 1);
1928 // This is needed because otherwise it creates some problems when
1929 // a save occurs while a cell is still edited in Calc.
1930 if (name != ".uno:ModifiedStatus=")
1931 {
1932 removeAll(type, [&name] (const CallbackData& elemData) {
1933 return elemData.getPayload().startsWith(name);
1934 }
1935 );
1936 }
1937 }
1938 }
1939 break;
1940
1941 case LOK_CALLBACK_WINDOW:
1942 if (processWindowEvent(type, aCallbackData))
1943 return;
1944 break;
1945
1946 case LOK_CALLBACK_GRAPHIC_SELECTION:
1947 {
1948 // remove only selection ranges and 'EMPTY' messages
1949 // always send 'INPLACE' and 'INPLACE EXIT' messages
1950 removeAll(type, [] (const CallbackData& elemData)
1951 { return (elemData.getPayload().indexOf("INPLACE") == -1); });
1952 }
1953 break;
1954 }
1955 }
1956
1957 // Validate that the cached data and the payload string are identical.
1958 assert(aCallbackData.validate() && "Cached callback payload object and string mismatch!");
1959 m_queue1.emplace_back(type);
1960 m_queue2.emplace_back(aCallbackData);
1961 SAL_INFO("lok", "Queued #" << (m_queue1.size() - 1) <<
1962 " [" << type << "]: [" << aCallbackData.getPayload() << "] to have " << m_queue1.size() << " entries.");
1963
1964#ifdef DBG_UTIL
1965 {
1966 // Dump the queue state and validate cached data.
1967 int i = 1;
1968 std::ostringstream oss;
1969 if (m_queue1.empty())
1970 oss << "Empty";
1971 else
1972 oss << m_queue1.size() << " items\n";
1973 auto it1 = m_queue1.begin();
1974 auto it2 = m_queue2.begin();
1975 for (; it1 != m_queue1.end(); ++it1, ++it2)
1976 oss << i++ << ": [" << *it1 << "] [" << it2->getPayload() << "].\n";
1977 SAL_INFO("lok", "Current Queue: " << oss.str());
1978 assert(
1979 std::all_of(
1980 m_queue2.begin(), m_queue2.end(),
1981 [](const CallbackData& c) { return c.validate(); }));
1982 }
1983#endif
1984
1985 lock.unlock();
1986 startTimer();
1987}
1988
1990{
1991 RectangleAndPart rcNew = aCallbackData.getRectangleAndPart();
1992 if (rcNew.isEmpty())
1993 {
1994 SAL_INFO("lok", "Skipping invalid event [" << type << "]: [" << aCallbackData.getPayload() << "].");
1995 return true;
1996 }
1997
1998 // If we have to invalidate all tiles, we can skip any new tile invalidation.
1999 // Find the last INVALIDATE_TILES entry, if any to see if it's invalidate-all.
2000 const auto& pos
2001 = std::find(m_queue1.rbegin(), m_queue1.rend(), LOK_CALLBACK_INVALIDATE_TILES);
2002 if (pos != m_queue1.rend())
2003 {
2004 auto pos2 = toQueue2(pos);
2005 const RectangleAndPart& rcOld = pos2->getRectangleAndPart();
2006 if (rcOld.isInfinite() && (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart) &&
2007 (rcOld.m_nMode == rcNew.m_nMode))
2008 {
2009 SAL_INFO("lok", "Skipping queue [" << type << "]: [" << aCallbackData.getPayload()
2010 << "] since all tiles need to be invalidated.");
2011 return true;
2012 }
2013
2014 if ((rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart) && (rcOld.m_nMode == rcNew.m_nMode))
2015 {
2016 // If fully overlapping.
2017 if (rcOld.m_aRectangle.Contains(rcNew.m_aRectangle))
2018 {
2019 SAL_INFO("lok", "Skipping queue [" << type << "]: [" << aCallbackData.getPayload()
2020 << "] since overlaps existing all-parts.");
2021 return true;
2022 }
2023 }
2024 }
2025
2026 if (rcNew.isInfinite())
2027 {
2028 SAL_INFO("lok", "Have Empty [" << type << "]: [" << aCallbackData.getPayload()
2029 << "] so removing all with part " << rcNew.m_nPart << ".");
2030 removeAll(LOK_CALLBACK_INVALIDATE_TILES, [&rcNew](const CallbackData& elemData) {
2031 // Remove exiting if new is all-encompassing, or if of the same part.
2032 return ((rcNew.m_nPart == -1 || rcNew.m_nPart == elemData.getRectangleAndPart().m_nPart)
2033 && (rcNew.m_nMode == elemData.getRectangleAndPart().m_nMode));
2034 });
2035 }
2036 else
2037 {
2038 const auto rcOrig = rcNew;
2039
2040 SAL_INFO("lok", "Have [" << type << "]: [" << aCallbackData.getPayload() << "] so merging overlapping.");
2041 removeAll(LOK_CALLBACK_INVALIDATE_TILES,[&rcNew](const CallbackData& elemData) {
2042 const RectangleAndPart& rcOld = elemData.getRectangleAndPart();
2043 if (rcNew.m_nPart != -1 && rcOld.m_nPart != -1 &&
2044 (rcOld.m_nPart != rcNew.m_nPart || rcOld.m_nMode != rcNew.m_nMode))
2045 {
2046 SAL_INFO("lok", "Nothing to merge between new: "
2047 << rcNew.toString() << ", and old: " << rcOld.toString());
2048 return false;
2049 }
2050
2051 if (rcNew.m_nPart == -1)
2052 {
2053 // Don't merge unless fully overlapped.
2054 SAL_INFO("lok", "New " << rcNew.toString() << " has " << rcOld.toString()
2055 << "?");
2056 if (rcNew.m_aRectangle.Contains(rcOld.m_aRectangle) && rcOld.m_nMode == rcNew.m_nMode)
2057 {
2058 SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
2059 << rcOld.toString() << ".");
2060 return true;
2061 }
2062 }
2063 else if (rcOld.m_nPart == -1)
2064 {
2065 // Don't merge unless fully overlapped.
2066 SAL_INFO("lok", "Old " << rcOld.toString() << " has " << rcNew.toString()
2067 << "?");
2068 if (rcOld.m_aRectangle.Contains(rcNew.m_aRectangle) && rcOld.m_nMode == rcNew.m_nMode)
2069 {
2070 SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
2071 << rcOld.toString() << ".");
2072 return true;
2073 }
2074 }
2075 else
2076 {
2077 const tools::Rectangle rcOverlap
2078 = rcNew.m_aRectangle.GetIntersection(rcOld.m_aRectangle);
2079 const bool bOverlap = !rcOverlap.IsEmpty() && rcOld.m_nMode == rcNew.m_nMode;
2080 SAL_INFO("lok", "Merging " << rcNew.toString() << " & " << rcOld.toString()
2081 << " => " << rcOverlap.toString()
2082 << " Overlap: " << bOverlap);
2083 if (bOverlap)
2084 {
2085 rcNew.m_aRectangle.Union(rcOld.m_aRectangle);
2086 SAL_INFO("lok", "Merged: " << rcNew.toString());
2087 return true;
2088 }
2089 }
2090
2091 // Keep others.
2092 return false;
2093 });
2094
2095 if (rcNew.m_aRectangle != rcOrig.m_aRectangle)
2096 {
2097 SAL_INFO("lok", "Replacing: " << rcOrig.toString() << " by " << rcNew.toString());
2098 if (rcNew.m_aRectangle.GetWidth() < rcOrig.m_aRectangle.GetWidth()
2099 || rcNew.m_aRectangle.GetHeight() < rcOrig.m_aRectangle.GetHeight())
2100 {
2101 SAL_WARN("lok", "Error: merged rect smaller.");
2102 }
2103 }
2104 }
2105
2106 aCallbackData.updateRectangleAndPart(rcNew);
2107 // Queue this one.
2108 return false;
2109}
2110
2112{
2113 const OString& payload = aCallbackData.getPayload();
2114
2115 boost::property_tree::ptree& aTree = aCallbackData.setJson(std::string(payload));
2116 const unsigned nLOKWindowId = aTree.get<unsigned>("id", 0);
2117 const std::string aAction = aTree.get<std::string>("action", "");
2118 if (aAction == "invalidate")
2119 {
2120 std::string aRectStr = aTree.get<std::string>("rectangle", "");
2121 // no 'rectangle' field => invalidate all of the window =>
2122 // remove all previous window part invalidations
2123 if (aRectStr.empty())
2124 {
2125 removeAll(LOK_CALLBACK_WINDOW,[&nLOKWindowId](const CallbackData& elemData) {
2126 const boost::property_tree::ptree& aOldTree = elemData.getJson();
2127 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0)
2128 && aOldTree.get<std::string>("action", "") == "invalidate")
2129 {
2130 return true;
2131 }
2132 return false;
2133 });
2134 }
2135 else
2136 {
2137 // if we have to invalidate all of the window, ignore
2138 // any part invalidation message
2139 bool invAllExist = false;
2140 auto it1 = m_queue1.rbegin();
2141 auto it2 = m_queue2.rbegin();
2142 for (;it1 != m_queue1.rend(); ++it1, ++it2)
2143 {
2144 if (*it1 != LOK_CALLBACK_WINDOW)
2145 continue;
2146 const boost::property_tree::ptree& aOldTree = it2->getJson();
2147 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0)
2148 && aOldTree.get<std::string>("action", "") == "invalidate"
2149 && aOldTree.get<std::string>("rectangle", "").empty())
2150 {
2151 invAllExist = true;
2152 break;
2153 }
2154 }
2155
2156 // we found a invalidate-all window callback
2157 if (invAllExist)
2158 {
2159 SAL_INFO("lok.dialog", "Skipping queue ["
2160 << type << "]: [" << payload
2161 << "] since whole window needs to be invalidated.");
2162 return true;
2163 }
2164
2165 std::istringstream aRectStream(aRectStr);
2166 tools::Long nLeft, nTop, nWidth, nHeight;
2167 char nComma;
2168 aRectStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight;
2169 tools::Rectangle aNewRect(nLeft, nTop, nLeft + nWidth, nTop + nHeight);
2170 bool currentIsRedundant = false;
2171 removeAll(LOK_CALLBACK_WINDOW, [&aNewRect, &nLOKWindowId,
2172 &currentIsRedundant](const CallbackData& elemData) {
2173 const boost::property_tree::ptree& aOldTree = elemData.getJson();
2174 if (aOldTree.get<std::string>("action", "") == "invalidate")
2175 {
2176 // Not possible that we encounter an empty rectangle here; we already handled this case above.
2177 std::istringstream aOldRectStream(aOldTree.get<std::string>("rectangle", ""));
2178 tools::Long nOldLeft, nOldTop, nOldWidth, nOldHeight;
2179 char nOldComma;
2180 aOldRectStream >> nOldLeft >> nOldComma >> nOldTop >> nOldComma >> nOldWidth
2181 >> nOldComma >> nOldHeight;
2182 const tools::Rectangle aOldRect = tools::Rectangle(
2183 nOldLeft, nOldTop, nOldLeft + nOldWidth, nOldTop + nOldHeight);
2184
2185 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
2186 {
2187 if (aNewRect == aOldRect)
2188 {
2189 SAL_INFO("lok.dialog", "Duplicate rect [" << aNewRect.toString()
2190 << "]. Skipping new.");
2191 // We have a rectangle in the queue already that makes the current Callback useless.
2192 currentIsRedundant = true;
2193 return false;
2194 }
2195 // new one engulfs the old one?
2196 else if (aNewRect.Contains(aOldRect))
2197 {
2198 SAL_INFO("lok.dialog",
2199 "New rect [" << aNewRect.toString() << "] engulfs old ["
2200 << aOldRect.toString() << "]. Replacing old.");
2201 return true;
2202 }
2203 // old one engulfs the new one?
2204 else if (aOldRect.Contains(aNewRect))
2205 {
2206 SAL_INFO("lok.dialog",
2207 "Old rect [" << aOldRect.toString() << "] engulfs new ["
2208 << aNewRect.toString() << "]. Skipping new.");
2209 // We have a rectangle in the queue already that makes the current Callback useless.
2210 currentIsRedundant = true;
2211 return false;
2212 }
2213 else
2214 {
2215 // Overlapping rects.
2216 const tools::Rectangle aPreMergeRect = aNewRect;
2217 aNewRect.Union(aOldRect);
2218 SAL_INFO("lok.dialog", "Merging rects ["
2219 << aPreMergeRect.toString() << "] & ["
2220 << aOldRect.toString() << "] = ["
2221 << aNewRect.toString()
2222 << "]. Replacing old.");
2223 return true;
2224 }
2225 }
2226 }
2227
2228 // keep rest
2229 return false;
2230 });
2231
2232 // Do not enqueue if redundant.
2233 if (currentIsRedundant)
2234 return true;
2235
2236 aTree.put("rectangle", aNewRect.toString().getStr());
2237 aCallbackData.setJson(aTree);
2238 assert(aCallbackData.validate() && "Validation after setJson failed!");
2239 }
2240 }
2241 else if (aAction == "created")
2242 {
2243 // Remove all previous actions on same dialog, if we are creating it anew.
2244 removeAll(LOK_CALLBACK_WINDOW,[&nLOKWindowId](const CallbackData& elemData) {
2245 const boost::property_tree::ptree& aOldTree = elemData.getJson();
2246 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
2247 return true;
2248 return false;
2249 });
2250
2251 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
2252 if (!pWindow)
2253 {
2254 gImpl->maLastExceptionMsg = "Document doesn't support dialog rendering, or window not found.";
2255 return false;
2256 }
2257
2258#ifndef IOS
2259 auto xClip = forceSetClipboardForCurrentView(m_pDocument);
2260
2262 pWindow->SetClipboard(xClipboard);
2263#endif
2264 }
2265 else if (aAction == "size_changed")
2266 {
2267 // A size change is practically re-creation of the window.
2268 // But at a minimum it's a full invalidation.
2269 removeAll(LOK_CALLBACK_WINDOW, [&nLOKWindowId](const CallbackData& elemData) {
2270 const boost::property_tree::ptree& aOldTree = elemData.getJson();
2271 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
2272 {
2273 const std::string aOldAction = aOldTree.get<std::string>("action", "");
2274 if (aOldAction == "invalidate")
2275 return true;
2276 }
2277 return false;
2278 });
2279 }
2280
2281 // Queue this one.
2282 return false;
2283}
2284
2286{
2287 if( m_updatedTypes.empty() && m_updatedTypesPerViewId.empty())
2288 return;
2289 assert(m_viewId >= 0);
2290 SfxViewShell* viewShell = SfxViewShell::GetFirst( false,
2291 [this](const SfxViewShell* shell) { return shell->GetViewShellId().get() == m_viewId; } );
2292 assert(viewShell != nullptr);
2293
2294 // First move data to local structures, so that callbacks don't possibly modify it.
2295 std::vector<bool> updatedTypes;
2296 std::swap(updatedTypes, m_updatedTypes);
2297 boost::container::flat_map<int, std::vector<PerViewIdData>> updatedTypesPerViewId;
2298 std::swap(updatedTypesPerViewId, m_updatedTypesPerViewId);
2299
2300 // Some types must always precede other types, for example
2301 // LOK_CALLBACK_TEXT_SELECTION_START and LOK_CALLBACK_TEXT_SELECTION_END
2302 // must always precede LOK_CALLBACK_TEXT_SELECTION if present.
2303 // Only these types should be present (see isUpdatedType()) and should be processed in this order.
2304 static const int orderedUpdatedTypes[] = {
2305 LOK_CALLBACK_TEXT_SELECTION_START, LOK_CALLBACK_TEXT_SELECTION_END, LOK_CALLBACK_TEXT_SELECTION };
2306 // Only these types should be present (see isUpdatedTypePerViewId()) and (as of now)
2307 // the order doesn't matter.
2308 static const int orderedUpdatedTypesPerViewId[] = {
2309 LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR,
2310 LOK_CALLBACK_INVALIDATE_VIEW_CURSOR,
2311 LOK_CALLBACK_TEXT_VIEW_SELECTION };
2312
2313 for( int type : orderedUpdatedTypes )
2314 {
2315 if(o3tl::make_unsigned( type ) < updatedTypes.size() && updatedTypes[ type ])
2316 {
2317 enqueueUpdatedType( type, viewShell, m_viewId );
2318 }
2319 }
2320 for( const auto& it : updatedTypesPerViewId )
2321 {
2322 int viewId = it.first;
2323 const std::vector<PerViewIdData>& types = it.second;
2324 for( int type : orderedUpdatedTypesPerViewId )
2325 {
2326 if(o3tl::make_unsigned( type ) < types.size() && types[ type ].set)
2327 {
2328 SfxViewShell* sourceViewShell = viewShell;
2329 const int sourceViewId = types[ type ].sourceViewId;
2330 if( sourceViewId != m_viewId )
2331 {
2332 assert(sourceViewId >= 0);
2333 sourceViewShell = SfxViewShell::GetFirst( false,
2334 [sourceViewId](const SfxViewShell* shell) { return shell->GetViewShellId().get() == sourceViewId; } );
2335 }
2336 if(sourceViewShell == nullptr)
2337 {
2338 SAL_INFO("lok", "View #" << sourceViewId << " no longer found for updated event [" << type << "]");
2339 continue; // View removed, probably cleaning up.
2340 }
2341 enqueueUpdatedType( type, sourceViewShell, viewId );
2342 }
2343 }
2344 }
2345}
2346
2347void CallbackFlushHandler::enqueueUpdatedType( int type, const SfxViewShell* viewShell, int viewId )
2348{
2349 if (type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR)
2350 {
2351 if (const SfxViewShell* viewShell2 = LokStarMathHelper(viewShell).GetSmViewShell())
2352 viewShell = viewShell2;
2353 }
2354 std::optional<OString> payload = viewShell->getLOKPayload( type, viewId );
2355 if(!payload)
2356 return; // No actual payload to send.
2357 CallbackData callbackData(*payload, viewId);
2358 m_queue1.emplace_back(type);
2359 m_queue2.emplace_back(callbackData);
2360 SAL_INFO("lok", "Queued updated [" << type << "]: [" << callbackData.getPayload()
2361 << "] to have " << m_queue1.size() << " entries.");
2362}
2363
2365{
2366 comphelper::ProfileZone aZone("CallbackFlushHandler::Invoke");
2367
2368 if (!m_pCallback)
2369 return;
2370
2371 // Get any pending invalidate tile events. This will call our callbacks,
2372 // so it must be done before taking the mutex.
2373 assert(m_viewId >= 0);
2374 if(SfxViewShell* viewShell = SfxViewShell::GetFirst( false,
2375 [this](const SfxViewShell* shell) { return shell->GetViewShellId().get() == m_viewId; } ))
2376 {
2377 viewShell->flushPendingLOKInvalidateTiles();
2378 }
2379
2380 std::unique_lock<std::recursive_mutex> lock(m_mutex);
2381
2382 // Append messages for updated types, fetch them only now.
2384
2385 SAL_INFO("lok", "Flushing " << m_queue1.size() << " elements.");
2386 auto it1 = m_queue1.begin();
2387 auto it2 = m_queue2.begin();
2388 for (; it1 != m_queue1.end(); ++it1, ++it2)
2389 {
2390 const int type = *it1;
2391 const auto& payload = it2->getPayload();
2392 const int viewId = lcl_isViewCallbackType(type) ? it2->getViewId() : -1;
2393
2394 SAL_INFO("lok", "processing event: [" << type << ',' << viewId << "]: [" << payload << "].");
2395
2396 // common code-path for events on this view:
2397 if (viewId == -1)
2398 {
2399 sal_Int32 idx;
2400 // key-value pairs
2401 if (type == LOK_CALLBACK_STATE_CHANGED &&
2402 (idx = payload.indexOf('=')) != -1)
2403 {
2404 OString key = payload.copy(0, idx);
2405 OString value = payload.copy(idx+1);
2406 const auto stateIt = m_lastStateChange.find(key);
2407 if (stateIt != m_lastStateChange.end())
2408 {
2409 // If the value didn't change, it's safe to ignore.
2410 if (stateIt->second == value)
2411 {
2412 SAL_INFO("lok", "Skipping new state duplicate: [" << type << "]: [" << payload << "].");
2413 continue;
2414 }
2415 SAL_INFO("lok", "Replacing a state element [" << type << "]: [" << payload << "].");
2416 stateIt->second = value;
2417 }
2418 else
2419 {
2420 SAL_INFO("lok", "Inserted a new state element: [" << type << "]: [" << payload << "]");
2421 m_lastStateChange.emplace(key, value);
2422 }
2423 }
2424 else
2425 {
2426 const auto stateIt = m_states.find(type);
2427 if (stateIt != m_states.end())
2428 {
2429 // If the state didn't change, it's safe to ignore.
2430 if (stateIt->second == payload)
2431 {
2432 SAL_INFO("lok", "Skipping duplicate [" << type << "]: [" << payload << "].");
2433 continue;
2434 }
2435 stateIt->second = payload;
2436 }
2437 }
2438 }
2439 else // less common path for events relating to other views
2440 {
2441 const auto statesIt = m_viewStates.find(viewId);
2442 if (statesIt != m_viewStates.end())
2443 {
2444 auto& states = statesIt->second;
2445 const auto stateIt = states.find(type);
2446 if (stateIt != states.end())
2447 {
2448 // If the state didn't change, it's safe to ignore.
2449 if (stateIt->second == payload)
2450 {
2451 SAL_INFO("lok", "Skipping view duplicate [" << type << ',' << viewId << "]: [" << payload << "].");
2452 continue;
2453 }
2454
2455 SAL_INFO("lok", "Replacing an element in view states [" << type << ',' << viewId << "]: [" << payload << "].");
2456 stateIt->second = payload;
2457 }
2458 else
2459 {
2460 SAL_INFO("lok", "Inserted a new element in view states: [" << type << ',' << viewId << "]: [" << payload << "]");
2461 states.emplace(type, payload);
2462
2463 }
2464 }
2465 }
2466
2467 m_pCallback(type, payload.getStr(), m_pData);
2468 }
2469
2470 m_queue1.clear();
2471 m_queue2.clear();
2472 Stop();
2474}
2475
2477{
2478 if (!IsActive())
2479 Start();
2480 if (!m_TimeoutIdle.IsActive())
2482}
2483
2485{
2486 bool bErased = false;
2487 auto it1 = m_queue1.begin();
2488 for(;;)
2489 {
2490 it1 = std::find(it1, m_queue1.end(), type);
2491 if(it1 == m_queue1.end())
2492 break;
2493 m_queue2.erase(toQueue2(it1));
2494 it1 = m_queue1.erase(it1);
2495 bErased = true;
2496 }
2497 return bErased;
2498}
2499
2500bool CallbackFlushHandler::removeAll(int type, const std::function<bool (const CallbackData&)>& rTestFunc)
2501{
2502 bool bErased = false;
2503 auto it1 = m_queue1.begin();
2504 for(;;)
2505 {
2506 it1 = std::find(it1, m_queue1.end(), type);
2507 if(it1 == m_queue1.end())
2508 break;
2509 auto it2 = toQueue2(it1);
2510 if (rTestFunc(*it2))
2511 {
2512 m_queue2.erase(it2);
2513 it1 = m_queue1.erase(it1);
2514 bErased = true;
2515 }
2516 else
2517 ++it1;
2518 }
2519 return bErased;
2520}
2521
2523{
2524 const auto& result = m_viewStates.emplace(viewId, decltype(m_viewStates)::mapped_type());
2525 if (!result.second && result.first != m_viewStates.end())
2526 {
2527 result.first->second.clear();
2528 }
2529}
2530
2532{
2533 m_viewStates.erase(viewId);
2534}
2535
2536
2537static void doc_destroy(LibreOfficeKitDocument *pThis)
2538{
2539 comphelper::ProfileZone aZone("doc_destroy");
2540
2541 SolarMutexGuard aGuard;
2542
2543#ifndef IOS
2545#endif
2546
2547 LibLODocument_Impl *pDocument = static_cast<LibLODocument_Impl*>(pThis);
2548 delete pDocument;
2549}
2550
2551static void lo_destroy (LibreOfficeKit* pThis);
2552static int lo_initialize (LibreOfficeKit* pThis, const char* pInstallPath, const char* pUserProfilePath);
2553static LibreOfficeKitDocument* lo_documentLoad (LibreOfficeKit* pThis, const char* pURL);
2554static char * lo_getError (LibreOfficeKit* pThis);
2555static void lo_freeError (char* pFree);
2556static LibreOfficeKitDocument* lo_documentLoadWithOptions (LibreOfficeKit* pThis,
2557 const char* pURL,
2558 const char* pOptions);
2559static void lo_registerCallback (LibreOfficeKit* pThis,
2560 LibreOfficeKitCallback pCallback,
2561 void* pData);
2562static char* lo_getFilterTypes(LibreOfficeKit* pThis);
2563static void lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long features);
2564static void lo_setDocumentPassword(LibreOfficeKit* pThis,
2565 const char* pURL,
2566 const char* pPassword);
2567static char* lo_getVersionInfo(LibreOfficeKit* pThis);
2568static int lo_runMacro (LibreOfficeKit* pThis, const char* pURL);
2569
2570static bool lo_signDocument(LibreOfficeKit* pThis,
2571 const char* pUrl,
2572 const unsigned char* pCertificateBinary,
2573 const int nCertificateBinarySize,
2574 const unsigned char* pPrivateKeyBinary,
2575 const int nPrivateKeyBinarySize);
2576
2577static char* lo_extractRequest(LibreOfficeKit* pThis,
2578 const char* pFilePath);
2579
2580static void lo_trimMemory(LibreOfficeKit* pThis, int nTarget);
2581
2582static void lo_runLoop(LibreOfficeKit* pThis,
2583 LibreOfficeKitPollCallback pPollCallback,
2584 LibreOfficeKitWakeCallback pWakeCallback,
2585 void* pData);
2586
2587static void lo_sendDialogEvent(LibreOfficeKit* pThis,
2588 unsigned long long int nLOKWindowId,
2589 const char* pArguments);
2590
2591static void lo_setOption(LibreOfficeKit* pThis, const char* pOption, const char* pValue);
2592
2593static void lo_dumpState(LibreOfficeKit* pThis, const char* pOptions, char** pState);
2594
2596 : m_pOfficeClass( gOfficeClass.lock() )
2597 , maThread(nullptr)
2598 , mpCallback(nullptr)
2599 , mpCallbackData(nullptr)
2600 , mOptionalFeatures(0)
2601{
2602 if(!m_pOfficeClass) {
2603 m_pOfficeClass = std::make_shared<LibreOfficeKitClass>();
2604 m_pOfficeClass->nSize = sizeof(LibreOfficeKitClass);
2605
2606 m_pOfficeClass->destroy = lo_destroy;
2607 m_pOfficeClass->documentLoad = lo_documentLoad;
2608 m_pOfficeClass->getError = lo_getError;
2609 m_pOfficeClass->freeError = lo_freeError;
2610 m_pOfficeClass->documentLoadWithOptions = lo_documentLoadWithOptions;
2611 m_pOfficeClass->registerCallback = lo_registerCallback;
2612 m_pOfficeClass->getFilterTypes = lo_getFilterTypes;
2613 m_pOfficeClass->setOptionalFeatures = lo_setOptionalFeatures;
2614 m_pOfficeClass->setDocumentPassword = lo_setDocumentPassword;
2615 m_pOfficeClass->getVersionInfo = lo_getVersionInfo;
2616 m_pOfficeClass->runMacro = lo_runMacro;
2617 m_pOfficeClass->signDocument = lo_signDocument;
2618 m_pOfficeClass->runLoop = lo_runLoop;
2619 m_pOfficeClass->sendDialogEvent = lo_sendDialogEvent;
2620 m_pOfficeClass->setOption = lo_setOption;
2621 m_pOfficeClass->dumpState = lo_dumpState;
2622 m_pOfficeClass->extractRequest = lo_extractRequest;
2623 m_pOfficeClass->trimMemory = lo_trimMemory;
2624
2626 }
2627
2628 pClass = m_pOfficeClass.get();
2629}
2630
2632{
2633}
2634
2635namespace
2636{
2637
2638void setLanguageAndLocale(OUString const & aLangISO)
2639{
2640 SvtSysLocaleOptions aLocalOptions;
2641 aLocalOptions.SetLocaleConfigString(aLangISO);
2642 aLocalOptions.SetUILocaleConfigString(aLangISO);
2643 aLocalOptions.Commit();
2644}
2645
2646void setFormatSpecificFilterData(std::u16string_view sFormat, comphelper::SequenceAsHashMap & rFilterDataMap)
2647{
2648 if (sFormat == u"pdf")
2649 {
2650 // always export bookmarks, which is needed for annotations
2651 rFilterDataMap["ExportBookmarks"] <<= true;
2652 }
2653}
2654
2655} // anonymous namespace
2656
2657// Wonder global state ...
2661
2662static LibreOfficeKitDocument* lo_documentLoad(LibreOfficeKit* pThis, const char* pURL)
2663{
2664 return lo_documentLoadWithOptions(pThis, pURL, nullptr);
2665}
2666
2667static LibreOfficeKitDocument* lo_documentLoadWithOptions(LibreOfficeKit* pThis, const char* pURL, const char* pOptions)
2668{
2669 comphelper::ProfileZone aZone("lo_documentLoadWithOptions");
2670
2671 SolarMutexGuard aGuard;
2672
2673 static int nDocumentIdCounter = 0;
2674
2675 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
2676 pLib->maLastExceptionMsg.clear();
2677
2678 const OUString aURL(getAbsoluteURL(pURL));
2679 if (aURL.isEmpty())
2680 {
2681 pLib->maLastExceptionMsg = "Filename to load was not provided.";
2682 SAL_INFO("lok", "URL for load is empty");
2683 return nullptr;
2684 }
2685
2686 pLib->maLastExceptionMsg.clear();
2687
2688 if (!xContext.is())
2689 {
2690 pLib->maLastExceptionMsg = "ComponentContext is not available";
2691 SAL_INFO("lok", "ComponentContext is not available");
2692 return nullptr;
2693 }
2694
2696
2697 if (!xComponentLoader.is())
2698 {
2699 pLib->maLastExceptionMsg = "ComponentLoader is not available";
2700 SAL_INFO("lok", "ComponentLoader is not available");
2701 return nullptr;
2702 }
2703
2704 try
2705 {
2706 // 'Language=...' is an option that LOK consumes by itself, and does
2707 // not pass it as a parameter to the filter
2708 OUString aOptions = getUString(pOptions);
2709 const OUString aLanguage = extractParameter(aOptions, u"Language");
2710 bool isValidLangTag = LanguageTag::isValidBcp47(aLanguage, nullptr);
2711
2712 if (!aLanguage.isEmpty() && isValidLangTag)
2713 {
2714 static bool isLoading = true;
2715 if (isLoading)
2716 {
2717 // Capture the language used to load the document.
2719 isLoading = false;
2720 }
2721
2723 // Set the LOK language tag, used for dialog tunneling.
2726
2727 SAL_INFO("lok", "Set document language to " << aLanguage);
2728 // use with care - it sets it for the entire core, not just the
2729 // document
2730 setLanguageAndLocale(aLanguage);
2731 // Need to reset the static initialized values
2733 }
2734
2735 // Set the timezone, if not empty.
2736 const OUString aTimezone = extractParameter(aOptions, u"Timezone");
2737 if (!aTimezone.isEmpty())
2738 {
2739 SfxLokHelper::setDefaultTimezone(true, aTimezone);
2740 }
2741 else
2742 {
2743 // Default to the TZ envar, if set.
2744 const char* tz = ::getenv("TZ");
2745 if (tz)
2746 {
2748 OStringToOUString(tz, RTL_TEXTENCODING_UTF8));
2749 }
2750 else
2751 {
2752 SfxLokHelper::setDefaultTimezone(false, OUString());
2753 }
2754 }
2755
2756 const OUString aDeviceFormFactor = extractParameter(aOptions, u"DeviceFormFactor");
2757 SfxLokHelper::setDeviceFormFactor(aDeviceFormFactor);
2758
2759 const OUString aBatch = extractParameter(aOptions, u"Batch");
2760 if (!aBatch.isEmpty())
2761 {
2762 Application::SetDialogCancelMode(DialogCancelMode::LOKSilent);
2763 }
2764
2765 const OUString sFilterOptions = aOptions;
2766
2767 rtl::Reference<LOKInteractionHandler> const pInteraction(
2768 new LOKInteractionHandler("load", pLib));
2769 auto const pair(pLib->mInteractionMap.insert(std::make_pair(aURL.toUtf8(), pInteraction)));
2770 comphelper::ScopeGuard const g([&] () {
2771 if (pair.second)
2772 {
2773 pLib->mInteractionMap.erase(aURL.toUtf8());
2774 }
2775 });
2776 uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction);
2777
2778 int nMacroSecurityLevel = 1;
2779 const OUString aMacroSecurityLevel = extractParameter(aOptions, u"MacroSecurityLevel");
2780 if (!aMacroSecurityLevel.isEmpty())
2781 {
2782 double nNumber;
2783 sal_uInt32 nFormat = 1;
2784 SvNumberFormatter aFormatter(::comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US);
2785 if (aFormatter.IsNumberFormat(aMacroSecurityLevel, nFormat, nNumber))
2786 nMacroSecurityLevel = static_cast<int>(nNumber);
2787 }
2788 SvtSecurityOptions::SetMacroSecurityLevel(nMacroSecurityLevel);
2789
2790#if defined(ANDROID) && HAVE_FEATURE_ANDROID_LOK
2791 sal_Int16 nMacroExecMode = document::MacroExecMode::USE_CONFIG;
2792#else
2793 const OUString aEnableMacrosExecution = extractParameter(aOptions, u"EnableMacrosExecution");
2794 sal_Int16 nMacroExecMode = aEnableMacrosExecution == "true" ? document::MacroExecMode::USE_CONFIG :
2795 document::MacroExecMode::NEVER_EXECUTE;
2796#endif
2797
2798 // set AsTemplate explicitly false to be able to load template files
2799 // as regular files, otherwise we cannot save them; it will try
2800 // to bring saveas dialog which cannot work with LOK case
2802 comphelper::makePropertyValue("FilterOptions", sFilterOptions),
2803 comphelper::makePropertyValue("InteractionHandler", xInteraction),
2804 comphelper::makePropertyValue("MacroExecutionMode", nMacroExecMode),
2805 comphelper::makePropertyValue("AsTemplate", false),
2806 comphelper::makePropertyValue("Silent", !aBatch.isEmpty())
2807 };
2808
2809 /* TODO
2810 sal_Int16 nUpdateDoc = document::UpdateDocMode::ACCORDING_TO_CONFIG;
2811 aFilterOptions[3].Name = "UpdateDocMode";
2812 aFilterOptions[3].Value <<= nUpdateDoc;
2813 */
2814
2816
2817 const int nThisDocumentId = nDocumentIdCounter++;
2819 uno::Reference<lang::XComponent> xComponent = xComponentLoader->loadComponentFromURL(
2820 aURL, "_blank", 0,
2821 aFilterOptions);
2822
2823 assert(!xComponent.is() || pair.second); // concurrent loading of same URL ought to fail
2824
2825 if (!xComponent.is())
2826 {
2827 pLib->maLastExceptionMsg = "loadComponentFromURL returned an empty reference";
2828 SAL_INFO("lok", "Document can't be loaded - " << pLib->maLastExceptionMsg);
2829 return nullptr;
2830 }
2831
2832 LibLODocument_Impl* pDocument = new LibLODocument_Impl(xComponent, nThisDocumentId);
2833
2834 // After loading the document, its initial view is the "current" view.
2835 if (pLib->mpCallback)
2836 {
2837 int nState = doc_getSignatureState(pDocument);
2838 pLib->mpCallback(LOK_CALLBACK_SIGNATURE_STATUS, OString::number(nState).getStr(), pLib->mpCallbackData);
2839 }
2840
2841 auto aFontMappingUseData = OutputDevice::FinishTrackingFontMappingUse();
2842
2843 if (aFontMappingUseData.size() > 0)
2844 {
2845 SAL_INFO("lok.fontsubst", "================ Original substitutions:");
2846 for (const auto &i : aFontMappingUseData)
2847 {
2848 SAL_INFO("lok.fontsubst", i.mOriginalFont);
2849 for (const auto &j : i.mUsedFonts)
2850 SAL_INFO("lok.fontsubst", " " << j);
2851 }
2852 }
2853
2854 // Filter out font substitutions that actually aren't any substitutions, like "Liberation
2855 // Serif" -> "Liberation Serif/Regular". If even one of the "substitutions" of a font is to
2856 // the same font, don't count that as a missing font.
2857
2858 aFontMappingUseData.erase
2859 (std::remove_if(aFontMappingUseData.begin(), aFontMappingUseData.end(),
2861 {
2862 // If the original font had an empty style and one of its
2863 // replacement fonts has the same family name, we assume the font is
2864 // present. The root problem here is that the code that collects
2865 // font substitutions tends to get just empty styles for the font
2866 // that is being substituted, as vcl::Font::GetStyleName() tends to
2867 // return an empty string. (Italicness is instead indicated by what
2868 // vcl::Font::GetItalic() returns and boldness by what
2869 // vcl::Font::GetWeight() returns.)
2870
2871 if (x.mOriginalFont.indexOf('/') == -1)
2872 for (const auto &j : x.mUsedFonts)
2873 if (j == x.mOriginalFont ||
2874 j.startsWith(Concat2View(x.mOriginalFont + "/")))
2875 return true;
2876
2877 return false;
2878 }),
2879 aFontMappingUseData.end());
2880
2881 // Filter out substitutions where a proprietary font has been substituted by a
2882 // metric-compatible one. Obviously this is just a heuristic and implemented only for some
2883 // well-known cases.
2884
2885 aFontMappingUseData.erase
2886 (std::remove_if(aFontMappingUseData.begin(), aFontMappingUseData.end(),
2888 {
2889 // Again, handle only cases where the original font does not include
2890 // a style. Unclear whether there ever will be a style part included
2891 // in the mOriginalFont.
2892
2893 if (x.mOriginalFont.indexOf('/') == -1)
2894 for (const auto &j : x.mUsedFonts)
2895 if ((x.mOriginalFont == "Arial" &&
2896 j.startsWith("Liberation Sans/")) ||
2897 (x.mOriginalFont == "Times New Roman" &&
2898 j.startsWith("Liberation Serif/")) ||
2899 (x.mOriginalFont == "Courier New" &&
2900 j.startsWith("Liberation Mono/")) ||
2901 (x.mOriginalFont == "Arial Narrow" &&
2902 j.startsWith("Liberation Sans Narrow/")) ||
2903 (x.mOriginalFont == "Cambria" &&
2904 j.startsWith("Caladea/")) ||
2905 (x.mOriginalFont == "Calibri" &&
2906 j.startsWith("Carlito/")) ||
2907 (x.mOriginalFont == "Palatino Linotype" &&
2908 j.startsWith("P052/")) ||
2909 // Perhaps a risky heuristic? If some glyphs from Symbol
2910 // have been mapped to ones in OpenSymbol, don't warn
2911 // that Symbol is missing.
2912 (x.mOriginalFont == "Symbol" &&
2913 j.startsWith("OpenSymbol/")))
2914 {
2915 return true;
2916 }
2917
2918 return false;
2919 }),
2920 aFontMappingUseData.end());
2921
2922 if (aFontMappingUseData.size() > 0)
2923 {
2924 SAL_INFO("lok.fontsubst", "================ Pruned substitutions:");
2925 for (const auto &i : aFontMappingUseData)
2926 {
2927 SAL_INFO("lok.fontsubst", i.mOriginalFont);
2928 for (const auto &j : i.mUsedFonts)
2929 SAL_INFO("lok.fontsubst", " " << j);
2930 }
2931 }
2932
2933 for (std::size_t i = 0; i < aFontMappingUseData.size(); ++i)
2934 {
2935 pDocument->maFontsMissing.insert(aFontMappingUseData[i].mOriginalFont);
2936 }
2937
2938 return pDocument;
2939 }
2940 catch (const uno::Exception& exception)
2941 {
2942 pLib->maLastExceptionMsg = exception.Message;
2943 TOOLS_INFO_EXCEPTION("lok", "Document can't be loaded");
2944 }
2945
2946 return nullptr;
2947}
2948
2949static int lo_runMacro(LibreOfficeKit* pThis, const char *pURL)
2950{
2951 comphelper::ProfileZone aZone("lo_runMacro");
2952
2953 SolarMutexGuard aGuard;
2954
2955 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
2956 pLib->maLastExceptionMsg.clear();
2957
2958 OUString sURL( pURL, strlen(pURL), RTL_TEXTENCODING_UTF8 );
2959 if (sURL.isEmpty())
2960 {
2961 pLib->maLastExceptionMsg = "Macro to run was not provided.";
2962 SAL_INFO("lok", "Macro URL is empty");
2963 return false;
2964 }
2965
2966 if (!sURL.startsWith("macro://"))
2967 {
2968 pLib->maLastExceptionMsg = "This doesn't look like macro URL";
2969 SAL_INFO("lok", "Macro URL is invalid");
2970 return false;
2971 }
2972
2973 pLib->maLastExceptionMsg.clear();
2974
2975 if (!xContext.is())
2976 {
2977 pLib->maLastExceptionMsg = "ComponentContext is not available";
2978 SAL_INFO("lok", "ComponentContext is not available");
2979 return false;
2980 }
2981
2982 util::URL aURL;
2983 aURL.Complete = sURL;
2984
2986
2987 if( xParser.is() )
2988 xParser->parseStrict( aURL );
2989
2991
2992 if (!xComponentLoader.is())
2993 {
2994 pLib->maLastExceptionMsg = "ComponentLoader is not available";
2995 SAL_INFO("lok", "ComponentLoader is not available");
2996 return false;
2997 }
2998
2999 xFactory = xContext->getServiceManager();
3000
3001 if (!xFactory)
3002 return false;
3003
3005 xSFactory.set(xFactory, uno::UNO_QUERY_THROW);
3006 xDP.set( xSFactory->createInstance("com.sun.star.comp.sfx2.SfxMacroLoader"), uno::UNO_QUERY );
3007 uno::Reference<frame::XDispatch> xD = xDP->queryDispatch( aURL, OUString(), 0);
3008
3009 if (!xD.is())
3010 {
3011 pLib->maLastExceptionMsg = "Macro loader is not available";
3012 SAL_INFO("lok", "Macro loader is not available");
3013 return false;
3014 }
3015
3016 uno::Reference < frame::XSynchronousDispatch > xSyncDisp( xD, uno::UNO_QUERY_THROW );
3018 css::beans::PropertyValue aErr;
3019 uno::Any aRet = xSyncDisp->dispatchWithReturnValue( aURL, aEmpty );
3020 aRet >>= aErr;
3021
3022 if (aErr.Name == "ErrorCode")
3023 {
3024 sal_uInt32 nErrCode = 0; // ERRCODE_NONE
3025 aErr.Value >>= nErrCode;
3026
3027 pLib->maLastExceptionMsg = "An error occurred running macro (error code: " + OUString::number( nErrCode ) + ")";
3028 SAL_INFO("lok", "Macro execution terminated with error code " << nErrCode);
3029
3030 return false;
3031 }
3032
3033 return true;
3034}
3035
3036static bool lo_signDocument(LibreOfficeKit* /*pThis*/,
3037 const char* pURL,
3038 const unsigned char* pCertificateBinary,
3039 const int nCertificateBinarySize,
3040 const unsigned char* pPrivateKeyBinary,
3041 const int nPrivateKeyBinarySize)
3042{
3043 comphelper::ProfileZone aZone("lo_signDocument");
3044
3045 OUString aURL(getAbsoluteURL(pURL));
3046 if (aURL.isEmpty())
3047 return false;
3048
3049 if (!xContext.is())
3050 return false;
3051
3052 uno::Sequence<sal_Int8> aCertificateSequence;
3053
3054 std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
3055 std::string aCertificateBase64String = extractCertificate(aCertificateString);
3056 if (!aCertificateBase64String.empty())
3057 {
3058 OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String);
3059 comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
3060 }
3061 else
3062 {
3063 aCertificateSequence.realloc(nCertificateBinarySize);
3064 std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.getArray());
3065 }
3066
3067 uno::Sequence<sal_Int8> aPrivateKeySequence;
3068 std::string aPrivateKeyString(reinterpret_cast<const char*>(pPrivateKeyBinary), nPrivateKeyBinarySize);
3069 std::string aPrivateKeyBase64String = extractPrivateKey(aPrivateKeyString);
3070 if (!aPrivateKeyBase64String.empty())
3071 {
3072 OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String);
3073 comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString);
3074 }
3075 else
3076 {
3077 aPrivateKeySequence.realloc(nPrivateKeyBinarySize);
3078 std::copy(pPrivateKeyBinary, pPrivateKeyBinary + nPrivateKeyBinarySize, aPrivateKeySequence.getArray());
3079 }
3080
3082 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
3083 if (!xSecurityContext.is())
3084 return false;
3085
3086 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
3087 uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
3088
3089 if (!xCertificateCreator.is())
3090 return false;
3091
3092 uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence);
3093
3094 if (!xCertificate.is())
3095 return false;
3096
3097 sfx2::DocumentSigner aDocumentSigner(aURL);
3098 if (!aDocumentSigner.signDocument(xCertificate))
3099 return false;
3100
3101 return true;
3102}
3103
3104
3105static char* lo_extractRequest(LibreOfficeKit* /*pThis*/, const char* pFilePath)
3106{
3109 OUString aURL(getAbsoluteURL(pFilePath));
3110 OUString result;
3111 if (!aURL.isEmpty())
3112 {
3113 if (xComponentLoader.is())
3114 {
3115 try
3116 {
3118 {
3119 {"Hidden", css::uno::Any(true)},
3120 {"ReadOnly", css::uno::Any(true)}
3121 }));
3122 xComp = xComponentLoader->loadComponentFromURL( aURL, "_blank", 0, aFilterOptions );
3123 }
3124 catch ( const lang::IllegalArgumentException& ex )
3125 {
3126 SAL_WARN("lok", "lo_extractRequest: IllegalArgumentException: " << ex.Message);
3127 result = "{ }";
3128 return convertOUString(result);
3129 }
3130 catch (...)
3131 {
3132 SAL_WARN("lok", "lo_extractRequest: Exception on loadComponentFromURL, url= " << aURL);
3133 result = "{ }";
3134 return convertOUString(result);
3135 }
3136
3137 if (xComp.is())
3138 {
3139 uno::Reference< document::XLinkTargetSupplier > xLTS( xComp, uno::UNO_QUERY );
3140
3141 if( xLTS.is() )
3142 {
3143 OUStringBuffer jsonText("{ \"Targets\": { ");
3144 bool lastParentheses = extractLinks(xLTS->getLinks(), false, jsonText);
3145 jsonText.append("} }");
3146 if (!lastParentheses)
3147 jsonText.append(" }");
3148
3149 OUString res(jsonText.makeStringAndClear());
3150 return convertOUString(res);
3151 }
3152 xComp->dispose();
3153 }
3154 else
3155 {
3156 result = "{ }";
3157 return convertOUString(result);
3158 }
3159
3160 }
3161 }
3162 result = "{ }";
3163 return convertOUString(result);
3164}
3165
3166static void lo_trimMemory(LibreOfficeKit* /* pThis */, int nTarget)
3167{
3168 vcl::lok::trimMemory(nTarget);
3169 if (nTarget > 1000)
3170 {
3171#ifdef HAVE_MALLOC_TRIM
3172 malloc_trim(0);
3173#endif
3174 }
3175}
3176
3177static void lo_registerCallback (LibreOfficeKit* pThis,
3178 LibreOfficeKitCallback pCallback,
3179 void* pData)
3180{
3181 SolarMutexGuard aGuard;
3182
3183 Application* pApp = GetpApp();
3184 assert(pApp);
3185
3186 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
3187 pLib->maLastExceptionMsg.clear();
3188
3189 pApp->m_pCallback = pLib->mpCallback = pCallback;
3190 pApp->m_pCallbackData = pLib->mpCallbackData = pData;
3191}
3192
3193static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* sUrl, const char* pFormat, const char* pFilterOptions)
3194{
3195 comphelper::ProfileZone aZone("doc_saveAs");
3196
3197 SolarMutexGuard aGuard;
3199
3200 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
3201
3202 OUString sFormat = getUString(pFormat);
3203 OUString aURL(getAbsoluteURL(sUrl));
3204
3205 uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
3206
3207 if (aURL.isEmpty())
3208 {
3209 SetLastExceptionMsg("Filename to save to was not provided.");
3210 SAL_INFO("lok", "URL for save is empty");
3211 return false;
3212 }
3213
3214 try
3215 {
3216 const ExtensionMap* pMap;
3217
3218 switch (doc_getDocumentType(pThis))
3219 {
3220 case LOK_DOCTYPE_SPREADSHEET:
3221 pMap = aCalcExtensionMap;
3222 break;
3223 case LOK_DOCTYPE_PRESENTATION:
3224 pMap = aImpressExtensionMap;
3225 break;
3226 case LOK_DOCTYPE_DRAWING:
3227 pMap = aDrawExtensionMap;
3228 break;
3229 case LOK_DOCTYPE_TEXT:
3230 pMap = aWriterExtensionMap;
3231 break;
3232 case LOK_DOCTYPE_OTHER:
3233 default:
3234 SAL_INFO("lok", "Can't save document - unsupported document type.");
3235 return false;
3236 }
3237
3238 if (pFormat == nullptr)
3239 {
3240 // sniff from the extension
3241 sal_Int32 idx = aURL.lastIndexOf(".");
3242 if( idx > 0 )
3243 {
3244 sFormat = aURL.copy( idx + 1 );
3245 }
3246 else
3247 {
3248 SetLastExceptionMsg("input URL '" + aURL + "' lacks a suffix");
3249 return false;
3250 }
3251 }
3252
3253 OUString aFilterName;
3254 for (sal_Int32 i = 0; pMap[i].extn; ++i)
3255 {
3256 if (sFormat.equalsIgnoreAsciiCaseAscii(pMap[i].extn))
3257 {
3258 aFilterName = getUString(pMap[i].filterName);
3259 break;
3260 }
3261 }
3262 if (aFilterName.isEmpty())
3263 {
3264 SetLastExceptionMsg("no output filter found for provided suffix");
3265 return false;
3266 }
3267
3268 OUString aFilterOptions = getUString(pFilterOptions);
3269
3270 // Check if watermark for pdf is passed by filteroptions...
3271 // It is not a real filter option so it must be filtered out.
3272 OUString watermarkText;
3273 std::u16string_view sFullSheetPreview;
3274 int aIndex = -1;
3275 if ((aIndex = aFilterOptions.indexOf(",Watermark=")) >= 0)
3276 {
3277 int bIndex = aFilterOptions.indexOf("WATERMARKEND");
3278 watermarkText = aFilterOptions.subView(aIndex+11, bIndex-(aIndex+11));
3279 aFilterOptions = OUString::Concat(aFilterOptions.subView(0, aIndex)) + aFilterOptions.subView(bIndex+12);
3280 }
3281
3282 if ((aIndex = aFilterOptions.indexOf(",FullSheetPreview=")) >= 0)
3283 {
3284 int bIndex = aFilterOptions.indexOf("FULLSHEETPREVEND");
3285 sFullSheetPreview = aFilterOptions.subView(aIndex+18, bIndex-(aIndex+18));
3286 aFilterOptions = OUString::Concat(aFilterOptions.subView(0, aIndex)) + aFilterOptions.subView(bIndex+16);
3287 }
3288
3289 bool bFullSheetPreview = sFullSheetPreview == u"true";
3290
3291 // Select a pdf version if specified a valid one. If not specified then ignore.
3292 // If invalid then fail.
3293 sal_Int32 pdfVer = 0;
3294 if ((aIndex = aFilterOptions.indexOf(",PDFVer=")) >= 0)
3295 {
3296 int bIndex = aFilterOptions.indexOf("PDFVEREND");
3297 std::u16string_view sPdfVer = aFilterOptions.subView(aIndex+8, bIndex-(aIndex+8));
3298 aFilterOptions = OUString::Concat(aFilterOptions.subView(0, aIndex)) + aFilterOptions.subView(bIndex+9);
3299
3300 if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF/A-1b"))
3301 pdfVer = 1;
3302 else if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF/A-2b"))
3303 pdfVer = 2;
3304 else if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF/A-3b"))
3305 pdfVer = 3;
3306 else if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF-1.5"))
3307 pdfVer = 15;
3308 else if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF-1.6"))
3309 pdfVer = 16;
3310 else
3311 {
3312 SetLastExceptionMsg("wrong PDF version");
3313 return false;
3314 }
3315 }
3316
3317 // 'TakeOwnership' == this is a 'real' SaveAs (that is, the document
3318 // gets a new name). When this is not provided, the meaning of
3319 // saveAs() is more like save-a-copy, which allows saving to any
3320 // random format like PDF or PNG.
3321 // It is not a real filter option, so we have to filter it out.
3322 const uno::Sequence<OUString> aOptionSeq = comphelper::string::convertCommaSeparated(aFilterOptions);
3323 std::vector<OUString> aFilteredOptionVec;
3324 bool bTakeOwnership = false;
3325 MediaDescriptor aSaveMediaDescriptor;
3326 for (const auto& rOption : aOptionSeq)
3327 {
3328 if (rOption == "TakeOwnership")
3329 bTakeOwnership = true;
3330 else if (rOption == "NoFileSync")
3331 aSaveMediaDescriptor["NoFileSync"] <<= true;
3332 else
3333 aFilteredOptionVec.push_back(rOption);
3334 }
3335
3336 aSaveMediaDescriptor["Overwrite"] <<= true;
3337 aSaveMediaDescriptor["FilterName"] <<= aFilterName;
3338
3339 auto aFilteredOptionSeq = comphelper::containerToSequence<OUString>(aFilteredOptionVec);
3340 aFilterOptions = comphelper::string::convertCommaSeparated(aFilteredOptionSeq);
3341 aSaveMediaDescriptor[MediaDescriptor::PROP_FILTEROPTIONS] <<= aFilterOptions;
3342
3343 comphelper::SequenceAsHashMap aFilterDataMap;
3344
3345 // If filter options is JSON string, then make sure aFilterDataMap stays empty, otherwise we
3346 // would ignore the filter options.
3347 if (!aFilterOptions.startsWith("{"))
3348 {
3349 setFormatSpecificFilterData(sFormat, aFilterDataMap);
3350 }
3351
3352 if (!watermarkText.isEmpty())
3353 aFilterDataMap["TiledWatermark"] <<= watermarkText;
3354
3355 if (bFullSheetPreview)
3356 aFilterDataMap["SinglePageSheets"] <<= true;
3357
3358 if (pdfVer)
3359 aFilterDataMap["SelectPdfVersion"] <<= pdfVer;
3360
3361 if (!aFilterDataMap.empty())
3362 {
3363 aSaveMediaDescriptor["FilterData"] <<= aFilterDataMap.getAsConstPropertyValueList();
3364 }
3365
3366 // add interaction handler too
3367 if (gImpl)
3368 {
3369 // gImpl does not have to exist when running from a unit test
3370 rtl::Reference<LOKInteractionHandler> const pInteraction(
3371 new LOKInteractionHandler("saveas", gImpl, pDocument));
3372 uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction);
3373
3374 aSaveMediaDescriptor[MediaDescriptor::PROP_INTERACTIONHANDLER] <<= xInteraction;
3375 }
3376
3377
3378 if (bTakeOwnership)
3379 xStorable->storeAsURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList());
3380 else
3381 xStorable->storeToURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList());
3382
3383 return true;
3384 }
3385 catch (const uno::Exception& exception)
3386 {
3387 SetLastExceptionMsg("exception: " + exception.Message);
3388 }
3389 return false;
3390}
3391
3399{
3400 SolarMutexGuard aGuard;
3402
3403 OUString sUnoCommands[] =
3404 {
3405 OUString(".uno:AlignLeft"),
3406 OUString(".uno:AlignHorizontalCenter"),
3407 OUString(".uno:AlignRight"),
3408 OUString(".uno:BackColor"),
3409 OUString(".uno:BackgroundColor"),
3410 OUString(".uno:TableCellBackgroundColor"),
3411 OUString(".uno:Bold"),
3412 OUString(".uno:CenterPara"),
3413 OUString(".uno:CharBackColor"),
3414 OUString(".uno:CharBackgroundExt"),
3415 OUString(".uno:CharFontName"),
3416 OUString(".uno:Color"),
3417 OUString(".uno:ControlCodes"),
3418 OUString(".uno:DecrementIndent"),
3419 OUString(".uno:DefaultBullet"),
3420 OUString(".uno:DefaultNumbering"),
3421 OUString(".uno:FontColor"),
3422 OUString(".uno:FontHeight"),
3423 OUString(".uno:IncrementIndent"),
3424 OUString(".uno:Italic"),
3425 OUString(".uno:JustifyPara"),
3426 OUString(".uno:JumpToMark"),
3427 OUString(".uno:OutlineFont"),
3428 OUString(".uno:LeftPara"),
3429 OUString(".uno:LanguageStatus"),
3430 OUString(".uno:RightPara"),
3431 OUString(".uno:Shadowed"),
3432 OUString(".uno:SubScript"),
3433 OUString(".uno:SuperScript"),
3434 OUString(".uno:Strikeout"),
3435 OUString(".uno:StyleApply"),
3436 OUString(".uno:Underline"),
3437 OUString(".uno:ModifiedStatus"),
3438 OUString(".uno:Undo"),
3439 OUString(".uno:Redo"),
3440 OUString(".uno:InsertPage"),
3441 OUString(".uno:DeletePage"),
3442 OUString(".uno:DuplicatePage"),
3443 OUString(".uno:InsertSlide"),
3444 OUString(".uno:DeleteSlide"),
3445 OUString(".uno:DuplicateSlide"),
3446 OUString(".uno:ChangeTheme"),
3447 OUString(".uno:Cut"),
3448 OUString(".uno:Copy"),
3449 OUString(".uno:Paste"),
3450 OUString(".uno:SelectAll"),
3451 OUString(".uno:ReplyComment"),
3452 OUString(".uno:ResolveComment"),
3453 OUString(".uno:ResolveCommentThread"),
3454 OUString(".uno:InsertRowsBefore"),
3455 OUString(".uno:InsertRowsAfter"),
3456 OUString(".uno:InsertColumnsBefore"),
3457 OUString(".uno:InsertColumnsAfter"),
3458 OUString(".uno:DeleteRows"),
3459 OUString(".uno:DeleteColumns"),
3460 OUString(".uno:DeleteTable"),
3461 OUString(".uno:SelectTable"),
3462 OUString(".uno:EntireRow"),
3463 OUString(".uno:EntireColumn"),
3464 OUString(".uno:EntireCell"),
3465 OUString(".uno:AssignLayout"),
3466 OUString(".uno:StatusDocPos"),
3467 OUString(".uno:RowColSelCount"),
3468 OUString(".uno:StatusPageStyle"),
3469 OUString(".uno:InsertMode"),
3470 OUString(".uno:SpellOnline"),
3471 OUString(".uno:StatusSelectionMode"),
3472 OUString(".uno:StateTableCell"),
3473 OUString(".uno:StatusBarFunc"),
3474 OUString(".uno:StatePageNumber"),
3475 OUString(".uno:StateWordCount"),
3476 OUString(".uno:SelectionMode"),
3477 OUString(".uno:PageStatus"),
3478 OUString(".uno:LayoutStatus"),
3479 OUString(".uno:Scale"),
3480 OUString(".uno:Context"),
3481 OUString(".uno:WrapText"),
3482 OUString(".uno:ToggleMergeCells"),
3483 OUString(".uno:NumberFormatCurrency"),
3484 OUString(".uno:NumberFormatPercent"),
3485 OUString(".uno:NumberFormatDecimal"),
3486 OUString(".uno:NumberFormatDate"),
3487 OUString(".uno:EditHeaderAndFooter"),
3488 OUString(".uno:FrameLineColor"),
3489 OUString(".uno:SortAscending"),
3490 OUString(".uno:SortDescending"),
3491 OUString(".uno:TrackChanges"),
3492 OUString(".uno:ShowTrackedChanges"),
3493 OUString(".uno:NextTrackedChange"),
3494 OUString(".uno:PreviousTrackedChange"),
3495 OUString(".uno:AcceptAllTrackedChanges"),
3496 OUString(".uno:RejectAllTrackedChanges"),
3497 OUString(".uno:TableDialog"),
3498 OUString(".uno:FormatCellDialog"),
3499 OUString(".uno:FontDialog"),
3500 OUString(".uno:ParagraphDialog"),
3501 OUString(".uno:OutlineBullet"),
3502 OUString(".uno:InsertIndexesEntry"),
3503 OUString(".uno:DocumentRepair"),
3504 OUString(".uno:TransformDialog"),
3505 OUString(".uno:InsertPageHeader"),
3506 OUString(".uno:InsertPageFooter"),
3507 OUString(".uno:OnlineAutoFormat"),
3508 OUString(".uno:InsertObjectChart"),
3509 OUString(".uno:InsertSection"),
3510 OUString(".uno:InsertAnnotation"),
3511 OUString(".uno:DeleteAnnotation"),
3512 OUString(".uno:InsertPagebreak"),
3513 OUString(".uno:InsertColumnBreak"),
3514 OUString(".uno:HyperlinkDialog"),
3515 OUString(".uno:InsertSymbol"),
3516 OUString(".uno:EditRegion"),
3517 OUString(".uno:ThesaurusDialog"),
3518 OUString(".uno:FormatArea"),
3519 OUString(".uno:FormatLine"),
3520 OUString(".uno:FormatColumns"),
3521 OUString(".uno:Watermark"),
3522 OUString(".uno:ResetAttributes"),
3523 OUString(".uno:Orientation"),
3524 OUString(".uno:ObjectAlignLeft"),
3525 OUString(".uno:ObjectAlignRight"),
3526 OUString(".uno:AlignCenter"),
3527 OUString(".uno:TransformPosX"),
3528 OUString(".uno:TransformPosY"),
3529 OUString(".uno:TransformWidth"),
3530 OUString(".uno:TransformHeight"),
3531 OUString(".uno:ObjectBackOne"),
3532 OUString(".uno:SendToBack"),
3533 OUString(".uno:ObjectForwardOne"),
3534 OUString(".uno:BringToFront"),
3535 OUString(".uno:WrapRight"),
3536 OUString(".uno:WrapThrough"),
3537 OUString(".uno:WrapLeft"),
3538 OUString(".uno:WrapIdeal"),
3539 OUString(".uno:WrapOn"),
3540 OUString(".uno:WrapOff"),
3541 OUString(".uno:UpdateCurIndex"),
3542 OUString(".uno:InsertCaptionDialog"),
3543 OUString(".uno:FormatGroup"),
3544 OUString(".uno:SplitTable"),
3545 OUString(".uno:SplitCell"),
3546 OUString(".uno:MergeCells"),
3547 OUString(".uno:DeleteNote"),
3548 OUString(".uno:AcceptChanges"),
3549 OUString(".uno:FormatPaintbrush"),
3550 OUString(".uno:SetDefault"),
3551 OUString(".uno:ParaLeftToRight"),
3552 OUString(".uno:ParaRightToLeft"),
3553 OUString(".uno:ParaspaceIncrease"),
3554 OUString(".uno:ParaspaceDecrease"),
3555 OUString(".uno:AcceptTrackedChange"),
3556 OUString(".uno:RejectTrackedChange"),
3557 OUString(".uno:ShowResolvedAnnotations"),
3558 OUString(".uno:InsertBreak"),
3559 OUString(".uno:InsertEndnote"),
3560 OUString(".uno:InsertFootnote"),
3561 OUString(".uno:InsertReferenceField"),
3562 OUString(".uno:InsertBookmark"),
3563 OUString(".uno:InsertAuthoritiesEntry"),
3564 OUString(".uno:InsertMultiIndex"),
3565 OUString(".uno:InsertField"),
3566 OUString(".uno:PageNumberWizard"),
3567 OUString(".uno:InsertPageNumberField"),
3568 OUString(".uno:InsertPageCountField"),
3569 OUString(".uno:InsertDateField"),
3570 OUString(".uno:InsertTitleField"),
3571 OUString(".uno:InsertFieldCtrl"),
3572 OUString(".uno:CharmapControl"),
3573 OUString(".uno:EnterGroup"),
3574 OUString(".uno:LeaveGroup"),
3575 OUString(".uno:AlignUp"),
3576 OUString(".uno:AlignMiddle"),
3577 OUString(".uno:AlignDown"),
3578 OUString(".uno:TraceChangeMode"),
3579 OUString(".uno:Combine"),
3580 OUString(".uno:Merge"),
3581 OUString(".uno:Dismantle"),
3582 OUString(".uno:Substract"),
3583 OUString(".uno:DistributeSelection"),
3584 OUString(".uno:Intersect"),
3585 OUString(".uno:BorderInner"),
3586 OUString(".uno:BorderOuter"),
3587 OUString(".uno:FreezePanes"),
3588 OUString(".uno:FreezePanesColumn"),
3589 OUString(".uno:FreezePanesRow"),
3590 OUString(".uno:Sidebar"),
3591 OUString(".uno:SheetRightToLeft"),
3592 OUString(".uno:RunMacro"),
3593 OUString(".uno:SpacePara1"),
3594 OUString(".uno:SpacePara15"),
3595 OUString(".uno:SpacePara2"),
3596 OUString(".uno:InsertSparkline"),
3597 OUString(".uno:DeleteSparkline"),
3598 OUString(".uno:DeleteSparklineGroup"),
3599 OUString(".uno:EditSparklineGroup"),
3600 OUString(".uno:EditSparkline"),
3601 OUString(".uno:GroupSparklines"),
3602 OUString(".uno:UngroupSparklines"),
3603 OUString(".uno:FormatSparklineMenu"),
3604 OUString(".uno:Protect"),
3605 OUString(".uno:UnsetCellsReadOnly"),
3606 OUString(".uno:ContentControlProperties"),
3607 OUString(".uno:InsertCheckboxContentControl"),
3608 OUString(".uno:InsertContentControl"),
3609 OUString(".uno:InsertDateContentControl"),
3610 OUString(".uno:InsertDropdownContentControl"),
3611 OUString(".uno:InsertPlainTextContentControl"),
3612 OUString(".uno:InsertPictureContentControl")
3613 };
3614
3615 util::URL aCommandURL;
3616 SfxViewShell* pViewShell = SfxViewShell::Current();
3617 SfxViewFrame* pViewFrame = pViewShell ? &pViewShell->GetViewFrame() : nullptr;
3618
3619 // check if Frame-Controller were created.
3620 if (!pViewFrame)
3621 {
3622 SAL_WARN("lok", "iniUnoCommands: No Frame-Controller created.");
3623 return;
3624 }
3625
3626 if (!xContext.is())
3628 if (!xContext.is())
3629 {
3630 SAL_WARN("lok", "iniUnoCommands: Component context is not available");
3631 return;
3632 }
3633
3634#if !defined IOS && !defined ANDROID && !defined __EMSCRIPTEN__
3636 if (!xSEInitializer.is())
3637 {
3638 SAL_WARN("lok", "iniUnoCommands: XSEInitializer is not available");
3639 return;
3640 }
3641
3643 xSEInitializer->createSecurityContext(OUString());
3644 if (!xSecurityContext.is())
3645 {
3646 SAL_WARN("lok", "iniUnoCommands: failed to create security context");
3647 }
3648#endif
3649
3650 SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(pViewFrame);
3652
3653 for (const auto & sUnoCommand : sUnoCommands)
3654 {
3655 aCommandURL.Complete = sUnoCommand;
3656 xParser->parseStrict(aCommandURL);
3657
3658 // when null, this command is not supported by the given component
3659 // (like eg. Calc does not have ".uno:DefaultBullet" etc.)
3660 if (const SfxSlot* pSlot = rSlotPool.GetUnoSlot(aCommandURL.Path))
3661 {
3662 // Initialize slot to dispatch .uno: Command.
3663 pViewFrame->GetBindings().GetDispatch(pSlot, aCommandURL, false);
3664 }
3665 }
3666}
3667
3668static int doc_getDocumentType (LibreOfficeKitDocument* pThis)
3669{
3670 comphelper::ProfileZone aZone("doc_getDocumentType");
3671
3672 SolarMutexGuard aGuard;
3673 return getDocumentType(pThis);
3674}
3675
3676static int doc_getParts (LibreOfficeKitDocument* pThis)
3677{
3678 comphelper::ProfileZone aZone("doc_getParts");
3679
3680 SolarMutexGuard aGuard;
3681
3682 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3683 if (!pDoc)
3684 {
3685 SetLastExceptionMsg("Document doesn't support tiled rendering");
3686 return 0;
3687 }
3688
3689 return pDoc->getParts();
3690}
3691
3692static int doc_getPart (LibreOfficeKitDocument* pThis)
3693{
3694 comphelper::ProfileZone aZone("doc_getPart");
3695
3696 SolarMutexGuard aGuard;
3698
3699 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3700 if (!pDoc)
3701 {
3702 SetLastExceptionMsg("Document doesn't support tiled rendering");
3703 return 0;
3704 }
3705
3706 return pDoc->getPart();
3707}
3708
3709static void doc_setPartImpl(LibreOfficeKitDocument* pThis, int nPart, bool bAllowChangeFocus = true)
3710{
3711 comphelper::ProfileZone aZone("doc_setPart");
3712
3713 SolarMutexGuard aGuard;
3715
3716 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3717 if (!pDoc)
3718 {
3719 SetLastExceptionMsg("Document doesn't support tiled rendering");
3720 return;
3721 }
3722
3723 pDoc->setPart( nPart, bAllowChangeFocus );
3724}
3725
3726static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart)
3727{
3728 doc_setPartImpl(pThis, nPart, true);
3729}
3730
3731static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart)
3732{
3733 comphelper::ProfileZone aZone("doc_getPartInfo");
3734
3735 SolarMutexGuard aGuard;
3736 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3737 if (!pDoc)
3738 {
3739 SetLastExceptionMsg("Document doesn't support tiled rendering");
3740 return nullptr;
3741 }
3742
3743 return convertOUString(pDoc->getPartInfo(nPart));
3744}
3745
3746static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect)
3747{
3748 SolarMutexGuard aGuard;
3749 if (gImpl)
3750 gImpl->maLastExceptionMsg.clear();
3751
3752 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3753 if (!pDoc)
3754 {
3755 gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
3756 return;
3757 }
3758
3759 pDoc->selectPart( nPart, nSelect );
3760}
3761
3762static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate)
3763{
3764 SolarMutexGuard aGuard;
3765 if (gImpl)
3766 gImpl->maLastExceptionMsg.clear();
3767
3768 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3769 if (!pDoc)
3770 {
3771 gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
3772 return;
3773 }
3774
3775 pDoc->moveSelectedParts(nPosition, bDuplicate);
3776}
3777
3778static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis)
3779{
3780 comphelper::ProfileZone aZone("doc_getPartPageRectangles");
3781
3782 SolarMutexGuard aGuard;
3784
3785 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3786 if (!pDoc)
3787 {
3788 SetLastExceptionMsg("Document doesn't support tiled rendering");
3789 return nullptr;
3790 }
3791
3792 return convertOUString(pDoc->getPartPageRectangles());
3793}
3794
3795static char* doc_getA11yFocusedParagraph(LibreOfficeKitDocument* pThis)
3796{
3797 SolarMutexGuard aGuard;
3799
3800 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3801 if (!pDoc)
3802 {
3803 SetLastExceptionMsg("Document doesn't support tiled rendering");
3804 return nullptr;
3805 }
3806
3807 if (SfxViewShell* pViewShell = SfxViewShell::Current())
3808 {
3809 return convertOUString(pViewShell->getA11yFocusedParagraph());
3810
3811 }
3812 return nullptr;
3813}
3814
3815static int doc_getA11yCaretPosition(LibreOfficeKitDocument* pThis)
3816{
3817 SolarMutexGuard aGuard;
3819
3820 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3821 if (!pDoc)
3822 {
3823 SetLastExceptionMsg("Document doesn't support tiled rendering");
3824 return -1;
3825 }
3826 if (SfxViewShell* pViewShell = SfxViewShell::Current())
3827 {
3828 return pViewShell->getA11yCaretPosition();
3829
3830 }
3831 return -1;
3832
3833}
3834
3835static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart)
3836{
3837 comphelper::ProfileZone aZone("doc_getPartName");
3838
3839 SolarMutexGuard aGuard;
3841
3842 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3843 if (!pDoc)
3844 {
3845 SetLastExceptionMsg("Document doesn't support tiled rendering");
3846 return nullptr;
3847 }
3848
3849 return convertOUString(pDoc->getPartName(nPart));
3850}
3851
3852static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart)
3853{
3854 comphelper::ProfileZone aZone("doc_getPartHash");
3855
3856 SolarMutexGuard aGuard;
3858
3859 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3860 if (!pDoc)
3861 {
3862 SetLastExceptionMsg("Document doesn't support tiled rendering");
3863 return nullptr;
3864 }
3865
3866 return convertOUString(pDoc->getPartHash(nPart));
3867}
3868
3869static void doc_setPartMode(LibreOfficeKitDocument* pThis,
3870 int nPartMode)
3871{
3872 comphelper::ProfileZone aZone("doc_setPartMode");
3873
3874 SolarMutexGuard aGuard;
3876
3877 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3878 if (!pDoc)
3879 {
3880 SetLastExceptionMsg("Document doesn't support tiled rendering");
3881 return;
3882 }
3883
3884
3885 int nCurrentPart = pDoc->getPart();
3886
3887 pDoc->setPartMode(nPartMode);
3888
3889 // We need to make sure the internal state is updated, just changing the mode
3890 // might not update the relevant shells (i.e. impress will keep rendering the
3891 // previous mode unless we do this).
3892 // TODO: we might want to do this within the relevant components rather than
3893 // here, but that's also dependent on how we implement embedded object
3894 // rendering I guess?
3895 // TODO: we could be clever and e.g. set to 0 when we change to/from
3896 // embedded object mode, and not when changing between slide/notes/combined
3897 // modes?
3898 if ( nCurrentPart < pDoc->getParts() )
3899 {
3900 pDoc->setPart( nCurrentPart );
3901 }
3902 else
3903 {
3904 pDoc->setPart( 0 );
3905 }
3906}
3907
3908static int doc_getEditMode(LibreOfficeKitDocument* pThis)
3909{
3910 comphelper::ProfileZone aZone("doc_getEditMode");
3911
3912 SolarMutexGuard aGuard;
3914
3915 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3916 if (!pDoc)
3917 {
3918 SetLastExceptionMsg("Document doesn't support tiled rendering");
3919 return 0;
3920 }
3921
3922 return pDoc->getEditMode();
3923}
3924
3925static void doc_paintTile(LibreOfficeKitDocument* pThis,
3926 unsigned char* pBuffer,
3927 const int nCanvasWidth, const int nCanvasHeight,
3928 const int nTilePosX, const int nTilePosY,
3929 const int nTileWidth, const int nTileHeight)
3930{
3931 comphelper::ProfileZone aZone("doc_paintTile");
3932
3933 SolarMutexGuard aGuard;
3935
3936 SAL_INFO( "lok.tiledrendering", "paintTile: painting [" << nTileWidth << "x" << nTileHeight <<
3937 "]@(" << nTilePosX << ", " << nTilePosY << ") to [" <<
3938 nCanvasWidth << "x" << nCanvasHeight << "]px" );
3939
3940 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3941 if (!pDoc)
3942 {
3943 SetLastExceptionMsg("Document doesn't support tiled rendering");
3944 return;
3945 }
3946
3947#if defined(UNX) && !defined(MACOSX) || defined(_WIN32)
3948
3949 // Painting of zoomed or HiDPI spreadsheets is special, we actually draw everything at 100%,
3950 // and only set cairo's (or CoreGraphic's, in the iOS case) scale factor accordingly, so that
3951 // everything is painted bigger or smaller. This is different to what Calc's internal scaling
3952 // would do - because that one is trying to fit the lines between cells to integer multiples of
3953 // pixels.
3955
3956#if defined(IOS)
3957 double fDPIScale = 1.0;
3958
3959 CGContextRef pCGContext = CGBitmapContextCreate(pBuffer, nCanvasWidth, nCanvasHeight, 8,
3960 nCanvasWidth * 4, CGColorSpaceCreateDeviceRGB(),
3961 kCGImageAlphaPremultipliedFirst | kCGImageByteOrder32Little);
3962
3963 CGContextTranslateCTM(pCGContext, 0, nCanvasHeight);
3964 CGContextScaleCTM(pCGContext, fDPIScale, -fDPIScale);
3965
3966 SAL_INFO( "lok.tiledrendering", "doc_paintTile: painting [" << nTileWidth << "x" << nTileHeight <<
3967 "]@(" << nTilePosX << ", " << nTilePosY << ") to [" <<
3968 nCanvasWidth << "x" << nCanvasHeight << "]px" );
3969
3970 Size aCanvasSize(nCanvasWidth, nCanvasHeight);
3971
3973 aData.rCGContext = reinterpret_cast<CGContextRef>(pCGContext);
3974
3975 ScopedVclPtrInstance<VirtualDevice> pDevice(aData, Size(1, 1), DeviceFormat::WITHOUT_ALPHA);
3976 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3977 pDevice->SetOutputSizePixel(aCanvasSize);
3978 pDoc->paintTile(*pDevice, aCanvasSize.Width(), aCanvasSize.Height(),
3979 nTilePosX, nTilePosY, nTileWidth, nTileHeight);
3980
3981 CGContextRelease(pCGContext);
3982#else
3983 ScopedVclPtrInstance< VirtualDevice > pDevice(DeviceFormat::WITHOUT_ALPHA);
3984
3985 // Set background to transparent by default.
3986 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3987
3988 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(
3989 Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(),
3990 pBuffer);
3991
3992 pDoc->paintTile(*pDevice, nCanvasWidth, nCanvasHeight,
3993 nTilePosX, nTilePosY, nTileWidth, nTileHeight);
3994
3995 static bool bDebug = getenv("LOK_DEBUG_TILES") != nullptr;
3996 if (bDebug)
3997 {
3998 // Draw a small red rectangle in the top left corner so that it's easy to see where a new tile begins.
3999 tools::Rectangle aRect(0, 0, 5, 5);
4000 aRect = pDevice->PixelToLogic(aRect);
4001 pDevice->Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
4002 pDevice->SetFillColor(COL_LIGHTRED);
4003 pDevice->SetLineColor();
4004 pDevice->DrawRect(aRect);
4005 pDevice->Pop();
4006 }
4007
4008#ifdef _WIN32
4009 // pBuffer was not used there
4010 pDevice->EnableMapMode(false);
4011 BitmapEx aBmpEx = pDevice->GetBitmapEx({ 0, 0 }, { nCanvasWidth, nCanvasHeight });
4012 Bitmap aBmp = aBmpEx.GetBitmap();
4013 AlphaMask aAlpha = aBmpEx.GetAlphaMask();
4014 Bitmap::ScopedReadAccess sraBmp(aBmp);
4015 Bitmap::ScopedReadAccess sraAlpha(aAlpha);
4016
4017 assert(sraBmp->Height() == nCanvasHeight);
4018 assert(sraBmp->Width() == nCanvasWidth);
4019 assert(!sraAlpha || sraBmp->Height() == sraAlpha->Height());
4020 assert(!sraAlpha || sraBmp->Width() == sraAlpha->Width());
4021 auto p = pBuffer;
4022 for (tools::Long y = 0; y < sraBmp->Height(); ++y)
4023 {
4024 Scanline dataBmp = sraBmp->GetScanline(y);
4025 Scanline dataAlpha = sraAlpha ? sraAlpha->GetScanline(y) : nullptr;
4026 for (tools::Long x = 0; x < sraBmp->Width(); ++x)
4027 {
4028 BitmapColor color = sraBmp->GetPixelFromData(dataBmp, x);
4029 sal_uInt8 alpha = dataAlpha ? sraAlpha->GetPixelFromData(dataAlpha, x).GetBlue() : 255;
4030 *p++ = color.GetBlue();
4031 *p++ = color.GetGreen();
4032 *p++ = color.GetRed();
4033 *p++ = alpha;
4034 }
4035 }
4036#endif
4037#endif
4038
4039#else
4040 (void) pBuffer;
4041#endif
4042}
4043
4044static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
4045 unsigned char* pBuffer,
4046 const int nPart,
4047 const int nMode,
4048 const int nCanvasWidth, const int nCanvasHeight,
4049 const int nTilePosX, const int nTilePosY,
4050 const int nTileWidth, const int nTileHeight)
4051{
4052 comphelper::ProfileZone aZone("doc_paintPartTile");
4053
4054 SolarMutexGuard aGuard;
4056
4057 SAL_INFO( "lok.tiledrendering", "paintPartTile: painting @ " << nPart << " : " << nMode << " ["
4058 << nTileWidth << "x" << nTileHeight << "]@("
4059 << nTilePosX << ", " << nTilePosY << ") to ["
4060 << nCanvasWidth << "x" << nCanvasHeight << "]px" );
4061
4062 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4063 int nOrigViewId = doc_getView(pThis);
4064
4065 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4066 if (!pDoc)
4067 {
4068 SetLastExceptionMsg("Document doesn't support tiled rendering");
4069 return;
4070 }
4071
4072 if (nOrigViewId < 0)
4073 {
4074 // tile painting always needs a SfxViewShell::Current(), but actually
4075 // it does not really matter which one - all of them should paint the
4076 // same thing. It's important to get a view for the correct document,
4077 // though.
4078 // doc_getViewsCount() returns the count of views for the document in the current view.
4079 int viewCount = doc_getViewsCount(pThis);
4080 if (viewCount == 0)
4081 return;
4082
4083 std::vector<int> viewIds(viewCount);
4084 doc_getViewIds(pThis, viewIds.data(), viewCount);
4085
4086 nOrigViewId = viewIds[0];
4087 doc_setView(pThis, nOrigViewId);
4088 }
4089
4090 // Disable callbacks while we are painting.
4091 if (nOrigViewId >= 0)
4092 {
4093 const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId);
4094 if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
4095 handlerIt->second->disableCallbacks();
4096 }
4097
4098 try
4099 {
4100 // Text documents have a single coordinate system; don't change part.
4101 int nOrigPart = 0;
4102 const int aType = doc_getDocumentType(pThis);
4103 const bool isText = (aType == LOK_DOCTYPE_TEXT);
4104 const bool isCalc = (aType == LOK_DOCTYPE_SPREADSHEET);
4105 int nOrigEditMode = 0;
4106 bool bPaintTextEdit = true;
4107 int nViewId = nOrigViewId;
4108 int nLastNonEditorView = -1;
4109 int nViewMatchingMode = -1;
4110 if (!isText)
4111 {
4112 // Check if just switching to another view is enough, that has
4113 // less side-effects.
4114 if (nPart != doc_getPart(pThis) || nMode != pDoc->getEditMode())
4115 {
4116 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
4117 while (pViewShell)
4118 {
4119 bool bIsInEdit = pViewShell->GetDrawView() &&
4120 pViewShell->GetDrawView()->GetTextEditOutliner();
4121 if (!bIsInEdit)
4122 nLastNonEditorView = pViewShell->GetViewShellId().get();
4123
4124 if (pViewShell->getPart() == nPart &&
4125 pViewShell->getEditMode() == nMode &&
4126 !bIsInEdit)
4127 {
4128 nViewId = pViewShell->GetViewShellId().get();
4129 nViewMatchingMode = nViewId;
4130 nLastNonEditorView = nViewId;
4131 doc_setView(pThis, nViewId);
4132 break;
4133 }
4134 else if (pViewShell->getEditMode() == nMode && !bIsInEdit)
4135 {
4136 nViewMatchingMode = pViewShell->GetViewShellId().get();
4137 }
4138
4139 pViewShell = SfxViewShell::GetNext(*pViewShell);
4140 }
4141 }
4142
4143 // if not found view with correct part
4144 // - at least avoid rendering active textbox, This is for Impress.
4145 // - prefer view with the same mode
4146 SfxViewShell* pCurrentViewShell = SfxViewShell::Current();
4147 if (nViewMatchingMode >= 0 && nViewMatchingMode != nViewId)
4148 {
4149 nViewId = nViewMatchingMode;
4150 doc_setView(pThis, nViewId);
4151 }
4152 else if (!isCalc && nLastNonEditorView >= 0 && nLastNonEditorView != nViewId &&
4153 pCurrentViewShell && pCurrentViewShell->GetDrawView() &&
4154 pCurrentViewShell->GetDrawView()->GetTextEditOutliner())
4155 {
4156 nViewId = nLastNonEditorView;
4157 doc_setView(pThis, nViewId);
4158 }
4159
4160 // Disable callbacks while we are painting - after setting the view
4161 if (nViewId != nOrigViewId && nViewId >= 0)
4162 {
4163 const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nViewId);
4164 if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
4165 handlerIt->second->disableCallbacks();
4166 }
4167
4168 nOrigPart = doc_getPart(pThis);
4169 if (nPart != nOrigPart)
4170 {
4171 doc_setPartImpl(pThis, nPart, false);
4172 }
4173
4174 nOrigEditMode = pDoc->getEditMode();
4175 if (nOrigEditMode != nMode)
4176 {
4177 SfxLokHelper::setEditMode(nMode, pDoc);
4178 }
4179
4180 bPaintTextEdit = (nPart == nOrigPart && nMode == nOrigEditMode);
4181 pDoc->setPaintTextEdit(bPaintTextEdit);
4182 }
4183
4184 doc_paintTile(pThis, pBuffer, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
4185
4186 if (!isText)
4187 {
4188 pDoc->setPaintTextEdit(true);
4189
4190 if (nMode != nOrigEditMode)
4191 {
4192 SfxLokHelper::setEditMode(nOrigEditMode, pDoc);
4193 }
4194
4195 if (nPart != nOrigPart)
4196 {
4197 doc_setPartImpl(pThis, nOrigPart, false);
4198 }
4199
4200 if (nViewId != nOrigViewId)
4201 {
4202 if (nViewId >= 0)
4203 {
4204 const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nViewId);
4205 if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
4206 handlerIt->second->enableCallbacks();
4207 }
4208
4209 doc_setView(pThis, nOrigViewId);
4210 }
4211 }
4212 }
4213 catch (const std::exception&)
4214 {
4215 // Nothing to do but restore the PartTilePainting flag.
4216 }
4217
4218 if (nOrigViewId >= 0)
4219 {
4220 const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId);
4221 if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
4222 handlerIt->second->enableCallbacks();
4223 }
4224}
4225
4226static int doc_getTileMode(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
4227{
4229 return LOK_TILEMODE_BGRA;
4230}
4231
4232static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
4233 long* pWidth,
4234 long* pHeight)
4235{
4236 comphelper::ProfileZone aZone("doc_getDocumentSize");
4237
4238 SolarMutexGuard aGuard;
4240
4241 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4242 if (pDoc)
4243 {
4244 Size aDocumentSize = pDoc->getDocumentSize();
4245 *pWidth = aDocumentSize.Width();
4246 *pHeight = aDocumentSize.Height();
4247 }
4248 else
4249 {
4250 SetLastExceptionMsg("Document doesn't support tiled rendering");
4251 }
4252}
4253
4254static void doc_getDataArea(LibreOfficeKitDocument* pThis,
4255 long nTab,
4256 long* pCol,
4257 long* pRow)
4258{
4259 comphelper::ProfileZone aZone("doc_getDataArea");
4260
4261 SolarMutexGuard aGuard;
4263
4264 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4265 if (pDoc)
4266 {
4267 Size aDocumentSize = pDoc->getDataArea(nTab);
4268 *pCol = aDocumentSize.Width();
4269 *pRow = aDocumentSize.Height();
4270 }
4271 else
4272 {
4273 SetLastExceptionMsg("Document doesn't support tiled rendering");
4274 }
4275}
4276
4277static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
4278 const char* pArguments)
4279{
4280 comphelper::ProfileZone aZone("doc_initializeForRendering");
4281
4282 SolarMutexGuard aGuard;
4284
4285 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4286 if (pDoc)
4287 {
4289 pDoc->initializeForTiledRendering(
4291 }
4292}
4293
4294static void doc_registerCallback(LibreOfficeKitDocument* pThis,
4295 LibreOfficeKitCallback pCallback,
4296 void* pData)
4297{
4298 SolarMutexGuard aGuard;
4300
4301 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4302
4303 const int nView = SfxLokHelper::getView();
4304 if (nView < 0)
4305 return;
4306
4307 const size_t nId = nView;
4308 if (pCallback != nullptr)
4309 {
4310 for (auto& pair : pDocument->mpCallbackFlushHandlers)
4311 {
4312 if (pair.first == nId)
4313 continue;
4314
4315 pair.second->addViewStates(nView);
4316 }
4317 }
4318 else
4319 {
4320 for (auto& pair : pDocument->mpCallbackFlushHandlers)
4321 {
4322 if (pair.first == nId)
4323 continue;
4324
4325 pair.second->removeViewStates(nView);
4326 }
4327 }
4328
4329 pDocument->mpCallbackFlushHandlers[nView] = std::make_shared<CallbackFlushHandler>(pThis, pCallback, pData);
4330
4331 if (pCallback != nullptr)
4332 {
4333 for (const auto& pair : pDocument->mpCallbackFlushHandlers)
4334 {
4335 if (pair.first == nId)
4336 continue;
4337
4338 pDocument->mpCallbackFlushHandlers[nView]->addViewStates(pair.first);
4339 }
4340
4341 if (SfxViewShell* pViewShell = SfxViewShell::Current())
4342 {
4343 pDocument->mpCallbackFlushHandlers[nView]->setViewId(pViewShell->GetViewShellId().get());
4344 pViewShell->setLibreOfficeKitViewCallback(pDocument->mpCallbackFlushHandlers[nView].get());
4345 }
4346
4347 if (pDocument->maFontsMissing.size() != 0)
4348 {
4349 std::string sPayload = "{ \"fontsmissing\": [ ";
4350 bool bFirst = true;
4351 for (const auto &f : pDocument->maFontsMissing)
4352 {
4353 if (bFirst)
4354 bFirst = false;
4355 else
4356 sPayload += ", ";
4357 sPayload += "\"" + std::string(f.toUtf8()) + "\"";
4358 }
4359 sPayload += " ] }";
4360 pCallback(LOK_CALLBACK_FONTS_MISSING, sPayload.c_str(), pData);
4361 pDocument->maFontsMissing.clear();
4362 }
4363 }
4364 else
4365 {
4366 if (SfxViewShell* pViewShell = SfxViewShell::Current())
4367 {
4368 pViewShell->setLibreOfficeKitViewCallback(nullptr);
4369 pDocument->mpCallbackFlushHandlers[nView]->setViewId(-1);
4370 }
4371 }
4372}
4373
4375static char* getPostIts(LibreOfficeKitDocument* pThis)
4376{
4378 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4379 if (!pDoc)
4380 {
4381 SetLastExceptionMsg("Document doesn't support tiled rendering");
4382 return nullptr;
4383 }
4384 tools::JsonWriter aJsonWriter;
4385 pDoc->getPostIts(aJsonWriter);
4386 return strdup(aJsonWriter.finishAndGetAsOString().getStr());
4387}
4388
4390static char* getPostItsPos(LibreOfficeKitDocument* pThis)
4391{
4393 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4394 if (!pDoc)
4395 {
4396 SetLastExceptionMsg("Document doesn't support tiled rendering");
4397 return nullptr;
4398 }
4399 tools::JsonWriter aJsonWriter;
4400 pDoc->getPostItsPos(aJsonWriter);
4401 return strdup(aJsonWriter.finishAndGetAsOString().getStr());
4402}
4403
4404static char* getRulerState(LibreOfficeKitDocument* pThis)
4405{
4407 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4408 if (!pDoc)
4409 {
4410 SetLastExceptionMsg("Document doesn't support tiled rendering");
4411 return nullptr;
4412 }
4413 tools::JsonWriter aJsonWriter;
4414 pDoc->getRulerState(aJsonWriter);
4415 return strdup(aJsonWriter.finishAndGetAsOString().getStr());
4416}
4417
4418static void doc_postKeyEvent(LibreOfficeKitDocument* pThis, int nType, int nCharCode, int nKeyCode)
4419{
4420 comphelper::ProfileZone aZone("doc_postKeyEvent");
4421
4422 SolarMutexGuard aGuard;
4424
4425 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4426 if (!pDoc)
4427 {
4428 SetLastExceptionMsg("Document doesn't support tiled rendering");
4429 return;
4430 }
4431
4432 try
4433 {
4434 pDoc->postKeyEvent(nType, nCharCode, nKeyCode);
4435 }
4436 catch (const uno::Exception& exception)
4437 {
4438 SetLastExceptionMsg(exception.Message);
4439 SAL_INFO("lok", "Failed to postKeyEvent " << exception.Message);
4440 }
4441}
4442
4443static void doc_setBlockedCommandList(LibreOfficeKitDocument* /*pThis*/, int nViewId, const char* blockedCommandList)
4444{
4445 SolarMutexGuard aGuard;
4446 SfxLokHelper::setBlockedCommandList(nViewId, blockedCommandList);
4447}
4448
4449static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis, unsigned nWindowId, int nType, const char* pText)
4450{
4451 comphelper::ProfileZone aZone("doc_postWindowExtTextInputEvent");
4452
4453 SolarMutexGuard aGuard;
4454 VclPtr<vcl::Window> pWindow;
4455 if (nWindowId == 0)
4456 {
4457 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4458 if (!pDoc)
4459 {
4460 SetLastExceptionMsg("Document doesn't support tiled rendering");
4461 return;
4462 }
4463 pWindow = pDoc->getDocWindow();
4464 }
4465 else
4466 {
4467 pWindow = vcl::Window::FindLOKWindow(nWindowId);
4468 }
4469
4470 if (!pWindow)
4471 {
4472 SetLastExceptionMsg("No window found for window id: " + OUString::number(nWindowId));
4473 return;
4474 }
4475
4476 SfxLokHelper::postExtTextEventAsync(pWindow, nType, OUString::fromUtf8(std::string_view(pText, strlen(pText))));
4477}
4478
4479static void doc_removeTextContext(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, int nCharBefore, int nCharAfter)
4480{
4481 SolarMutexGuard aGuard;
4482 VclPtr<vcl::Window> pWindow;
4483 if (nLOKWindowId == 0)
4484 {
4485 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4486 if (!pDoc)
4487 {
4488 gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
4489 return;
4490 }
4491 pWindow = pDoc->getDocWindow();
4492 }
4493 else
4494 {
4495 pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
4496 }
4497
4498 if (!pWindow)
4499 {
4500 gImpl->maLastExceptionMsg = "No window found for window id: " + OUString::number(nLOKWindowId);
4501 return;
4502 }
4503
4504 // Annoyingly - backspace and delete are handled in the apps via an accelerator
4505 // which are PostMessage'd by SfxViewShell::ExecKey_Impl so to stay in the same
4506 // order we do this synchronously here, unless we're in a dialog.
4507 if (nCharBefore > 0)
4508 {
4509 // backspace
4510 if (nLOKWindowId == 0)
4511 {
4512 KeyEvent aEvt(8, 1283);
4513 for (int i = 0; i < nCharBefore; ++i)
4514 pWindow->KeyInput(aEvt);
4515 }
4516 else
4517 SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 8, 1283, nCharBefore - 1);
4518 }
4519
4520 if (nCharAfter > 0)
4521 {
4522 // delete (forward)
4523 if (nLOKWindowId == 0)
4524 {
4525 KeyEvent aEvt(46, 1286);
4526 for (int i = 0; i < nCharAfter; ++i)
4527 pWindow->KeyInput(aEvt);
4528 }
4529 else
4530 SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 46, 1286, nCharAfter - 1);
4531 }
4532}
4533
4534static void doc_postWindowKeyEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nCharCode, int nKeyCode)
4535{
4536 comphelper::ProfileZone aZone("doc_postWindowKeyEvent");
4537
4538 SolarMutexGuard aGuard;
4540
4541 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
4542 if (!pWindow)
4543 {
4544 SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
4545 return;
4546 }
4547
4548 KeyEvent aEvent(nCharCode, nKeyCode, 0);
4549
4550 switch (nType)
4551 {
4552 case LOK_KEYEVENT_KEYINPUT:
4553 Application::PostKeyEvent(VclEventId::WindowKeyInput, pWindow, &aEvent);
4554 break;
4555 case LOK_KEYEVENT_KEYUP:
4556 Application::PostKeyEvent(VclEventId::WindowKeyUp, pWindow, &aEvent);
4557 break;
4558 default:
4559 assert(false);
4560 break;
4561 }
4562}
4563
4564static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput)
4565{
4566 comphelper::ProfileZone aZone("doc_renderShapeSelection");
4567
4568 SolarMutexGuard aGuard;
4570
4571 LokChartHelper aChartHelper(SfxViewShell::Current());
4572
4573 if (aChartHelper.GetWindow())
4574 return 0;
4575
4576 try
4577 {
4578 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4579
4580 uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
4581
4582 SvMemoryStream aOutStream;
4584
4585 utl::MediaDescriptor aMediaDescriptor;
4586 switch (doc_getDocumentType(pThis))
4587 {
4588 case LOK_DOCTYPE_PRESENTATION:
4589 aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export");
4590 break;
4591 case LOK_DOCTYPE_DRAWING:
4592 aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export");
4593 break;
4594 case LOK_DOCTYPE_TEXT:
4595 aMediaDescriptor["FilterName"] <<= OUString("writer_svg_Export");
4596 break;
4597 case LOK_DOCTYPE_SPREADSHEET:
4598 aMediaDescriptor["FilterName"] <<= OUString("calc_svg_Export");
4599 break;
4600 default:
4601 SAL_WARN("lok", "Failed to render shape selection: Document type is not supported");
4602 }
4603 aMediaDescriptor["SelectionOnly"] <<= true;
4604 aMediaDescriptor["OutputStream"] <<= xOut;
4605 aMediaDescriptor["IsPreview"] <<= true; // will down-scale graphics
4606
4607 xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
4608
4609 if (pOutput)
4610 {
4611 const size_t nOutputSize = aOutStream.GetEndOfData();
4612 *pOutput = static_cast<char*>(malloc(nOutputSize));
4613 if (*pOutput)
4614 {
4615 std::memcpy(*pOutput, aOutStream.GetData(), nOutputSize);
4616 return nOutputSize;
4617 }
4618 }
4619 }
4620 catch (const uno::Exception& exception)
4621 {
4622 css::uno::Any exAny( cppu::getCaughtException() );
4623 SetLastExceptionMsg(exception.Message);
4624 SAL_WARN("lok", "Failed to render shape selection: " << exceptionToString(exAny));
4625 }
4626
4627 return 0;
4628}
4629
4630namespace {
4631
4639class DispatchResultListener : public cppu::WeakImplHelper<css::frame::XDispatchResultListener>
4640{
4641 const OString maCommand;
4642 const std::shared_ptr<CallbackFlushHandler> mpCallback;
4643 const std::chrono::steady_clock::time_point mSaveTime; //< The time we started saving.
4644 const bool mbWasModified; //< Whether or not the document was modified before saving.
4645
4646public:
4647 DispatchResultListener(const char* pCommand, std::shared_ptr<CallbackFlushHandler> pCallback)
4648 : maCommand(pCommand)
4649 , mpCallback(std::move(pCallback))
4650 , mSaveTime(std::chrono::steady_clock::now())
4651 , mbWasModified(SfxObjectShell::Current()->IsModified())
4652 {
4653 assert(mpCallback);
4654 }
4655
4656 virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& rEvent) override
4657 {
4658 tools::JsonWriter aJson;
4659 aJson.put("commandName", maCommand);
4660
4661 if (rEvent.State != frame::DispatchResultState::DONTKNOW)
4662 {
4663 bool bSuccess = (rEvent.State == frame::DispatchResultState::SUCCESS);
4664 aJson.put("success", bSuccess);
4665 }
4666
4667 unoAnyToJson(aJson, "result", rEvent.Result);
4668 aJson.put("wasModified", mbWasModified);
4669 aJson.put("startUnixTimeMics",
4670 std::chrono::time_point_cast<std::chrono::microseconds>(mSaveTime)
4671 .time_since_epoch()
4672 .count());
4673 aJson.put("saveDurationMics", std::chrono::duration_cast<std::chrono::microseconds>(
4674 std::chrono::steady_clock::now() - mSaveTime)
4675 .count());
4676 mpCallback->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.finishAndGetAsOString());
4677 }
4678
4679 virtual void SAL_CALL disposing(const css::lang::EventObject&) override {}
4680};
4681
4682} // anonymous namespace
4683
4684
4685static void lcl_sendDialogEvent(unsigned long long int nWindowId, const char* pArguments)
4686{
4687 SolarMutexGuard aGuard;
4688
4690
4691 if (aMap.find("id") == aMap.end())
4692 return;
4693
4694 sal_uInt64 nCurrentShellId = reinterpret_cast<sal_uInt64>(SfxViewShell::Current());
4695
4696 try
4697 {
4698 OUString sControlId = aMap["id"];
4699
4700 // dialogs send own id but notebookbar and sidebar controls are remembered by SfxViewShell id
4701 if (jsdialog::ExecuteAction(OUString::number(nWindowId), sControlId, aMap))
4702 return;
4703 auto sCurrentShellId = OUString::number(nCurrentShellId);
4704 if (jsdialog::ExecuteAction(sCurrentShellId + "sidebar", sControlId, aMap))
4705 return;
4706 if (jsdialog::ExecuteAction(sCurrentShellId + "notebookbar", sControlId, aMap))
4707 return;
4708 if (jsdialog::ExecuteAction(sCurrentShellId + "formulabar", sControlId, aMap))
4709 return;
4710 // this is needed for dialogs shown before document is loaded: MacroWarning dialog, etc...
4711 // these dialogs are created with WindowId "0"
4712 if (!SfxViewShell::Current() && jsdialog::ExecuteAction("0", sControlId, aMap))
4713 return;
4714
4715 // force resend - used in mobile-wizard
4716 jsdialog::SendFullUpdate(sCurrentShellId + "sidebar", "Panel");
4717
4718 } catch(...) {}
4719}
4720
4721
4722static void doc_sendDialogEvent(LibreOfficeKitDocument* /*pThis*/, unsigned long long int nWindowId, const char* pArguments)
4723{
4724 lcl_sendDialogEvent(nWindowId, pArguments);
4725}
4726
4727static void lo_sendDialogEvent(LibreOfficeKit* /*pThis*/, unsigned long long int nWindowId, const char* pArguments)
4728{
4729 lcl_sendDialogEvent(nWindowId, pArguments);
4730}
4731
4732static void lo_setOption(LibreOfficeKit* /*pThis*/, const char *pOption, const char* pValue)
4733{
4734 static char* pCurrentSalLogOverride = nullptr;
4735
4736 if (strcmp(pOption, "traceeventrecording") == 0)
4737 {
4738 if (strcmp(pValue, "start") == 0)
4739 {
4740 comphelper::TraceEvent::setBufferSizeAndCallback(100, TraceEventDumper::flushRecordings);
4742 if (traceEventDumper == nullptr)
4743 traceEventDumper = new TraceEventDumper();
4744 }
4745 else if (strcmp(pValue, "stop") == 0)
4747 }
4748 else if (strcmp(pOption, "sallogoverride") == 0)
4749 {
4750 if (pCurrentSalLogOverride != nullptr)
4751 free(pCurrentSalLogOverride);
4752 if (pValue == nullptr)
4753 pCurrentSalLogOverride = nullptr;
4754 else
4755 pCurrentSalLogOverride = strdup(pValue);
4756
4757 if (pCurrentSalLogOverride == nullptr || pCurrentSalLogOverride[0] == '\0')
4758 sal_detail_set_log_selector(nullptr);
4759 else
4760 sal_detail_set_log_selector(pCurrentSalLogOverride);
4761 }
4762#ifdef LINUX
4763 else if (strcmp(pOption, "addfont") == 0)
4764 {
4765 if (memcmp(pValue, "file://", 7) == 0)
4766 pValue += 7;
4767
4768 int fd = open(pValue, O_RDONLY);
4769 if (fd == -1)
4770 {
4771 std::cerr << "Could not open font file '" << pValue << "': " << strerror(errno) << std::endl;
4772 return;
4773 }
4774
4775 OUString sMagicFileName = "file:///:FD:/" + OUString::number(fd);
4776
4779 pDevice->AddTempDevFont(sMagicFileName, "");
4781 }
4782#endif
4783}
4784
4785static void lo_dumpState (LibreOfficeKit* pThis, const char* /* pOptions */, char** pState)
4786{
4787 if (!pState)
4788 return;
4789
4790 // NB. no SolarMutexGuard since this may be caused in some extremis / deadlock
4792
4793 *pState = nullptr;
4794 OStringBuffer aState(4096*256);
4795
4796 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
4797
4798 pLib->dumpState(aState);
4799
4800 OString aStr = aState.makeStringAndClear();
4801 *pState = strdup(aStr.getStr());
4802}
4803
4804void LibLibreOffice_Impl::dumpState(rtl::OStringBuffer &rState)
4805{
4806 rState.append("LibreOfficeKit state:");
4807 rState.append("\n\tLastExceptionMsg:\t");
4808 rState.append(rtl::OUStringToOString(maLastExceptionMsg, RTL_TEXTENCODING_UTF8));
4809 rState.append("\n\tUnipoll:\t");
4810 rState.append(vcl::lok::isUnipoll() ? "yes" : "no: events on thread");
4811 rState.append("\n\tOptionalFeatures:\t0x");
4812 rState.append(static_cast<sal_Int64>(mOptionalFeatures), 16);
4813 rState.append("\n\tCallbackData:\t0x");
4814 rState.append(reinterpret_cast<sal_Int64>(mpCallback), 16);
4815 // TODO: dump mInteractionMap
4817 vcl::lok::dumpState(rState);
4818}
4819
4820static void doc_postUnoCommand(LibreOfficeKitDocument* pThis, const char* pCommand, const char* pArguments, bool bNotifyWhenFinished)
4821{
4822 comphelper::ProfileZone aZone("doc_postUnoCommand");
4823
4824 SolarMutexGuard aGuard;
4826
4828 OUString aCommand(pCommand, strlen(pCommand), RTL_TEXTENCODING_UTF8);
4829 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4830
4831 std::vector<beans::PropertyValue> aPropertyValuesVector(jsonToPropertyValuesVector(pArguments));
4832
4833 if (!vcl::lok::isUnipoll())
4834 {
4835 beans::PropertyValue aSynchronMode;
4836 aSynchronMode.Name = "SynchronMode";
4837 aSynchronMode.Value <<= false;
4838 aPropertyValuesVector.push_back(aSynchronMode);
4839 }
4840
4841 int nView = SfxLokHelper::getView();
4842 if (nView < 0)
4843 return;
4844
4845 if (gImpl && aCommand == ".uno:ToggleOrientation")
4846 {
4847 ExecuteOrientationChange();
4848 return;
4849 }
4850
4851 // handle potential interaction
4852 if (gImpl && aCommand == ".uno:Save")
4853 {
4854 // Check if saving a PDF file
4855 OUString aMimeType = lcl_getCurrentDocumentMimeType(pDocument);
4856 if (pDocSh && pDocSh->IsModified() && aMimeType == "application/pdf")
4857 {
4858 // If we have a PDF file (for saving annotations for example), we need
4859 // to run save-as to the same file as the opened document. Plain save
4860 // doesn't work as the PDF is not a "native" format.
4861 uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
4862 OUString aURL = xStorable->getLocation();
4863 OString aURLUtf8 = OUStringToOString(aURL, RTL_TEXTENCODING_UTF8);
4864 bool bResult = doc_saveAs(pThis, aURLUtf8.getStr(), "pdf", nullptr);
4865
4866 // Send the result of save
4867 tools::JsonWriter aJson;
4868 aJson.put("commandName", pCommand);
4869 aJson.put("success", bResult);
4870 pDocument->mpCallbackFlushHandlers[nView]->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.finishAndGetAsOString());
4871 return;
4872 }
4873
4874
4875 rtl::Reference<LOKInteractionHandler> const pInteraction(
4876 new LOKInteractionHandler("save", gImpl, pDocument));
4877 uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction);
4878
4879 beans::PropertyValue aValue;
4880 aValue.Name = "InteractionHandler";
4881 aValue.Value <<= xInteraction;
4882 aPropertyValuesVector.push_back(aValue);
4883
4884 bool bDontSaveIfUnmodified = false;
4885 aPropertyValuesVector.erase(std::remove_if(aPropertyValuesVector.begin(),
4886 aPropertyValuesVector.end(),
4887 [&bDontSaveIfUnmodified](const beans::PropertyValue& aItem){
4888 if (aItem.Name == "DontSaveIfUnmodified")
4889 {
4890 bDontSaveIfUnmodified = aItem.Value.get<bool>();
4891 return true;
4892 }
4893 return false;
4894 }), aPropertyValuesVector.end());
4895
4896 // skip saving and tell the result via UNO_COMMAND_RESULT
4897 if (bDontSaveIfUnmodified && (!pDocSh || !pDocSh->IsModified()))
4898 {
4899 tools::JsonWriter aJson;
4900 aJson.put("commandName", pCommand);
4901 aJson.put("success", false);
4902 // Add the reason for not saving
4903 {
4904 auto resultNode = aJson.startNode("result");
4905 aJson.put("type", "string");
4906 aJson.put("value", "unmodified");
4907 }
4908 pDocument->mpCallbackFlushHandlers[nView]->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.finishAndGetAsOString());
4909 return;
4910 }
4911 }
4912 else if (gImpl && aCommand == ".uno:TransformDialog")
4913 {
4914 bool bNeedConversion = false;
4915 SfxViewShell* pViewShell = SfxViewShell::Current();
4916 LokChartHelper aChartHelper(pViewShell);
4917
4918 if (aChartHelper.GetWindow() )
4919 {
4920 bNeedConversion = true;
4921 }
4922 else if (const SdrView* pView = pViewShell->GetDrawView())
4923 {
4924 if (OutputDevice* pOutputDevice = pView->GetFirstOutputDevice())
4925 {
4926 bNeedConversion = (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM);
4927 }
4928 }
4929
4930 if (bNeedConversion)
4931 {
4932 sal_Int32 value;
4933 for (beans::PropertyValue& rPropValue: aPropertyValuesVector)
4934 {
4935 if (rPropValue.Name == "TransformPosX"
4936 || rPropValue.Name == "TransformPosY"
4937 || rPropValue.Name == "TransformWidth"
4938 || rPropValue.Name == "TransformHeight"
4939 || rPropValue.Name == "TransformRotationX"
4940 || rPropValue.Name == "TransformRotationY")
4941 {
4942 rPropValue.Value >>= value;
4944 rPropValue.Value <<= value;
4945 }
4946 }
4947 }
4948
4949 if (aChartHelper.GetWindow() && aPropertyValuesVector.size() > 0)
4950 {
4951 if (aPropertyValuesVector[0].Name != "Action")
4952 {
4953 tools::Rectangle aChartBB = aChartHelper.GetChartBoundingBox();
4954
4955 int nLeft = o3tl::convert(aChartBB.Left(), o3tl::Length::twip, o3tl::Length::mm100);
4957
4958 for (beans::PropertyValue& rPropValue: aPropertyValuesVector)
4959 {
4960 if (rPropValue.Name == "TransformPosX" || rPropValue.Name == "TransformRotationX")
4961 {
4962 auto const value = *o3tl::doAccess<sal_Int32>(rPropValue.Value);
4963 rPropValue.Value <<= value - nLeft;
4964 }
4965 else if (rPropValue.Name == "TransformPosY" || rPropValue.Name == "TransformRotationY")
4966 {
4967 auto const value = *o3tl::doAccess<sal_Int32>(rPropValue.Value);
4968 rPropValue.Value <<= value - nTop;
4969 }
4970 }
4971 }
4972 util::URL aCommandURL;
4973 aCommandURL.Path = "LOKTransform";
4974 css::uno::Reference<css::frame::XDispatch>& aChartDispatcher = aChartHelper.GetXDispatcher();
4975 aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector));
4976 return;
4977 }
4978 }
4979 else if (gImpl && aCommand == ".uno:LOKSidebarWriterPage")
4980 {
4981 setupSidebar(u"WriterPageDeck");
4982 return;
4983 }
4984 else if (gImpl && aCommand == ".uno:SidebarShow")
4985 {
4986 setupSidebar();
4987 return;
4988 }
4989 else if (gImpl && aCommand == ".uno:SidebarHide")
4990 {
4991 hideSidebar();
4992 return;
4993 }
4994
4995 bool bResult = false;
4996 LokChartHelper aChartHelper(SfxViewShell::Current());
4997
4998 if (aChartHelper.GetWindow() && aCommand != ".uno:Save" )
4999 {
5000 util::URL aCommandURL;
5001 aCommandURL.Path = aCommand.copy(5);
5002 css::uno::Reference<css::frame::XDispatch>& aChartDispatcher = aChartHelper.GetXDispatcher();
5003 aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector));
5004 return;
5005 }
5006 if (LokStarMathHelper aMathHelper(SfxViewShell::Current());
5007 aMathHelper.GetGraphicWindow() && aCommand != ".uno:Save")
5008 {
5009 aMathHelper.Dispatch(aCommand, comphelper::containerToSequence(aPropertyValuesVector));
5010 return;
5011 }
5012 if (bNotifyWhenFinished && pDocument->mpCallbackFlushHandlers.count(nView))
5013 {
5014 bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector),
5015 new DispatchResultListener(pCommand, pDocument->mpCallbackFlushHandlers[nView]));
5016 }
5017 else
5018 bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector));
5019
5020 if (!bResult)
5021 {
5022 SetLastExceptionMsg("Failed to dispatch " + aCommand);
5023 }
5024}
5025
5026static void doc_postMouseEvent(LibreOfficeKitDocument* pThis, int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
5027{
5028 comphelper::ProfileZone aZone("doc_postMouseEvent");
5029
5030 SolarMutexGuard aGuard;
5032
5033 ITiledRenderable* pDoc = getTiledRenderable(pThis);
5034 if (!pDoc)
5035 {
5036 SetLastExceptionMsg("Document doesn't support tiled rendering");
5037 return;
5038 }
5039 try
5040 {
5041 pDoc->postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier);
5042 }
5043 catch (const uno::Exception& exception)
5044 {
5045 SetLastExceptionMsg(exception.Message);
5046 SAL_INFO("lok", "Failed to postMouseEvent " << exception.Message);
5047 }
5048}
5049
5050static void doc_postWindowMouseEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
5051{
5052 comphelper::ProfileZone aZone("doc_postWindowMouseEvent");
5053
5054 SolarMutexGuard aGuard;
5056
5057 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
5058 if (!pWindow)
5059 {
5060 SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
5061 return;
5062 }
5063
5064 const Point aPos(nX, nY);
5065
5066 MouseEvent aEvent(aPos, nCount, MouseEventModifiers::SIMPLECLICK, nButtons, nModifier);
5067
5068 vcl::EnableDialogInput(pWindow);
5069
5070 switch (nType)
5071 {
5072 case LOK_MOUSEEVENT_MOUSEBUTTONDOWN:
5073 Application::PostMouseEvent(VclEventId::WindowMouseButtonDown, pWindow, &aEvent);
5074 break;
5075 case LOK_MOUSEEVENT_MOUSEBUTTONUP:
5076 Application::PostMouseEvent(VclEventId::WindowMouseButtonUp, pWindow, &aEvent);
5077 break;
5078 case LOK_MOUSEEVENT_MOUSEMOVE:
5079 Application::PostMouseEvent(VclEventId::WindowMouseMove, pWindow, &aEvent);
5080 break;
5081 default:
5082 assert(false);
5083 break;
5084 }
5085}
5086
5087static void doc_postWindowGestureEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, const char* pType, int nX, int nY, int nOffset)
5088{
5089 comphelper::ProfileZone aZone("doc_postWindowGestureEvent");
5090
5091 SolarMutexGuard aGuard;
5093
5094 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
5095 if (!pWindow)
5096 {
5097 SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
5098 return;
5099 }
5100
5101 OString aType(pType);
5102 GestureEventPanType eEventType = GestureEventPanType::Update;
5103
5104 if (aType == "panBegin")
5105 eEventType = GestureEventPanType::Begin;
5106 else if (aType == "panEnd")
5107 eEventType = GestureEventPanType::End;
5108
5110 sal_Int32(nX),
5111 sal_Int32(nY),
5112 eEventType,
5113 sal_Int32(nOffset),
5114 PanningOrientation::Vertical,
5115 };
5116
5117 vcl::EnableDialogInput(pWindow);
5118
5119 Application::PostGestureEvent(VclEventId::WindowGestureEvent, pWindow, &aEvent);
5120}
5121
5122static void doc_setTextSelection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY)
5123{
5124 comphelper::ProfileZone aZone("doc_setTextSelection");
5125
5126 SolarMutexGuard aGuard;
5128
5129 ITiledRenderable* pDoc = getTiledRenderable(pThis);
5130 if (!pDoc)
5131 {
5132 SetLastExceptionMsg("Document doesn't support tiled rendering");
5133 return;
5134 }
5135
5136 pDoc->setTextSelection(nType, nX, nY);
5137}
5138
5139static void doc_setWindowTextSelection(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, bool swap, int nX, int nY)
5140{
5141 comphelper::ProfileZone aZone("doc_setWindowTextSelection");
5142
5143 SolarMutexGuard aGuard;
5145
5146 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
5147 if (!pWindow)
5148 {
5149 SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
5150 return;
5151 }
5152
5153
5154 Size aOffset(pWindow->GetOutOffXPixel(), pWindow->GetOutOffYPixel());
5155 Point aCursorPos(nX, nY);
5156 aCursorPos.Move(aOffset);
5157 sal_uInt16 nModifier = swap ? KEY_MOD1 + KEY_MOD2 : KEY_SHIFT;
5158
5159 MouseEvent aCursorEvent(aCursorPos, 1, MouseEventModifiers::SIMPLECLICK, 0, nModifier);
5160 Application::PostMouseEvent(VclEventId::WindowMouseButtonDown, pWindow, &aCursorEvent);
5161 Application::PostMouseEvent(VclEventId::WindowMouseButtonUp, pWindow, &aCursorEvent);
5162}
5163
5164static bool getFromTransferable(
5165 const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
5166 const OString &aInMimeType, OString &aRet);
5167
5169 const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
5170 const OString &aMimeType, OString &aRet)
5171{
5172 if (!getFromTransferable(xTransferable, aMimeType, aRet))
5173 return false;
5174
5175 // Encode in base64.
5176 auto aSeq = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()),
5177 aRet.getLength());
5178 OStringBuffer aBase64Data;
5179 comphelper::Base64::encode(aBase64Data, aSeq);
5180
5181 // Embed in HTML.
5182 aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
5183 "<html><head>"
5184 "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta "
5185 "name=\"generator\" content=\""
5186 + getGenerator().toUtf8()
5187 + "\"/>"
5188 "</head><body><img src=\"data:" + aMimeType + ";base64,"
5189 + aBase64Data + "\"/></body></html>";
5190
5191 return true;
5192}
5193
5195 const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
5196 const OString &aMimeType, OString &aRet)
5197{
5198 if (!getFromTransferable(xTransferable, aMimeType, aRet))
5199 return false;
5200
5201 // Embed in HTML - FIXME: needs some escaping.
5202 aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
5203 "<html><head>"
5204 "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta "
5205 "name=\"generator\" content=\""
5206 + getGenerator().toUtf8()
5207 + "\"/></head><body><pre>" + aRet + "</pre></body></html>";
5208
5209 return true;
5210}
5211
5213 const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
5214 const OString &aInMimeType, OString &aRet)
5215{
5216 OString aMimeType(aInMimeType);
5217
5218 // Take care of UTF-8 text here.
5219 bool bConvert = false;
5220 sal_Int32 nIndex = 0;
5221 if (o3tl::getToken(aMimeType, 0, ';', nIndex) == "text/plain")
5222 {
5223 if (o3tl::getToken(aMimeType, 0, ';', nIndex) == "charset=utf-8")
5224 {
5225 aMimeType = "text/plain;charset=utf-16";
5226 bConvert = true;
5227 }
5228 }
5229
5230 datatransfer::DataFlavor aFlavor;
5231 aFlavor.MimeType = OUString::fromUtf8(aMimeType);
5232 if (aMimeType == "text/plain;charset=utf-16")
5233 aFlavor.DataType = cppu::UnoType<OUString>::get();
5234 else
5235 aFlavor.DataType = cppu::UnoType< uno::Sequence<sal_Int8> >::get();
5236
5237 if (!xTransferable->isDataFlavorSupported(aFlavor))
5238 {
5239 // Try harder for HTML it is our copy/paste meta-file format
5240 if (aInMimeType == "text/html")
5241 {
5242 // Desperate measures - convert text to HTML instead.
5243 if (encodeTextAsHTML(xTransferable, "text/plain;charset=utf-8", aRet))
5244 return true;
5245 // If html is not supported, might be a graphic-selection,
5246 if (encodeImageAsHTML(xTransferable, "image/png", aRet))
5247 return true;
5248 }
5249
5250 SetLastExceptionMsg("Flavor " + aFlavor.MimeType + " is not supported");
5251 return false;
5252 }
5253
5254 uno::Any aAny;
5255 try
5256 {
5257 aAny = xTransferable->getTransferData(aFlavor);
5258 }
5259 catch (const css::datatransfer::UnsupportedFlavorException& e)
5260 {
5261 SetLastExceptionMsg("Unsupported flavor " + aFlavor.MimeType + " exception " + e.Message);
5262 return false;
5263 }
5264 catch (const css::uno::Exception& e)
5265 {
5266 SetLastExceptionMsg("Exception getting " + aFlavor.MimeType + " exception " + e.Message);
5267 return false;
5268 }
5269
5270 if (aFlavor.DataType == cppu::UnoType<OUString>::get())
5271 {
5272 OUString aString;
5273 aAny >>= aString;
5274 if (bConvert)
5275 aRet = OUStringToOString(aString, RTL_TEXTENCODING_UTF8);
5276 else
5277 aRet = OString(reinterpret_cast<const char *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode));
5278 }
5279 else
5280 {
5281 uno::Sequence<sal_Int8> aSequence;
5282 aAny >>= aSequence;
5283 aRet = OString(reinterpret_cast<const char*>(aSequence.getConstArray()), aSequence.getLength());
5284 }
5285
5286 return true;
5287}
5288
5289static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pUsedMimeType)
5290{
5291 comphelper::ProfileZone aZone("doc_getTextSelection");
5292
5293 SolarMutexGuard aGuard;
5295
5296 ITiledRenderable* pDoc = getTiledRenderable(pThis);
5297 if (!pDoc)
5298 {
5299 SetLastExceptionMsg("Document doe