Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / desktop / source / lib / init.cxx
blob0cc08257f6572ec593554f1c5df77cca75c5d081
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.
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 */
10 #include <sfx2/lokhelper.hxx>
11 #include <config_buildconfig.h>
12 #include <config_features.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdlib.h>
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
29 #undef HAVE_MALLOC_TRIM
31 #ifdef LINUX
32 #include <fcntl.h>
33 #if defined __GLIBC__
34 # include <malloc.h>
35 # define HAVE_MALLOC_TRIM
36 #endif
37 #endif
39 #ifdef ANDROID
40 #include <osl/detail/android-bootstrap.h>
41 #endif
43 #ifdef EMSCRIPTEN
44 #include <osl/detail/emscripten-bootstrap.h>
45 #endif
47 #include <algorithm>
48 #include <memory>
49 #include <iostream>
50 #include <string_view>
52 #include <boost/property_tree/json_parser.hpp>
53 #include <boost/algorithm/string.hpp>
55 #include <LibreOfficeKit/LibreOfficeKit.h>
56 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
58 #include <sal/log.hxx>
59 #include <utility>
60 #include <vcl/errinf.hxx>
61 #include <vcl/lok.hxx>
62 #include <o3tl/any.hxx>
63 #include <o3tl/unit_conversion.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>
73 #include <cppuhelper/bootstrap.hxx>
74 #include <comphelper/base64.hxx>
75 #include <comphelper/dispatchcommand.hxx>
76 #include <comphelper/lok.hxx>
77 #include <comphelper/processfactory.hxx>
78 #include <comphelper/string.hxx>
79 #include <comphelper/profilezone.hxx>
80 #include <comphelper/propertysequence.hxx>
81 #include <comphelper/propertyvalue.hxx>
82 #include <comphelper/scopeguard.hxx>
83 #include <comphelper/threadpool.hxx>
84 #include <comphelper/servicehelper.hxx>
85 #include <comphelper/sequenceashashmap.hxx>
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>
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>
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>
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>
134 #include <sfx2/lokcomponenthelpers.hxx>
135 #include <sfx2/DocumentSigner.hxx>
136 #include <sfx2/sidebar/SidebarDockingWindow.hxx>
137 #include <sfx2/sidebar/SidebarController.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>
145 #include <vcl/GestureEventPan.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>
158 #include <vcl/ITiledRenderable.hxx>
159 #include <vcl/dialoghelper.hxx>
160 #ifdef _WIN32
161 #include <vcl/BitmapReadAccess.hxx>
162 #endif
163 #include <unicode/uchar.h>
164 #include <unotools/securityoptions.hxx>
165 #include <unotools/confignode.hxx>
166 #include <unotools/syslocaleoptions.hxx>
167 #include <unotools/mediadescriptor.hxx>
168 #include <unotools/pathoptions.hxx>
169 #include <unotools/tempfile.hxx>
170 #include <unotools/streamwrap.hxx>
171 #include <osl/module.hxx>
172 #include <comphelper/sequence.hxx>
173 #include <sfx2/sfxbasemodel.hxx>
174 #include <svl/undo.hxx>
175 #include <unotools/datetime.hxx>
176 #include <i18nlangtag/mslangid.hxx>
177 #include <i18nlangtag/languagetag.hxx>
178 #include <vcl/abstdlg.hxx>
179 #include <comphelper/diagnose_ex.hxx>
180 #include <vcl/uitest/uiobject.hxx>
181 #include <vcl/jsdialog/executor.hxx>
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>
191 #include <app.hxx>
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>
199 #include "lokinteractionhandler.hxx"
200 #include "lokclipboard.hxx"
201 #include <officecfg/Office/Common.hxx>
202 #include <officecfg/Office/Impress.hxx>
203 #include <officecfg/Office/Linguistic.hxx>
204 #include <unotools/optionsdlg.hxx>
205 #include <svl/ctloptions.hxx>
206 #include <svtools/accessibilityoptions.hxx>
207 #include <svtools/colorcfg.hxx>
208 #include <svtools/miscopt.hxx>
209 #include <svtools/slidesorterbaropt.hxx>
210 #include <unotools/cmdoptions.hxx>
211 #include <unotools/compatibility.hxx>
212 #include <unotools/fltrcfg.hxx>
213 #include <unotools/lingucfg.hxx>
214 #include <unotools/moduleoptions.hxx>
215 #include <unotools/searchopt.hxx>
216 #include <unotools/useroptions.hxx>
217 #include <unotools/viewoptions.hxx>
218 #include <vcl/settings.hxx>
220 #include <officecfg/Setup.hxx>
221 #include <com/sun/star/ui/XAcceleratorConfiguration.hpp>
222 #include <svtools/acceleratorexecute.hxx>
224 using namespace css;
225 using namespace vcl;
226 using namespace desktop;
227 using namespace utl;
229 static LibLibreOffice_Impl *gImpl = nullptr;
230 static bool lok_preinit_2_called = false;
231 static std::weak_ptr< LibreOfficeKitClass > gOfficeClass;
232 static std::weak_ptr< LibreOfficeKitDocumentClass > gDocumentClass;
234 static void SetLastExceptionMsg(const OUString& s = OUString())
236 SAL_WARN_IF(!s.isEmpty(), "lok", "lok exception '" + s + "'");
237 if (gImpl)
238 gImpl->maLastExceptionMsg = s;
241 namespace {
243 struct ExtensionMap
245 const char *extn;
246 const char *filterName;
249 class TraceEventDumper : public AutoTimer
251 static const int dumpTimeoutMS = 5000;
253 public:
254 TraceEventDumper() : AutoTimer( "Trace Event dumper" )
256 SetTimeout(dumpTimeoutMS);
257 Start();
260 virtual void Invoke() override
262 flushRecordings();
265 static void flushRecordings()
267 const css::uno::Sequence<OUString> aEvents =
268 comphelper::TraceEvent::getRecordingAndClear();
269 OStringBuffer aOutput;
270 for (const auto &s : aEvents)
272 aOutput.append(OUStringToOString(s, RTL_TEXTENCODING_UTF8)
273 + "\n");
275 if (aOutput.getLength() > 0)
277 OString aChunk = aOutput.makeStringAndClear();
278 if (gImpl && gImpl->mpCallback)
279 gImpl->mpCallback(LOK_CALLBACK_PROFILE_FRAME, aChunk.getStr(), gImpl->mpCallbackData);
284 } // unnamed namespace
286 static TraceEventDumper *traceEventDumper = nullptr;
288 const ExtensionMap aWriterExtensionMap[] =
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 }
307 const ExtensionMap aCalcExtensionMap[] =
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 }
323 const ExtensionMap aImpressExtensionMap[] =
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 }
343 const ExtensionMap aDrawExtensionMap[] =
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 }
355 static OUString getUString(const char* pString)
357 if (pString == nullptr)
358 return OUString();
360 std::string_view sString(pString, strlen(pString));
361 return OStringToOUString(sString, RTL_TEXTENCODING_UTF8);
364 // Tolerate embedded \0s etc.
365 static char *convertOString(const OString &rStr)
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;
373 static char *convertOUString(std::u16string_view aStr)
375 return convertOString(OUStringToOString(aStr, RTL_TEXTENCODING_UTF8));
378 /// Try to convert a relative URL to an absolute one, unless it already looks like a URL.
379 static OUString getAbsoluteURL(const char* pURL)
381 OUString aURL(getUString(pURL));
382 if (aURL.isEmpty())
383 return aURL;
385 // convert relative paths to absolute ones
386 OUString aWorkingDir;
387 osl_getProcessWorkingDir(&aWorkingDir.pData);
388 if (!aWorkingDir.endsWith("/"))
389 aWorkingDir += "/";
393 return rtl::Uri::convertRelToAbs(aWorkingDir, aURL);
395 catch (const rtl::MalformedUriException &)
399 return OUString();
402 std::vector<beans::PropertyValue> desktop::jsonToPropertyValuesVector(const char* pJSON)
404 std::vector<beans::PropertyValue> aArguments;
405 if (pJSON && pJSON[0] != '\0')
407 aArguments = comphelper::JsonToPropertyValues(pJSON);
409 return aArguments;
412 static bool extractLinks(const uno::Reference< container::XNameAccess >& xLinks, bool subcontent, OUStringBuffer& jsonText)
414 const uno::Sequence< OUString > aNames( xLinks->getElementNames() );
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++ )
423 uno::Any aAny;
424 OUString aLink( *pNames++ );
426 bool bError = false;
429 aAny = xLinks->getByName( aLink );
431 catch(const uno::Exception&)
433 // if the name of the target was invalid (like empty headings)
434 // no object can be provided
435 bError = true;
437 if(bError)
438 continue;
440 uno::Reference< beans::XPropertySet > xTarget;
441 if( aAny >>= xTarget )
445 // get name to display
446 aAny = xTarget->getPropertyValue( aProp_LinkDisplayName );
447 OUString aDisplayName;
448 aAny >>= aDisplayName;
449 OUString aStrDisplayname ( aDisplayName );
451 if (subcontent)
453 jsonText.append("\"");
454 jsonText.append(aStrDisplayname);
455 jsonText.append("\": \"");
456 jsonText.append(aLink);
457 jsonText.append("\"");
458 if (i < nLinks-1)
460 jsonText.append(", ");
463 else
465 uno::Reference< lang::XServiceInfo > xSI( xTarget, uno::UNO_QUERY );
466 bIsTarget = xSI->supportsService( aProp_LinkTarget );
467 if (i != 0)
469 if (!bIsTarget)
470 jsonText.append("}");
471 if (i < nLinks)
473 jsonText.append(", ");
476 jsonText.append("\"");
477 jsonText.append(aStrDisplayname);
478 jsonText.append("\": ");
480 if (bIsTarget)
482 jsonText.append("true");
483 continue;
485 jsonText.append("{");
488 uno::Reference< document::XLinkTargetSupplier > xLTS( xTarget, uno::UNO_QUERY );
489 if( xLTS.is() )
491 extractLinks(xLTS->getLinks(), true, jsonText);
494 catch(...)
496 SAL_WARN("lok", "extractLinks: Exception");
500 return bIsTarget;
503 static void unoAnyToJson(tools::JsonWriter& rJson, std::string_view pNodeName, const uno::Any& anyItem)
505 auto aNode = rJson.startNode(pNodeName);
506 OUString aType = anyItem.getValueTypeName();
507 rJson.put("type", aType);
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")
517 uno::Sequence<uno::Any> aSeq;
518 if (anyItem >>= aSeq)
520 auto valueNode = rJson.startNode("value");
522 for (auto i = 0; i < aSeq.getLength(); ++i)
524 unoAnyToJson(rJson, OString::number(i), aSeq[i]);
530 static int lcl_getViewId(std::string_view payload);
532 namespace desktop {
534 RectangleAndPart RectangleAndPart::Create(const OString& rPayload)
536 RectangleAndPart aRet;
537 if (rPayload.startsWith("EMPTY")) // payload starts with "EMPTY"
539 aRet.m_aRectangle = tools::Rectangle(0, 0, SfxLokHelper::MaxTwips, SfxLokHelper::MaxTwips);
540 if (comphelper::LibreOfficeKit::isPartInInvalidation())
542 int nSeparatorPos = rPayload.indexOf(',', 6);
543 bool bHasMode = nSeparatorPos > 0;
544 if (bHasMode)
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));
550 else
552 aRet.m_nPart = o3tl::toInt32(rPayload.subView(6));
553 aRet.m_nMode = 0;
557 return aRet;
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;
581 if (comphelper::LibreOfficeKit::isPartInInvalidation())
583 while( *pos != ',' )
584 ++pos;
585 ++pos;
586 assert(pos < end);
587 nPart = rtl_str_toInt64_WithLength(pos, 10, end - pos);
589 while( *pos && *pos != ',' )
590 ++pos;
591 if (*pos)
593 ++pos;
594 assert(pos < end);
595 nMode = rtl_str_toInt64_WithLength(pos, 10, end - pos);
599 aRet.m_aRectangle = SanitizedRectangle(nLeft, nTop, nWidth, nHeight);
600 aRet.m_nPart = nPart;
601 aRet.m_nMode = nMode;
602 return aRet;
605 tools::Rectangle RectangleAndPart::SanitizedRectangle(tools::Long nLeft, tools::Long nTop, tools::Long nWidth, tools::Long nHeight)
607 if (nWidth <= 0 || nHeight <= 0)
608 return tools::Rectangle();
610 // The top-left corner starts at (0, 0).
611 // Anything negative is invalid.
612 if (nLeft < 0)
614 nWidth += nLeft;
615 nLeft = 0;
618 if (nTop < 0)
620 nHeight += nTop;
621 nTop = 0;
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();
630 tools::Rectangle RectangleAndPart::SanitizedRectangle(const tools::Rectangle& rect)
632 return SanitizedRectangle(rect.Left(), rect.Top(), rect.getOpenWidth(), rect.getOpenHeight());
635 const OString& CallbackFlushHandler::CallbackData::getPayload() const
637 if(PayloadString.isEmpty())
639 // Do to-string conversion on demand, as many calls will get dropped without
640 // needing the string.
641 if(PayloadObject.which() == 1)
642 PayloadString = getRectangleAndPart().toString();
644 return PayloadString;
647 void CallbackFlushHandler::CallbackData::updateRectangleAndPart(const RectangleAndPart& rRectAndPart)
649 PayloadObject = rRectAndPart;
650 PayloadString.clear(); // will be set on demand if needed
653 const RectangleAndPart& CallbackFlushHandler::CallbackData::getRectangleAndPart() const
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);
662 boost::property_tree::ptree& CallbackFlushHandler::CallbackData::setJson(const std::string& payload)
664 boost::property_tree::ptree aTree;
665 std::stringstream aStream(payload);
666 boost::property_tree::read_json(aStream, aTree);
668 // Let boost normalize the payload so it always matches the cache.
669 setJson(aTree);
671 // Return reference to the cached object.
672 return boost::get<boost::property_tree::ptree>(PayloadObject);
675 void CallbackFlushHandler::CallbackData::setJson(const boost::property_tree::ptree& rTree)
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()));
682 PayloadObject = rTree;
685 const boost::property_tree::ptree& CallbackFlushHandler::CallbackData::getJson() const
687 assert(PayloadObject.which() == 2);
688 return boost::get<boost::property_tree::ptree>(PayloadObject);
691 int CallbackFlushHandler::CallbackData::getViewId() const
693 if (isCached())
695 assert(PayloadObject.which() == 3);
696 return boost::get<int>(PayloadObject);
698 return lcl_getViewId(getPayload());
701 bool CallbackFlushHandler::CallbackData::validate() const
703 switch (PayloadObject.which())
705 // Not cached.
706 case 0:
707 return true;
709 // RectangleAndPart.
710 case 1:
711 return getRectangleAndPart().toString().getStr() == getPayload();
713 // Json.
714 case 2:
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);
722 // View id.
723 case 3:
724 return getViewId() == lcl_getViewId( getPayload());
726 default:
727 assert(!"Unknown variant type; please add an entry to validate.");
730 return false;
733 } // namespace desktop
735 static bool lcl_isViewCallbackType(const int type)
737 switch (type)
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;
746 default:
747 return false;
751 static bool isUpdatedType(int type)
753 switch (type)
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;
764 static bool isUpdatedTypePerViewId(int type)
766 switch (type)
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;
777 static int lcl_getViewId(std::string_view payload)
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;
785 size_t numberPos = payload.find(":", viewIdPos + 6);
786 if (numberPos == std::string::npos)
787 return 0;
789 for (++numberPos; numberPos < payload.length(); ++numberPos)
791 if (payload[numberPos] == ',' || payload[numberPos] == '}' || (payload[numberPos] >= '0' && payload[numberPos] <= '9'))
792 break;
795 if (numberPos < payload.length() && payload[numberPos] >= '0' && payload[numberPos] <= '9')
796 return o3tl::toInt32(payload.substr(numberPos));
798 return 0;
801 namespace {
803 std::string extractCertificate(const std::string & certificate)
805 const std::string header("-----BEGIN CERTIFICATE-----");
806 const std::string footer("-----END CERTIFICATE-----");
808 std::string result;
810 size_t pos1 = certificate.find(header);
811 if (pos1 == std::string::npos)
812 return result;
814 size_t pos2 = certificate.find(footer, pos1 + 1);
815 if (pos2 == std::string::npos)
816 return result;
818 pos1 = pos1 + header.length();
819 pos2 = pos2 - pos1;
821 return certificate.substr(pos1, pos2);
824 std::string extractPrivateKey(const std::string & privateKey)
826 const std::string header("-----BEGIN PRIVATE KEY-----");
827 const std::string footer("-----END PRIVATE KEY-----");
829 std::string result;
831 size_t pos1 = privateKey.find(header);
832 if (pos1 == std::string::npos)
833 return result;
835 size_t pos2 = privateKey.find(footer, pos1 + 1);
836 if (pos2 == std::string::npos)
837 return result;
839 pos1 = pos1 + header.length();
840 pos2 = pos2 - pos1;
842 return privateKey.substr(pos1, pos2);
845 OUString lcl_getCurrentDocumentMimeType(const LibLODocument_Impl* pDocument)
847 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
848 if (!pBaseModel)
849 return "";
851 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
852 if (!pObjectShell)
853 return "";
855 SfxMedium* pMedium = pObjectShell->GetMedium();
856 if (!pMedium)
857 return "";
859 auto pFilter = pMedium->GetFilter();
860 if (!pFilter)
861 return "";
863 return pFilter->GetMimeType();
866 // Gets an undo manager to enter and exit undo context. Needed by ToggleOrientation
867 css::uno::Reference< css::document::XUndoManager > getUndoManager( const css::uno::Reference< css::frame::XFrame >& rxFrame )
869 const css::uno::Reference< css::frame::XController >& xController = rxFrame->getController();
870 if ( xController.is() )
872 const css::uno::Reference< css::frame::XModel >& xModel = xController->getModel();
873 if ( xModel.is() )
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 );
880 return css::uno::Reference< css::document::XUndoManager > ();
883 // Adjusts page margins for Writer doc. Needed by ToggleOrientation
884 void ExecuteMarginLRChange(
885 const tools::Long nPageLeftMargin,
886 const tools::Long nPageRightMargin,
887 SvxLongLRSpaceItem* pPageLRMarginItem)
889 pPageLRMarginItem->SetLeft( nPageLeftMargin );
890 pPageLRMarginItem->SetRight( nPageRightMargin );
891 SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_LRSPACE,
892 SfxCallMode::RECORD, { pPageLRMarginItem });
895 // Adjusts page margins for Writer doc. Needed by ToggleOrientation
896 void ExecuteMarginULChange(
897 const tools::Long nPageTopMargin,
898 const tools::Long nPageBottomMargin,
899 SvxLongULSpaceItem* pPageULMarginItem)
901 pPageULMarginItem->SetUpper( nPageTopMargin );
902 pPageULMarginItem->SetLower( nPageBottomMargin );
903 SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_ULSPACE,
904 SfxCallMode::RECORD, { pPageULMarginItem });
907 // Main function which toggles page orientation of the Writer doc. Needed by ToggleOrientation
908 void ExecuteOrientationChange()
910 SfxViewFrame* pViewFrm = SfxViewFrame::Current();
911 if (!pViewFrm)
912 return;
914 std::unique_ptr<SvxPageItem> pPageItem(new SvxPageItem(SID_ATTR_PAGE));
916 // 1mm in twips rounded
917 // This should be in sync with MINBODY in sw/source/uibase/sidebar/PageMarginControl.hxx
918 constexpr tools::Long MINBODY = o3tl::toTwips(1, o3tl::Length::mm);
920 css::uno::Reference< css::document::XUndoManager > mxUndoManager(
921 getUndoManager( pViewFrm->GetFrame().GetFrameInterface() ) );
923 if ( mxUndoManager.is() )
924 mxUndoManager->enterUndoContext( "" );
927 const SvxSizeItem* pSizeItem;
928 pViewFrm->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_SIZE, pSizeItem);
929 std::unique_ptr<SvxSizeItem> pPageSizeItem(pSizeItem->Clone());
931 const SvxLongLRSpaceItem* pLRSpaceItem;
932 pViewFrm->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_LRSPACE, pLRSpaceItem);
933 std::unique_ptr<SvxLongLRSpaceItem> pPageLRMarginItem(pLRSpaceItem->Clone());
935 const SvxLongULSpaceItem* pULSpaceItem;
936 pViewFrm->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_ULSPACE, pULSpaceItem);
937 std::unique_ptr<SvxLongULSpaceItem> pPageULMarginItem(pULSpaceItem->Clone());
940 bool bIsLandscape = false;
941 if ( pPageSizeItem->GetSize().Width() > pPageSizeItem->GetSize().Height())
942 bIsLandscape = true;
944 // toggle page orientation
945 pPageItem->SetLandscape(!bIsLandscape);
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));
954 // apply changed attributes
955 if (SfxViewShell::Current())
957 SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_SIZE,
958 SfxCallMode::RECORD, { pPageSizeItem.get(), pPageItem.get() });
963 // check, if margin values still fit to the changed page size.
964 // if not, adjust margin values
966 const tools::Long nML = pPageLRMarginItem->GetLeft();
967 const tools::Long nMR = pPageLRMarginItem->GetRight();
968 const tools::Long nTmpPW = nML + nMR + MINBODY;
970 const tools::Long nPW = pPageSizeItem->GetSize().Width();
972 if ( nTmpPW > nPW )
974 if ( nML <= nMR )
976 ExecuteMarginLRChange( pPageLRMarginItem->GetLeft(), nMR - (nTmpPW - nPW ), pPageLRMarginItem.get() );
978 else
980 ExecuteMarginLRChange( nML - (nTmpPW - nPW ), pPageLRMarginItem->GetRight(), pPageLRMarginItem.get() );
984 const tools::Long nMT = pPageULMarginItem->GetUpper();
985 const tools::Long nMB = pPageULMarginItem->GetLower();
986 const tools::Long nTmpPH = nMT + nMB + MINBODY;
988 const tools::Long nPH = pPageSizeItem->GetSize().Height();
990 if ( nTmpPH > nPH )
992 if ( nMT <= nMB )
994 ExecuteMarginULChange( pPageULMarginItem->GetUpper(), nMB - ( nTmpPH - nPH ), pPageULMarginItem.get() );
996 else
998 ExecuteMarginULChange( nMT - ( nTmpPH - nPH ), pPageULMarginItem->GetLower(), pPageULMarginItem.get() );
1003 if ( mxUndoManager.is() )
1004 mxUndoManager->leaveUndoContext();
1007 void setupSidebar(std::u16string_view sidebarDeckId = u"")
1009 SfxViewShell* pViewShell = SfxViewShell::Current();
1010 SfxViewFrame* pViewFrame = pViewShell ? &pViewShell->GetViewFrame() : nullptr;
1011 if (pViewFrame)
1013 if (!pViewFrame->GetChildWindow(SID_SIDEBAR))
1014 pViewFrame->SetChildWindow(SID_SIDEBAR, false /* create it */, true /* focus */);
1016 pViewFrame->ShowChildWindow(SID_SIDEBAR, true);
1018 // Force synchronous population of panels
1019 SfxChildWindow *pChild = pViewFrame->GetChildWindow(SID_SIDEBAR);
1020 if (!pChild)
1021 return;
1023 auto pDockingWin = dynamic_cast<sfx2::sidebar::SidebarDockingWindow *>(pChild->GetWindow());
1024 if (!pDockingWin)
1025 return;
1027 pViewFrame->ShowChildWindow( SID_SIDEBAR );
1029 const rtl::Reference<sfx2::sidebar::SidebarController>& xController
1030 = pDockingWin->GetOrCreateSidebarController();
1032 xController->FadeIn();
1033 xController->RequestOpenDeck();
1035 if (!sidebarDeckId.empty())
1037 xController->SwitchToDeck(sidebarDeckId);
1039 else
1041 xController->SwitchToDefaultDeck();
1044 pDockingWin->SyncUpdate();
1046 else
1047 SetLastExceptionMsg("No view shell or sidebar");
1050 void hideSidebar()
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");
1060 } // end anonymous namespace
1062 // Could be anonymous in principle, but for the unit testing purposes, we
1063 // declare it in init.hxx.
1064 OUString desktop::extractParameter(OUString& rOptions, std::u16string_view rName)
1066 OUString aValue;
1068 OUString aNameEquals(OUString::Concat(rName) + "=");
1069 OUString aCommaNameEquals(OUString::Concat(",") + rName + "=");
1071 int nIndex = -1;
1072 if (rOptions.startsWith(aNameEquals))
1074 size_t nLen = aNameEquals.getLength();
1075 int nComma = rOptions.indexOf(",", nLen);
1076 if (nComma >= 0)
1078 aValue = rOptions.copy(nLen, nComma - nLen);
1079 rOptions = rOptions.copy(nComma + 1);
1081 else
1083 aValue = rOptions.copy(nLen);
1084 rOptions.clear();
1087 else if ((nIndex = rOptions.indexOf(aCommaNameEquals)) >= 0)
1089 size_t nLen = aCommaNameEquals.getLength();
1090 int nComma = rOptions.indexOf(",", nIndex + nLen);
1091 if (nComma >= 0)
1093 aValue = rOptions.copy(nIndex + nLen, nComma - nIndex - nLen);
1094 rOptions = OUString::Concat(rOptions.subView(0, nIndex)) + rOptions.subView(nComma);
1096 else
1098 aValue = rOptions.copy(nIndex + nLen);
1099 rOptions = rOptions.copy(0, nIndex);
1103 return aValue;
1106 extern "C"
1109 static void doc_destroy(LibreOfficeKitDocument* pThis);
1110 static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* pUrl, const char* pFormat, const char* pFilterOptions);
1111 static int doc_getDocumentType(LibreOfficeKitDocument* pThis);
1112 static int doc_getParts(LibreOfficeKitDocument* pThis);
1113 static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis);
1114 static int doc_getPart(LibreOfficeKitDocument* pThis);
1115 static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart);
1116 static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect);
1117 static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate);
1118 static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart);
1119 static void doc_setPartMode(LibreOfficeKitDocument* pThis, int nPartMode);
1120 static int doc_getEditMode(LibreOfficeKitDocument* pThis);
1121 static 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);
1126 static 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);
1133 static int doc_getTileMode(LibreOfficeKitDocument* pThis);
1134 static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
1135 long* pWidth,
1136 long* pHeight);
1137 static void doc_getDataArea(LibreOfficeKitDocument* pThis,
1138 long nTab,
1139 long* pCol,
1140 long* pRow);
1141 static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
1142 const char* pArguments);
1144 static void doc_registerCallback(LibreOfficeKitDocument* pThis,
1145 LibreOfficeKitCallback pCallback,
1146 void* pData);
1147 static void doc_postKeyEvent(LibreOfficeKitDocument* pThis,
1148 int nType,
1149 int nCharCode,
1150 int nKeyCode);
1151 static void doc_setBlockedCommandList(LibreOfficeKitDocument* pThis,
1152 int nViewId,
1153 const char* blockedCommandList);
1155 static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis,
1156 unsigned nWindowId,
1157 int nType,
1158 const char* pText);
1159 static void doc_removeTextContext(LibreOfficeKitDocument* pThis,
1160 unsigned nLOKWindowId,
1161 int nCharBefore,
1162 int nCharAfter);
1163 static void doc_sendDialogEvent(LibreOfficeKitDocument* pThis,
1164 unsigned long long int nLOKWindowId,
1165 const char* pArguments);
1166 static void doc_postWindowKeyEvent(LibreOfficeKitDocument* pThis,
1167 unsigned nLOKWindowId,
1168 int nType,
1169 int nCharCode,
1170 int nKeyCode);
1171 static void doc_postMouseEvent (LibreOfficeKitDocument* pThis,
1172 int nType,
1173 int nX,
1174 int nY,
1175 int nCount,
1176 int nButtons,
1177 int nModifier);
1178 static 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);
1186 static void doc_postWindowGestureEvent(LibreOfficeKitDocument* pThis,
1187 unsigned nLOKWindowId,
1188 const char* pType,
1189 int nX,
1190 int nY,
1191 int nOffset);
1192 static void doc_postUnoCommand(LibreOfficeKitDocument* pThis,
1193 const char* pCommand,
1194 const char* pArguments,
1195 bool bNotifyWhenFinished);
1196 static void doc_setWindowTextSelection(LibreOfficeKitDocument* pThis,
1197 unsigned nLOKWindowId,
1198 bool swap,
1199 int nX,
1200 int nY);
1201 static void doc_setTextSelection (LibreOfficeKitDocument* pThis,
1202 int nType,
1203 int nX,
1204 int nY);
1205 static char* doc_getTextSelection(LibreOfficeKitDocument* pThis,
1206 const char* pMimeType,
1207 char** pUsedMimeType);
1208 static int doc_getSelectionType(LibreOfficeKitDocument* pThis);
1209 static int doc_getSelectionTypeAndText(LibreOfficeKitDocument* pThis,
1210 const char* pMimeType,
1211 char** pText,
1212 char** pUsedMimeType);
1213 static int doc_getClipboard (LibreOfficeKitDocument* pThis,
1214 const char **pMimeTypes,
1215 size_t *pOutCount,
1216 char ***pOutMimeTypes,
1217 size_t **pOutSizes,
1218 char ***pOutStreams);
1219 static int doc_setClipboard (LibreOfficeKitDocument* pThis,
1220 const size_t nInCount,
1221 const char **pInMimeTypes,
1222 const size_t *pInSizes,
1223 const char **pInStreams);
1224 static bool doc_paste(LibreOfficeKitDocument* pThis,
1225 const char* pMimeType,
1226 const char* pData,
1227 size_t nSize);
1228 static void doc_setGraphicSelection (LibreOfficeKitDocument* pThis,
1229 int nType,
1230 int nX,
1231 int nY);
1232 static void doc_resetSelection (LibreOfficeKitDocument* pThis);
1233 static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand);
1234 static void doc_setClientZoom(LibreOfficeKitDocument* pThis,
1235 int nTilePixelWidth,
1236 int nTilePixelHeight,
1237 int nTileTwipWidth,
1238 int nTileTwipHeight);
1239 static void doc_setClientVisibleArea(LibreOfficeKitDocument* pThis, int nX, int nY, int nWidth, int nHeight);
1240 static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int nLevel, int nIndex, bool bHidden);
1241 static int doc_createView(LibreOfficeKitDocument* pThis);
1242 static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis, const char* pOptions);
1243 static void doc_destroyView(LibreOfficeKitDocument* pThis, int nId);
1244 static void doc_setView(LibreOfficeKitDocument* pThis, int nId);
1245 static int doc_getView(LibreOfficeKitDocument* pThis);
1246 static int doc_getViewsCount(LibreOfficeKitDocument* pThis);
1247 static bool doc_getViewIds(LibreOfficeKitDocument* pThis, int* pArray, size_t nSize);
1248 static void doc_setViewLanguage(LibreOfficeKitDocument* pThis, int nId, const char* language);
1249 static unsigned char* doc_renderFontOrientation(LibreOfficeKitDocument* pThis,
1250 const char *pFontName,
1251 const char *pChar,
1252 int* pFontWidth,
1253 int* pFontHeight,
1254 int pOrientation);
1255 static unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis,
1256 const char *pFontName,
1257 const char *pChar,
1258 int* pFontWidth,
1259 int* pFontHeight);
1260 static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart);
1262 static void doc_paintWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
1263 const int nX, const int nY,
1264 const int nWidth, const int nHeight);
1266 static 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);
1271 static 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);
1276 static void doc_postWindow(LibreOfficeKitDocument* pThis, unsigned
1277 nLOKWindowId, int nAction, const char* pData);
1279 static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart);
1281 static bool doc_insertCertificate(LibreOfficeKitDocument* pThis,
1282 const unsigned char* pCertificateBinary,
1283 const int nCertificateBinarySize,
1284 const unsigned char* pPrivateKeyBinary,
1285 const int nPrivateKeyBinarySize);
1287 static bool doc_addCertificate(LibreOfficeKitDocument* pThis,
1288 const unsigned char* pCertificateBinary,
1289 const int nCertificateBinarySize);
1291 static int doc_getSignatureState(LibreOfficeKitDocument* pThis);
1293 static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput);
1295 static void doc_resizeWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
1296 const int nWidth, const int nHeight);
1298 static void doc_completeFunction(LibreOfficeKitDocument* pThis, const char*);
1301 static void doc_sendFormFieldEvent(LibreOfficeKitDocument* pThis,
1302 const char* pArguments);
1304 static bool doc_renderSearchResult(LibreOfficeKitDocument* pThis,
1305 const char* pSearchResult, unsigned char** pBitmapBuffer,
1306 int* pWidth, int* pHeight, size_t* pByteSize);
1308 static void doc_sendContentControlEvent(LibreOfficeKitDocument* pThis, const char* pArguments);
1310 static void doc_setViewTimezone(LibreOfficeKitDocument* pThis, int nId, const char* timezone);
1312 static void doc_setAccessibilityState(LibreOfficeKitDocument* pThis, int nId, bool bEnabled);
1314 static char* doc_getA11yFocusedParagraph(LibreOfficeKitDocument* pThis);
1316 static int doc_getA11yCaretPosition(LibreOfficeKitDocument* pThis);
1317 } // extern "C"
1319 namespace {
1320 ITiledRenderable* getTiledRenderable(LibreOfficeKitDocument* pThis)
1322 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
1323 return dynamic_cast<ITiledRenderable*>(pDocument->mxComponent.get());
1326 #ifndef IOS
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).
1333 rtl::Reference<LOKClipboard> forceSetClipboardForCurrentView(LibreOfficeKitDocument *pThis)
1335 ITiledRenderable* pDoc = getTiledRenderable(pThis);
1336 rtl::Reference<LOKClipboard> xClip(LOKClipboardFactory::getClipboardForCurView());
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));
1342 return xClip;
1345 #endif
1347 const vcl::Font* FindFont(std::u16string_view rFontName)
1349 SfxObjectShell* pDocSh = SfxObjectShell::Current();
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;
1361 vcl::Font FindFont_FallbackToDefault(std::u16string_view rFontName)
1363 if (auto pFound = FindFont(rFontName))
1364 return *pFound;
1366 return OutputDevice::GetDefaultFont(DefaultFontType::SANS_UNICODE, LANGUAGE_NONE,
1367 GetDefaultFontFlags::NONE);
1370 int getDocumentType (LibreOfficeKitDocument* pThis)
1372 SetLastExceptionMsg();
1374 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
1378 uno::Reference<lang::XServiceInfo> xDocument(pDocument->mxComponent, uno::UNO_QUERY_THROW);
1380 if (xDocument->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
1382 return LOK_DOCTYPE_SPREADSHEET;
1384 else if (xDocument->supportsService("com.sun.star.presentation.PresentationDocument"))
1386 return LOK_DOCTYPE_PRESENTATION;
1388 else if (xDocument->supportsService("com.sun.star.drawing.DrawingDocument"))
1390 return LOK_DOCTYPE_DRAWING;
1392 else if (xDocument->supportsService("com.sun.star.text.TextDocument") || xDocument->supportsService("com.sun.star.text.WebDocument"))
1394 return LOK_DOCTYPE_TEXT;
1396 else
1398 SetLastExceptionMsg("unknown document type");
1401 catch (const uno::Exception& exception)
1403 SetLastExceptionMsg("exception: " + exception.Message);
1405 return LOK_DOCTYPE_OTHER;
1408 } // anonymous namespace
1410 LibLODocument_Impl::LibLODocument_Impl(uno::Reference <css::lang::XComponent> xComponent, int nDocumentId)
1411 : mxComponent(std::move(xComponent))
1412 , mnDocumentId(nDocumentId)
1414 assert(nDocumentId != -1 && "Cannot set mnDocumentId to -1");
1416 m_pDocumentClass = gDocumentClass.lock();
1417 if (!m_pDocumentClass)
1419 m_pDocumentClass = std::make_shared<LibreOfficeKitDocumentClass>();
1421 m_pDocumentClass->nSize = sizeof(LibreOfficeKitDocumentClass);
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;
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;
1472 m_pDocumentClass->renderFont = doc_renderFont;
1473 m_pDocumentClass->renderFontOrientation = doc_renderFontOrientation;
1474 m_pDocumentClass->getPartHash = doc_getPartHash;
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;
1482 m_pDocumentClass->setViewLanguage = doc_setViewLanguage;
1484 m_pDocumentClass->getPartInfo = doc_getPartInfo;
1486 m_pDocumentClass->insertCertificate = doc_insertCertificate;
1487 m_pDocumentClass->addCertificate = doc_addCertificate;
1488 m_pDocumentClass->getSignatureState = doc_getSignatureState;
1490 m_pDocumentClass->renderShapeSelection = doc_renderShapeSelection;
1491 m_pDocumentClass->postWindowGestureEvent = doc_postWindowGestureEvent;
1493 m_pDocumentClass->createViewWithOptions = doc_createViewWithOptions;
1494 m_pDocumentClass->completeFunction = doc_completeFunction;
1496 m_pDocumentClass->sendFormFieldEvent = doc_sendFormFieldEvent;
1497 m_pDocumentClass->renderSearchResult = doc_renderSearchResult;
1499 m_pDocumentClass->setBlockedCommandList = doc_setBlockedCommandList;
1501 m_pDocumentClass->sendContentControlEvent = doc_sendContentControlEvent;
1503 m_pDocumentClass->setViewTimezone = doc_setViewTimezone;
1505 m_pDocumentClass->setAccessibilityState = doc_setAccessibilityState;
1507 m_pDocumentClass->getA11yFocusedParagraph = doc_getA11yFocusedParagraph;
1508 m_pDocumentClass->getA11yCaretPosition = doc_getA11yCaretPosition;
1510 gDocumentClass = m_pDocumentClass;
1512 pClass = m_pDocumentClass.get();
1514 #ifndef IOS
1515 forceSetClipboardForCurrentView(this);
1516 #endif
1519 LibLODocument_Impl::~LibLODocument_Impl()
1523 mxComponent->dispose();
1525 catch (const css::lang::DisposedException&)
1527 TOOLS_WARN_EXCEPTION("lok", "failed to dispose document");
1531 static OUString getGenerator()
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);
1540 extern "C" {
1542 CallbackFlushHandler::TimeoutIdle::TimeoutIdle( CallbackFlushHandler* handler )
1543 : Timer( "lokit timer callback" )
1544 , mHandler( handler )
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
1552 void CallbackFlushHandler::TimeoutIdle::Invoke()
1554 mHandler->Invoke();
1557 // One of these is created per view to handle events cf. doc_registerCallback
1558 CallbackFlushHandler::CallbackFlushHandler(LibreOfficeKitDocument* pDocument, LibreOfficeKitCallback pCallback, void* pData)
1559 : Idle( "lokit idle callback" ),
1560 m_pDocument(pDocument),
1561 m_pCallback(pCallback),
1562 m_pData(pData),
1563 m_nDisableCallbacks(0),
1564 m_TimeoutIdle( this )
1566 SetPriority(TaskPriority::POST_PAINT);
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");
1586 CallbackFlushHandler::~CallbackFlushHandler()
1588 Stop();
1591 CallbackFlushHandler::queue_type2::iterator CallbackFlushHandler::toQueue2(CallbackFlushHandler::queue_type1::iterator pos)
1593 int delta = std::distance(m_queue1.begin(), pos);
1594 return m_queue2.begin() + delta;
1597 CallbackFlushHandler::queue_type2::reverse_iterator CallbackFlushHandler::toQueue2(CallbackFlushHandler::queue_type1::reverse_iterator pos)
1599 int delta = std::distance(m_queue1.rbegin(), pos);
1600 return m_queue2.rbegin() + delta;
1603 void CallbackFlushHandler::setUpdatedType( int nType, bool value )
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
1608 m_updatedTypes[ nType ] = value;
1609 if(value)
1610 startTimer();
1613 void CallbackFlushHandler::resetUpdatedType( int nType )
1615 setUpdatedType( nType, false );
1618 void CallbackFlushHandler::setUpdatedTypePerViewId( int nType, int nViewId, int nSourceViewId, bool value )
1620 assert(isUpdatedTypePerViewId(nType));
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();
1629 void CallbackFlushHandler::resetUpdatedTypePerViewId( int nType, int nViewId )
1631 assert(isUpdatedTypePerViewId(nType));
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 )
1638 setUpdatedTypePerViewId( nType, nViewId, -1, false );
1639 return;
1641 for( auto& it : m_updatedTypesPerViewId )
1643 std::vector<PerViewIdData>& types = it.second;
1644 if( types.size() >= o3tl::make_unsigned( nType ))
1645 types[ nType ].set = false;
1649 void CallbackFlushHandler::libreOfficeKitViewCallback(int nType, const OString& pPayload)
1651 CallbackData callbackData(pPayload);
1652 queue(nType, callbackData);
1655 void CallbackFlushHandler::libreOfficeKitViewCallbackWithViewId(int nType, const OString& pPayload, int nViewId)
1657 CallbackData callbackData(pPayload, nViewId);
1658 queue(nType, callbackData);
1661 void CallbackFlushHandler::libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle* pRect, int nPart, int nMode)
1663 CallbackData callbackData(pRect, nPart, nMode);
1664 queue(LOK_CALLBACK_INVALIDATE_TILES, callbackData);
1667 void CallbackFlushHandler::libreOfficeKitViewUpdatedCallback(int nType)
1669 assert(isUpdatedType( nType ));
1670 std::unique_lock<std::recursive_mutex> lock(m_mutex);
1671 SAL_INFO("lok", "Updated: [" << nType << "]");
1672 setUpdatedType(nType, true);
1675 void CallbackFlushHandler::libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId, int nSourceViewId)
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);
1683 void CallbackFlushHandler::dumpState(rtl::OStringBuffer &rState)
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)
1693 rState.append("\n\t\t");
1694 rState.append(static_cast<sal_Int32>(i.first));
1695 rState.append("\t");
1696 rState.append(i.second);
1700 void CallbackFlushHandler::libreOfficeKitViewAddPendingInvalidateTiles()
1702 // Invoke() will call flushPendingLOKInvalidateTiles(), so just make sure the timer is active.
1703 startTimer();
1706 void CallbackFlushHandler::queue(const int type, const OString& data)
1708 CallbackData callbackData(data);
1709 queue(type, callbackData);
1712 void CallbackFlushHandler::queue(const int type, CallbackData& aCallbackData)
1714 comphelper::ProfileZone aZone("CallbackFlushHandler::queue");
1716 SAL_INFO("lok", "Queue: [" << type << "]: [" << aCallbackData.getPayload() << "] on " << m_queue1.size() << " entries.");
1718 bool bIsChartActive = false;
1719 bool bIsComment = false;
1720 if (type == LOK_CALLBACK_GRAPHIC_SELECTION)
1722 LokChartHelper aChartHelper(SfxViewShell::Current());
1723 bIsChartActive = aChartHelper.GetWindow() != nullptr;
1725 else if (type == LOK_CALLBACK_COMMENT)
1727 bIsComment = true;
1730 if (callbacksDisabled() && !bIsChartActive && !bIsComment)
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)
1750 SAL_INFO("lok", "Skipping while painting [" << type << "]: [" << aCallbackData.getPayload() << "].");
1751 return;
1754 // In Writer we drop all notifications during painting.
1755 if (doc_getDocumentType(m_pDocument) == LOK_DOCTYPE_TEXT)
1756 return;
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)
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;
1773 std::unique_lock<std::recursive_mutex> lock(m_mutex);
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))
1780 SAL_INFO("lok", "Received event with updated type [" << type << "] as normal callback");
1781 resetUpdatedType(type);
1783 if(isUpdatedTypePerViewId(type))
1785 SAL_INFO("lok", "Received event with updated type [" << type << "] as normal callback");
1786 resetUpdatedTypePerViewId(type, aCallbackData.getViewId());
1789 // drop duplicate callbacks for the listed types
1790 switch (type)
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 case LOK_CALLBACK_COLOR_PALETTES:
1821 const auto& pos = std::find(m_queue1.rbegin(), m_queue1.rend(), type);
1822 auto pos2 = toQueue2(pos);
1823 if (pos != m_queue1.rend() && pos2->getPayload() == aCallbackData.getPayload())
1825 SAL_INFO("lok", "Skipping queue duplicate [" << type << + "]: [" << aCallbackData.getPayload() << "].");
1826 return;
1829 break;
1832 if (type == LOK_CALLBACK_TEXT_SELECTION && aCallbackData.isEmpty())
1834 const auto& posStart = std::find(m_queue1.rbegin(), m_queue1.rend(), LOK_CALLBACK_TEXT_SELECTION_START);
1835 auto posStart2 = toQueue2(posStart);
1836 if (posStart != m_queue1.rend())
1837 posStart2->clear();
1839 const auto& posEnd = std::find(m_queue1.rbegin(), m_queue1.rend(), LOK_CALLBACK_TEXT_SELECTION_END);
1840 auto posEnd2 = toQueue2(posEnd);
1841 if (posEnd != m_queue1.rend())
1842 posEnd2->clear();
1845 // When payload is empty discards any previous state.
1846 if (aCallbackData.isEmpty())
1848 switch (type)
1850 case LOK_CALLBACK_TEXT_SELECTION_START:
1851 case LOK_CALLBACK_TEXT_SELECTION_END:
1852 case LOK_CALLBACK_TEXT_SELECTION:
1853 case LOK_CALLBACK_GRAPHIC_SELECTION:
1854 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1855 case LOK_CALLBACK_INVALIDATE_TILES:
1856 if (removeAll(type))
1857 SAL_INFO("lok", "Removed dups of [" << type << "]: [" << aCallbackData.getPayload() << "].");
1858 break;
1861 else
1863 switch (type)
1865 // These are safe to use the latest state and ignore previous
1866 // ones (if any) since the last overrides previous ones.
1867 case LOK_CALLBACK_TEXT_SELECTION_START:
1868 case LOK_CALLBACK_TEXT_SELECTION_END:
1869 case LOK_CALLBACK_TEXT_SELECTION:
1870 case LOK_CALLBACK_MOUSE_POINTER:
1871 case LOK_CALLBACK_CELL_CURSOR:
1872 case LOK_CALLBACK_CELL_FORMULA:
1873 case LOK_CALLBACK_CELL_ADDRESS:
1874 case LOK_CALLBACK_CURSOR_VISIBLE:
1875 case LOK_CALLBACK_SET_PART:
1876 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
1877 case LOK_CALLBACK_RULER_UPDATE:
1878 case LOK_CALLBACK_A11Y_FOCUS_CHANGED:
1879 case LOK_CALLBACK_A11Y_CARET_CHANGED:
1880 case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED:
1881 case LOK_CALLBACK_COLOR_PALETTES:
1883 if (removeAll(type))
1884 SAL_INFO("lok", "Removed dups of [" << type << "]: [" << aCallbackData.getPayload() << "].");
1886 break;
1888 // These are safe to use the latest state and ignore previous
1889 // ones (if any) since the last overrides previous ones,
1890 // but only if the view is the same.
1891 case LOK_CALLBACK_CELL_VIEW_CURSOR:
1892 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
1893 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
1894 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1895 case LOK_CALLBACK_TEXT_VIEW_SELECTION:
1896 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
1897 case LOK_CALLBACK_CALC_FUNCTION_LIST:
1898 case LOK_CALLBACK_FORM_FIELD_BUTTON:
1900 // deleting the duplicate of visible cursor message can cause hyperlink popup not to show up on second/or more click on the same place.
1901 // If the hyperlink is not empty we can bypass that to show the popup
1902 const bool hyperLinkException = type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR &&
1903 aCallbackData.getPayload().indexOf("\"hyperlink\":\"\"") == -1 &&
1904 aCallbackData.getPayload().indexOf("\"hyperlink\": {}") == -1;
1905 if(!hyperLinkException)
1907 const int nViewId = aCallbackData.getViewId();
1908 removeAll(type, [nViewId] (const CallbackData& elemData) {
1909 return (nViewId == elemData.getViewId());
1914 break;
1916 case LOK_CALLBACK_INVALIDATE_TILES:
1917 if (processInvalidateTilesEvent(type, aCallbackData))
1918 return;
1919 break;
1921 // State changes with same name override previous ones with a different value.
1922 // Ex. ".uno:PageStatus=Slide 20 of 83" overwrites any previous PageStatus.
1923 case LOK_CALLBACK_STATE_CHANGED:
1925 // Compare the state name=value and overwrite earlier entries with same name.
1926 const auto pos = aCallbackData.getPayload().indexOf('=');
1927 if (pos != -1)
1929 const std::string_view name = aCallbackData.getPayload().subView(0, pos + 1);
1930 // This is needed because otherwise it creates some problems when
1931 // a save occurs while a cell is still edited in Calc.
1932 if (name != ".uno:ModifiedStatus=")
1934 removeAll(type, [&name] (const CallbackData& elemData) {
1935 return elemData.getPayload().startsWith(name);
1941 break;
1943 case LOK_CALLBACK_WINDOW:
1944 if (processWindowEvent(type, aCallbackData))
1945 return;
1946 break;
1948 case LOK_CALLBACK_GRAPHIC_SELECTION:
1950 // remove only selection ranges and 'EMPTY' messages
1951 // always send 'INPLACE' and 'INPLACE EXIT' messages
1952 removeAll(type, [] (const CallbackData& elemData)
1953 { return (elemData.getPayload().indexOf("INPLACE") == -1); });
1955 break;
1959 // Validate that the cached data and the payload string are identical.
1960 assert(aCallbackData.validate() && "Cached callback payload object and string mismatch!");
1961 m_queue1.emplace_back(type);
1962 m_queue2.emplace_back(aCallbackData);
1963 SAL_INFO("lok", "Queued #" << (m_queue1.size() - 1) <<
1964 " [" << type << "]: [" << aCallbackData.getPayload() << "] to have " << m_queue1.size() << " entries.");
1966 #ifdef DBG_UTIL
1968 // Dump the queue state and validate cached data.
1969 int i = 1;
1970 std::ostringstream oss;
1971 if (m_queue1.empty())
1972 oss << "Empty";
1973 else
1974 oss << m_queue1.size() << " items\n";
1975 auto it1 = m_queue1.begin();
1976 auto it2 = m_queue2.begin();
1977 for (; it1 != m_queue1.end(); ++it1, ++it2)
1978 oss << i++ << ": [" << *it1 << "] [" << it2->getPayload() << "].\n";
1979 SAL_INFO("lok", "Current Queue: " << oss.str());
1980 assert(
1981 std::all_of(
1982 m_queue2.begin(), m_queue2.end(),
1983 [](const CallbackData& c) { return c.validate(); }));
1985 #endif
1987 lock.unlock();
1988 startTimer();
1991 bool CallbackFlushHandler::processInvalidateTilesEvent(int type, CallbackData& aCallbackData)
1993 RectangleAndPart rcNew = aCallbackData.getRectangleAndPart();
1994 if (rcNew.isEmpty())
1996 SAL_INFO("lok", "Skipping invalid event [" << type << "]: [" << aCallbackData.getPayload() << "].");
1997 return true;
2000 // If we have to invalidate all tiles, we can skip any new tile invalidation.
2001 // Find the last INVALIDATE_TILES entry, if any to see if it's invalidate-all.
2002 const auto& pos
2003 = std::find(m_queue1.rbegin(), m_queue1.rend(), LOK_CALLBACK_INVALIDATE_TILES);
2004 if (pos != m_queue1.rend())
2006 auto pos2 = toQueue2(pos);
2007 const RectangleAndPart& rcOld = pos2->getRectangleAndPart();
2008 if (rcOld.isInfinite() && (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart) &&
2009 (rcOld.m_nMode == rcNew.m_nMode))
2011 SAL_INFO("lok", "Skipping queue [" << type << "]: [" << aCallbackData.getPayload()
2012 << "] since all tiles need to be invalidated.");
2013 return true;
2016 if ((rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart) && (rcOld.m_nMode == rcNew.m_nMode))
2018 // If fully overlapping.
2019 if (rcOld.m_aRectangle.Contains(rcNew.m_aRectangle))
2021 SAL_INFO("lok", "Skipping queue [" << type << "]: [" << aCallbackData.getPayload()
2022 << "] since overlaps existing all-parts.");
2023 return true;
2028 if (rcNew.isInfinite())
2030 SAL_INFO("lok", "Have Empty [" << type << "]: [" << aCallbackData.getPayload()
2031 << "] so removing all with part " << rcNew.m_nPart << ".");
2032 removeAll(LOK_CALLBACK_INVALIDATE_TILES, [&rcNew](const CallbackData& elemData) {
2033 // Remove exiting if new is all-encompassing, or if of the same part.
2034 return ((rcNew.m_nPart == -1 || rcNew.m_nPart == elemData.getRectangleAndPart().m_nPart)
2035 && (rcNew.m_nMode == elemData.getRectangleAndPart().m_nMode));
2038 else
2040 const auto rcOrig = rcNew;
2042 SAL_INFO("lok", "Have [" << type << "]: [" << aCallbackData.getPayload() << "] so merging overlapping.");
2043 removeAll(LOK_CALLBACK_INVALIDATE_TILES,[&rcNew](const CallbackData& elemData) {
2044 const RectangleAndPart& rcOld = elemData.getRectangleAndPart();
2045 if (rcNew.m_nPart != -1 && rcOld.m_nPart != -1 &&
2046 (rcOld.m_nPart != rcNew.m_nPart || rcOld.m_nMode != rcNew.m_nMode))
2048 SAL_INFO("lok", "Nothing to merge between new: "
2049 << rcNew.toString() << ", and old: " << rcOld.toString());
2050 return false;
2053 if (rcNew.m_nPart == -1)
2055 // Don't merge unless fully overlapped.
2056 SAL_INFO("lok", "New " << rcNew.toString() << " has " << rcOld.toString()
2057 << "?");
2058 if (rcNew.m_aRectangle.Contains(rcOld.m_aRectangle) && rcOld.m_nMode == rcNew.m_nMode)
2060 SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
2061 << rcOld.toString() << ".");
2062 return true;
2065 else if (rcOld.m_nPart == -1)
2067 // Don't merge unless fully overlapped.
2068 SAL_INFO("lok", "Old " << rcOld.toString() << " has " << rcNew.toString()
2069 << "?");
2070 if (rcOld.m_aRectangle.Contains(rcNew.m_aRectangle) && rcOld.m_nMode == rcNew.m_nMode)
2072 SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
2073 << rcOld.toString() << ".");
2074 return true;
2077 else
2079 const tools::Rectangle rcOverlap
2080 = rcNew.m_aRectangle.GetIntersection(rcOld.m_aRectangle);
2081 const bool bOverlap = !rcOverlap.IsEmpty() && rcOld.m_nMode == rcNew.m_nMode;
2082 SAL_INFO("lok", "Merging " << rcNew.toString() << " & " << rcOld.toString()
2083 << " => " << rcOverlap.toString()
2084 << " Overlap: " << bOverlap);
2085 if (bOverlap)
2087 rcNew.m_aRectangle.Union(rcOld.m_aRectangle);
2088 SAL_INFO("lok", "Merged: " << rcNew.toString());
2089 return true;
2093 // Keep others.
2094 return false;
2097 if (rcNew.m_aRectangle != rcOrig.m_aRectangle)
2099 SAL_INFO("lok", "Replacing: " << rcOrig.toString() << " by " << rcNew.toString());
2100 if (rcNew.m_aRectangle.GetWidth() < rcOrig.m_aRectangle.GetWidth()
2101 || rcNew.m_aRectangle.GetHeight() < rcOrig.m_aRectangle.GetHeight())
2103 SAL_WARN("lok", "Error: merged rect smaller.");
2108 aCallbackData.updateRectangleAndPart(rcNew);
2109 // Queue this one.
2110 return false;
2113 bool CallbackFlushHandler::processWindowEvent(int type, CallbackData& aCallbackData)
2115 const OString& payload = aCallbackData.getPayload();
2117 boost::property_tree::ptree& aTree = aCallbackData.setJson(std::string(payload));
2118 const unsigned nLOKWindowId = aTree.get<unsigned>("id", 0);
2119 const std::string aAction = aTree.get<std::string>("action", "");
2120 if (aAction == "invalidate")
2122 std::string aRectStr = aTree.get<std::string>("rectangle", "");
2123 // no 'rectangle' field => invalidate all of the window =>
2124 // remove all previous window part invalidations
2125 if (aRectStr.empty())
2127 removeAll(LOK_CALLBACK_WINDOW,[&nLOKWindowId](const CallbackData& elemData) {
2128 const boost::property_tree::ptree& aOldTree = elemData.getJson();
2129 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0)
2130 && aOldTree.get<std::string>("action", "") == "invalidate")
2132 return true;
2134 return false;
2137 else
2139 // if we have to invalidate all of the window, ignore
2140 // any part invalidation message
2141 bool invAllExist = false;
2142 auto it1 = m_queue1.rbegin();
2143 auto it2 = m_queue2.rbegin();
2144 for (;it1 != m_queue1.rend(); ++it1, ++it2)
2146 if (*it1 != LOK_CALLBACK_WINDOW)
2147 continue;
2148 const boost::property_tree::ptree& aOldTree = it2->getJson();
2149 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0)
2150 && aOldTree.get<std::string>("action", "") == "invalidate"
2151 && aOldTree.get<std::string>("rectangle", "").empty())
2153 invAllExist = true;
2154 break;
2158 // we found a invalidate-all window callback
2159 if (invAllExist)
2161 SAL_INFO("lok.dialog", "Skipping queue ["
2162 << type << "]: [" << payload
2163 << "] since whole window needs to be invalidated.");
2164 return true;
2167 std::istringstream aRectStream(aRectStr);
2168 tools::Long nLeft, nTop, nWidth, nHeight;
2169 char nComma;
2170 aRectStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight;
2171 tools::Rectangle aNewRect(nLeft, nTop, nLeft + nWidth, nTop + nHeight);
2172 bool currentIsRedundant = false;
2173 removeAll(LOK_CALLBACK_WINDOW, [&aNewRect, &nLOKWindowId,
2174 &currentIsRedundant](const CallbackData& elemData) {
2175 const boost::property_tree::ptree& aOldTree = elemData.getJson();
2176 if (aOldTree.get<std::string>("action", "") == "invalidate")
2178 // Not possible that we encounter an empty rectangle here; we already handled this case above.
2179 std::istringstream aOldRectStream(aOldTree.get<std::string>("rectangle", ""));
2180 tools::Long nOldLeft, nOldTop, nOldWidth, nOldHeight;
2181 char nOldComma;
2182 aOldRectStream >> nOldLeft >> nOldComma >> nOldTop >> nOldComma >> nOldWidth
2183 >> nOldComma >> nOldHeight;
2184 const tools::Rectangle aOldRect = tools::Rectangle(
2185 nOldLeft, nOldTop, nOldLeft + nOldWidth, nOldTop + nOldHeight);
2187 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
2189 if (aNewRect == aOldRect)
2191 SAL_INFO("lok.dialog", "Duplicate rect [" << aNewRect.toString()
2192 << "]. Skipping new.");
2193 // We have a rectangle in the queue already that makes the current Callback useless.
2194 currentIsRedundant = true;
2195 return false;
2197 // new one engulfs the old one?
2198 else if (aNewRect.Contains(aOldRect))
2200 SAL_INFO("lok.dialog",
2201 "New rect [" << aNewRect.toString() << "] engulfs old ["
2202 << aOldRect.toString() << "]. Replacing old.");
2203 return true;
2205 // old one engulfs the new one?
2206 else if (aOldRect.Contains(aNewRect))
2208 SAL_INFO("lok.dialog",
2209 "Old rect [" << aOldRect.toString() << "] engulfs new ["
2210 << aNewRect.toString() << "]. Skipping new.");
2211 // We have a rectangle in the queue already that makes the current Callback useless.
2212 currentIsRedundant = true;
2213 return false;
2215 else
2217 // Overlapping rects.
2218 const tools::Rectangle aPreMergeRect = aNewRect;
2219 aNewRect.Union(aOldRect);
2220 SAL_INFO("lok.dialog", "Merging rects ["
2221 << aPreMergeRect.toString() << "] & ["
2222 << aOldRect.toString() << "] = ["
2223 << aNewRect.toString()
2224 << "]. Replacing old.");
2225 return true;
2230 // keep rest
2231 return false;
2234 // Do not enqueue if redundant.
2235 if (currentIsRedundant)
2236 return true;
2238 aTree.put("rectangle", aNewRect.toString().getStr());
2239 aCallbackData.setJson(aTree);
2240 assert(aCallbackData.validate() && "Validation after setJson failed!");
2243 else if (aAction == "created")
2245 // Remove all previous actions on same dialog, if we are creating it anew.
2246 removeAll(LOK_CALLBACK_WINDOW,[&nLOKWindowId](const CallbackData& elemData) {
2247 const boost::property_tree::ptree& aOldTree = elemData.getJson();
2248 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
2249 return true;
2250 return false;
2253 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
2254 if (!pWindow)
2256 gImpl->maLastExceptionMsg = "Document doesn't support dialog rendering, or window not found.";
2257 return false;
2260 #ifndef IOS
2261 auto xClip = forceSetClipboardForCurrentView(m_pDocument);
2263 uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(xClip);
2264 pWindow->SetClipboard(xClipboard);
2265 #endif
2267 else if (aAction == "size_changed")
2269 // A size change is practically re-creation of the window.
2270 // But at a minimum it's a full invalidation.
2271 removeAll(LOK_CALLBACK_WINDOW, [&nLOKWindowId](const CallbackData& elemData) {
2272 const boost::property_tree::ptree& aOldTree = elemData.getJson();
2273 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
2275 const std::string aOldAction = aOldTree.get<std::string>("action", "");
2276 if (aOldAction == "invalidate")
2277 return true;
2279 return false;
2283 // Queue this one.
2284 return false;
2287 void CallbackFlushHandler::enqueueUpdatedTypes()
2289 if( m_updatedTypes.empty() && m_updatedTypesPerViewId.empty())
2290 return;
2291 assert(m_viewId >= 0);
2292 SfxViewShell* viewShell = SfxViewShell::GetFirst( false,
2293 [this](const SfxViewShell* shell) { return shell->GetViewShellId().get() == m_viewId; } );
2294 assert(viewShell != nullptr);
2296 // First move data to local structures, so that callbacks don't possibly modify it.
2297 std::vector<bool> updatedTypes;
2298 std::swap(updatedTypes, m_updatedTypes);
2299 boost::container::flat_map<int, std::vector<PerViewIdData>> updatedTypesPerViewId;
2300 std::swap(updatedTypesPerViewId, m_updatedTypesPerViewId);
2302 // Some types must always precede other types, for example
2303 // LOK_CALLBACK_TEXT_SELECTION_START and LOK_CALLBACK_TEXT_SELECTION_END
2304 // must always precede LOK_CALLBACK_TEXT_SELECTION if present.
2305 // Only these types should be present (see isUpdatedType()) and should be processed in this order.
2306 static const int orderedUpdatedTypes[] = {
2307 LOK_CALLBACK_TEXT_SELECTION_START, LOK_CALLBACK_TEXT_SELECTION_END, LOK_CALLBACK_TEXT_SELECTION };
2308 // Only these types should be present (see isUpdatedTypePerViewId()) and (as of now)
2309 // the order doesn't matter.
2310 static const int orderedUpdatedTypesPerViewId[] = {
2311 LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR,
2312 LOK_CALLBACK_INVALIDATE_VIEW_CURSOR,
2313 LOK_CALLBACK_TEXT_VIEW_SELECTION };
2315 for( int type : orderedUpdatedTypes )
2317 if(o3tl::make_unsigned( type ) < updatedTypes.size() && updatedTypes[ type ])
2319 enqueueUpdatedType( type, viewShell, m_viewId );
2322 for( const auto& it : updatedTypesPerViewId )
2324 int viewId = it.first;
2325 const std::vector<PerViewIdData>& types = it.second;
2326 for( int type : orderedUpdatedTypesPerViewId )
2328 if(o3tl::make_unsigned( type ) < types.size() && types[ type ].set)
2330 SfxViewShell* sourceViewShell = viewShell;
2331 const int sourceViewId = types[ type ].sourceViewId;
2332 if( sourceViewId != m_viewId )
2334 assert(sourceViewId >= 0);
2335 sourceViewShell = SfxViewShell::GetFirst( false,
2336 [sourceViewId](const SfxViewShell* shell) { return shell->GetViewShellId().get() == sourceViewId; } );
2338 if(sourceViewShell == nullptr)
2340 SAL_INFO("lok", "View #" << sourceViewId << " no longer found for updated event [" << type << "]");
2341 continue; // View removed, probably cleaning up.
2343 enqueueUpdatedType( type, sourceViewShell, viewId );
2349 void CallbackFlushHandler::enqueueUpdatedType( int type, const SfxViewShell* viewShell, int viewId )
2351 if (type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR)
2353 if (const SfxViewShell* viewShell2 = LokStarMathHelper(viewShell).GetSmViewShell())
2354 viewShell = viewShell2;
2356 std::optional<OString> payload = viewShell->getLOKPayload( type, viewId );
2357 if(!payload)
2358 return; // No actual payload to send.
2359 CallbackData callbackData(*payload, viewId);
2360 m_queue1.emplace_back(type);
2361 m_queue2.emplace_back(callbackData);
2362 SAL_INFO("lok", "Queued updated [" << type << "]: [" << callbackData.getPayload()
2363 << "] to have " << m_queue1.size() << " entries.");
2366 void CallbackFlushHandler::Invoke()
2368 comphelper::ProfileZone aZone("CallbackFlushHandler::Invoke");
2370 if (!m_pCallback)
2371 return;
2373 // Get any pending invalidate tile events. This will call our callbacks,
2374 // so it must be done before taking the mutex.
2375 assert(m_viewId >= 0);
2376 if(SfxViewShell* viewShell = SfxViewShell::GetFirst( false,
2377 [this](const SfxViewShell* shell) { return shell->GetViewShellId().get() == m_viewId; } ))
2379 viewShell->flushPendingLOKInvalidateTiles();
2382 std::unique_lock<std::recursive_mutex> lock(m_mutex);
2384 // Append messages for updated types, fetch them only now.
2385 enqueueUpdatedTypes();
2387 SAL_INFO("lok", "Flushing " << m_queue1.size() << " elements.");
2388 auto it1 = m_queue1.begin();
2389 auto it2 = m_queue2.begin();
2390 for (; it1 != m_queue1.end(); ++it1, ++it2)
2392 const int type = *it1;
2393 const auto& payload = it2->getPayload();
2394 const int viewId = lcl_isViewCallbackType(type) ? it2->getViewId() : -1;
2396 SAL_INFO("lok", "processing event: [" << type << ',' << viewId << "]: [" << payload << "].");
2398 // common code-path for events on this view:
2399 if (viewId == -1)
2401 sal_Int32 idx;
2402 // key-value pairs
2403 if (type == LOK_CALLBACK_STATE_CHANGED &&
2404 (idx = payload.indexOf('=')) != -1)
2406 OString key = payload.copy(0, idx);
2407 OString value = payload.copy(idx+1);
2408 const auto stateIt = m_lastStateChange.find(key);
2409 if (stateIt != m_lastStateChange.end())
2411 // If the value didn't change, it's safe to ignore.
2412 if (stateIt->second == value)
2414 SAL_INFO("lok", "Skipping new state duplicate: [" << type << "]: [" << payload << "].");
2415 continue;
2417 SAL_INFO("lok", "Replacing a state element [" << type << "]: [" << payload << "].");
2418 stateIt->second = value;
2420 else
2422 SAL_INFO("lok", "Inserted a new state element: [" << type << "]: [" << payload << "]");
2423 m_lastStateChange.emplace(key, value);
2426 else
2428 const auto stateIt = m_states.find(type);
2429 if (stateIt != m_states.end())
2431 // If the state didn't change, it's safe to ignore.
2432 if (stateIt->second == payload)
2434 SAL_INFO("lok", "Skipping duplicate [" << type << "]: [" << payload << "].");
2435 continue;
2437 stateIt->second = payload;
2441 else // less common path for events relating to other views
2443 const auto statesIt = m_viewStates.find(viewId);
2444 if (statesIt != m_viewStates.end())
2446 auto& states = statesIt->second;
2447 const auto stateIt = states.find(type);
2448 if (stateIt != states.end())
2450 // If the state didn't change, it's safe to ignore.
2451 if (stateIt->second == payload)
2453 SAL_INFO("lok", "Skipping view duplicate [" << type << ',' << viewId << "]: [" << payload << "].");
2454 continue;
2457 SAL_INFO("lok", "Replacing an element in view states [" << type << ',' << viewId << "]: [" << payload << "].");
2458 stateIt->second = payload;
2460 else
2462 SAL_INFO("lok", "Inserted a new element in view states: [" << type << ',' << viewId << "]: [" << payload << "]");
2463 states.emplace(type, payload);
2469 m_pCallback(type, payload.getStr(), m_pData);
2472 m_queue1.clear();
2473 m_queue2.clear();
2474 Stop();
2475 m_TimeoutIdle.Stop();
2478 void CallbackFlushHandler::startTimer()
2480 if (!IsActive())
2481 Start();
2482 if (!m_TimeoutIdle.IsActive())
2483 m_TimeoutIdle.Start();
2486 bool CallbackFlushHandler::removeAll(int type)
2488 bool bErased = false;
2489 auto it1 = m_queue1.begin();
2490 for(;;)
2492 it1 = std::find(it1, m_queue1.end(), type);
2493 if(it1 == m_queue1.end())
2494 break;
2495 m_queue2.erase(toQueue2(it1));
2496 it1 = m_queue1.erase(it1);
2497 bErased = true;
2499 return bErased;
2502 bool CallbackFlushHandler::removeAll(int type, const std::function<bool (const CallbackData&)>& rTestFunc)
2504 bool bErased = false;
2505 auto it1 = m_queue1.begin();
2506 for(;;)
2508 it1 = std::find(it1, m_queue1.end(), type);
2509 if(it1 == m_queue1.end())
2510 break;
2511 auto it2 = toQueue2(it1);
2512 if (rTestFunc(*it2))
2514 m_queue2.erase(it2);
2515 it1 = m_queue1.erase(it1);
2516 bErased = true;
2518 else
2519 ++it1;
2521 return bErased;
2524 void CallbackFlushHandler::addViewStates(int viewId)
2526 const auto& result = m_viewStates.emplace(viewId, decltype(m_viewStates)::mapped_type());
2527 if (!result.second && result.first != m_viewStates.end())
2529 result.first->second.clear();
2533 void CallbackFlushHandler::removeViewStates(int viewId)
2535 m_viewStates.erase(viewId);
2539 static void doc_destroy(LibreOfficeKitDocument *pThis)
2541 comphelper::ProfileZone aZone("doc_destroy");
2543 SolarMutexGuard aGuard;
2545 #ifndef IOS
2546 LOKClipboardFactory::releaseClipboardForView(-1);
2547 #endif
2549 LibLODocument_Impl *pDocument = static_cast<LibLODocument_Impl*>(pThis);
2550 delete pDocument;
2553 static void lo_destroy (LibreOfficeKit* pThis);
2554 static int lo_initialize (LibreOfficeKit* pThis, const char* pInstallPath, const char* pUserProfilePath);
2555 static LibreOfficeKitDocument* lo_documentLoad (LibreOfficeKit* pThis, const char* pURL);
2556 static char * lo_getError (LibreOfficeKit* pThis);
2557 static void lo_freeError (char* pFree);
2558 static LibreOfficeKitDocument* lo_documentLoadWithOptions (LibreOfficeKit* pThis,
2559 const char* pURL,
2560 const char* pOptions);
2561 static void lo_registerCallback (LibreOfficeKit* pThis,
2562 LibreOfficeKitCallback pCallback,
2563 void* pData);
2564 static char* lo_getFilterTypes(LibreOfficeKit* pThis);
2565 static void lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long features);
2566 static void lo_setDocumentPassword(LibreOfficeKit* pThis,
2567 const char* pURL,
2568 const char* pPassword);
2569 static char* lo_getVersionInfo(LibreOfficeKit* pThis);
2570 static int lo_runMacro (LibreOfficeKit* pThis, const char* pURL);
2572 static bool lo_signDocument(LibreOfficeKit* pThis,
2573 const char* pUrl,
2574 const unsigned char* pCertificateBinary,
2575 const int nCertificateBinarySize,
2576 const unsigned char* pPrivateKeyBinary,
2577 const int nPrivateKeyBinarySize);
2579 static char* lo_extractRequest(LibreOfficeKit* pThis,
2580 const char* pFilePath);
2582 static void lo_trimMemory(LibreOfficeKit* pThis, int nTarget);
2584 static void lo_runLoop(LibreOfficeKit* pThis,
2585 LibreOfficeKitPollCallback pPollCallback,
2586 LibreOfficeKitWakeCallback pWakeCallback,
2587 void* pData);
2589 static void lo_sendDialogEvent(LibreOfficeKit* pThis,
2590 unsigned long long int nLOKWindowId,
2591 const char* pArguments);
2593 static void lo_setOption(LibreOfficeKit* pThis, const char* pOption, const char* pValue);
2595 static void lo_dumpState(LibreOfficeKit* pThis, const char* pOptions, char** pState);
2597 LibLibreOffice_Impl::LibLibreOffice_Impl()
2598 : m_pOfficeClass( gOfficeClass.lock() )
2599 , maThread(nullptr)
2600 , mpCallback(nullptr)
2601 , mpCallbackData(nullptr)
2602 , mOptionalFeatures(0)
2604 if(!m_pOfficeClass) {
2605 m_pOfficeClass = std::make_shared<LibreOfficeKitClass>();
2606 m_pOfficeClass->nSize = sizeof(LibreOfficeKitClass);
2608 m_pOfficeClass->destroy = lo_destroy;
2609 m_pOfficeClass->documentLoad = lo_documentLoad;
2610 m_pOfficeClass->getError = lo_getError;
2611 m_pOfficeClass->freeError = lo_freeError;
2612 m_pOfficeClass->documentLoadWithOptions = lo_documentLoadWithOptions;
2613 m_pOfficeClass->registerCallback = lo_registerCallback;
2614 m_pOfficeClass->getFilterTypes = lo_getFilterTypes;
2615 m_pOfficeClass->setOptionalFeatures = lo_setOptionalFeatures;
2616 m_pOfficeClass->setDocumentPassword = lo_setDocumentPassword;
2617 m_pOfficeClass->getVersionInfo = lo_getVersionInfo;
2618 m_pOfficeClass->runMacro = lo_runMacro;
2619 m_pOfficeClass->signDocument = lo_signDocument;
2620 m_pOfficeClass->runLoop = lo_runLoop;
2621 m_pOfficeClass->sendDialogEvent = lo_sendDialogEvent;
2622 m_pOfficeClass->setOption = lo_setOption;
2623 m_pOfficeClass->dumpState = lo_dumpState;
2624 m_pOfficeClass->extractRequest = lo_extractRequest;
2625 m_pOfficeClass->trimMemory = lo_trimMemory;
2627 gOfficeClass = m_pOfficeClass;
2630 pClass = m_pOfficeClass.get();
2633 LibLibreOffice_Impl::~LibLibreOffice_Impl()
2637 namespace
2640 void setLanguageAndLocale(OUString const & aLangISO)
2642 SvtSysLocaleOptions aLocalOptions;
2643 aLocalOptions.SetLocaleConfigString(aLangISO);
2644 aLocalOptions.SetUILocaleConfigString(aLangISO);
2645 aLocalOptions.Commit();
2648 void setFormatSpecificFilterData(std::u16string_view sFormat, comphelper::SequenceAsHashMap & rFilterDataMap)
2650 if (sFormat == u"pdf")
2652 // always export bookmarks, which is needed for annotations
2653 rFilterDataMap["ExportBookmarks"] <<= true;
2657 } // anonymous namespace
2659 // Wonder global state ...
2660 static uno::Reference<css::uno::XComponentContext> xContext;
2661 static uno::Reference<css::lang::XMultiServiceFactory> xSFactory;
2662 static uno::Reference<css::lang::XMultiComponentFactory> xFactory;
2664 static LibreOfficeKitDocument* lo_documentLoad(LibreOfficeKit* pThis, const char* pURL)
2666 return lo_documentLoadWithOptions(pThis, pURL, nullptr);
2669 static LibreOfficeKitDocument* lo_documentLoadWithOptions(LibreOfficeKit* pThis, const char* pURL, const char* pOptions)
2671 comphelper::ProfileZone aZone("lo_documentLoadWithOptions");
2673 SolarMutexGuard aGuard;
2675 static int nDocumentIdCounter = 0;
2677 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
2678 pLib->maLastExceptionMsg.clear();
2680 const OUString aURL(getAbsoluteURL(pURL));
2681 if (aURL.isEmpty())
2683 pLib->maLastExceptionMsg = "Filename to load was not provided.";
2684 SAL_INFO("lok", "URL for load is empty");
2685 return nullptr;
2688 pLib->maLastExceptionMsg.clear();
2690 if (!xContext.is())
2692 pLib->maLastExceptionMsg = "ComponentContext is not available";
2693 SAL_INFO("lok", "ComponentContext is not available");
2694 return nullptr;
2697 uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext);
2699 if (!xComponentLoader.is())
2701 pLib->maLastExceptionMsg = "ComponentLoader is not available";
2702 SAL_INFO("lok", "ComponentLoader is not available");
2703 return nullptr;
2708 // 'Language=...' is an option that LOK consumes by itself, and does
2709 // not pass it as a parameter to the filter
2710 OUString aOptions = getUString(pOptions);
2711 const OUString aLanguage = extractParameter(aOptions, u"Language");
2712 bool isValidLangTag = LanguageTag::isValidBcp47(aLanguage, nullptr);
2714 if (!aLanguage.isEmpty() && isValidLangTag)
2716 static bool isLoading = true;
2717 if (isLoading)
2719 // Capture the language used to load the document.
2720 SfxLokHelper::setLoadLanguage(aLanguage);
2721 isLoading = false;
2724 SfxLokHelper::setDefaultLanguage(aLanguage);
2725 // Set the LOK language tag, used for dialog tunneling.
2726 comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(aLanguage));
2727 comphelper::LibreOfficeKit::setLocale(LanguageTag(aLanguage));
2729 SAL_INFO("lok", "Set document language to " << aLanguage);
2730 // use with care - it sets it for the entire core, not just the
2731 // document
2732 setLanguageAndLocale(aLanguage);
2733 // Need to reset the static initialized values
2734 SvNumberFormatter::resetTheCurrencyTable();
2737 // Set the timezone, if not empty.
2738 const OUString aTimezone = extractParameter(aOptions, u"Timezone");
2739 if (!aTimezone.isEmpty())
2741 SfxLokHelper::setDefaultTimezone(true, aTimezone);
2743 else
2745 // Default to the TZ envar, if set.
2746 const char* tz = ::getenv("TZ");
2747 if (tz)
2749 SfxLokHelper::setDefaultTimezone(true,
2750 OStringToOUString(tz, RTL_TEXTENCODING_UTF8));
2752 else
2754 SfxLokHelper::setDefaultTimezone(false, OUString());
2758 const OUString aDeviceFormFactor = extractParameter(aOptions, u"DeviceFormFactor");
2759 SfxLokHelper::setDeviceFormFactor(aDeviceFormFactor);
2761 const OUString aBatch = extractParameter(aOptions, u"Batch");
2762 if (!aBatch.isEmpty())
2764 Application::SetDialogCancelMode(DialogCancelMode::LOKSilent);
2767 const OUString sFilterOptions = aOptions;
2769 rtl::Reference<LOKInteractionHandler> const pInteraction(
2770 new LOKInteractionHandler("load", pLib));
2771 auto const pair(pLib->mInteractionMap.insert(std::make_pair(aURL.toUtf8(), pInteraction)));
2772 comphelper::ScopeGuard const g([&] () {
2773 if (pair.second)
2775 pLib->mInteractionMap.erase(aURL.toUtf8());
2778 uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction);
2780 int nMacroSecurityLevel = 1;
2781 const OUString aMacroSecurityLevel = extractParameter(aOptions, u"MacroSecurityLevel");
2782 if (!aMacroSecurityLevel.isEmpty())
2784 double nNumber;
2785 sal_uInt32 nFormat = 1;
2786 SvNumberFormatter aFormatter(::comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US);
2787 if (aFormatter.IsNumberFormat(aMacroSecurityLevel, nFormat, nNumber))
2788 nMacroSecurityLevel = static_cast<int>(nNumber);
2790 SvtSecurityOptions::SetMacroSecurityLevel(nMacroSecurityLevel);
2792 #if defined(ANDROID) && HAVE_FEATURE_ANDROID_LOK
2793 sal_Int16 nMacroExecMode = document::MacroExecMode::USE_CONFIG;
2794 #else
2795 const OUString aEnableMacrosExecution = extractParameter(aOptions, u"EnableMacrosExecution");
2796 sal_Int16 nMacroExecMode = aEnableMacrosExecution == "true" ? document::MacroExecMode::USE_CONFIG :
2797 document::MacroExecMode::NEVER_EXECUTE;
2798 #endif
2800 // set AsTemplate explicitly false to be able to load template files
2801 // as regular files, otherwise we cannot save them; it will try
2802 // to bring saveas dialog which cannot work with LOK case
2803 uno::Sequence<css::beans::PropertyValue> aFilterOptions{
2804 comphelper::makePropertyValue("FilterOptions", sFilterOptions),
2805 comphelper::makePropertyValue("InteractionHandler", xInteraction),
2806 comphelper::makePropertyValue("MacroExecutionMode", nMacroExecMode),
2807 comphelper::makePropertyValue("AsTemplate", false),
2808 comphelper::makePropertyValue("Silent", !aBatch.isEmpty())
2811 /* TODO
2812 sal_Int16 nUpdateDoc = document::UpdateDocMode::ACCORDING_TO_CONFIG;
2813 aFilterOptions[3].Name = "UpdateDocMode";
2814 aFilterOptions[3].Value <<= nUpdateDoc;
2817 OutputDevice::StartTrackingFontMappingUse();
2819 const int nThisDocumentId = nDocumentIdCounter++;
2820 SfxViewShell::SetCurrentDocId(ViewShellDocId(nThisDocumentId));
2821 uno::Reference<lang::XComponent> xComponent = xComponentLoader->loadComponentFromURL(
2822 aURL, "_blank", 0,
2823 aFilterOptions);
2825 assert(!xComponent.is() || pair.second); // concurrent loading of same URL ought to fail
2827 if (!xComponent.is())
2829 pLib->maLastExceptionMsg = "loadComponentFromURL returned an empty reference";
2830 SAL_INFO("lok", "Document can't be loaded - " << pLib->maLastExceptionMsg);
2831 return nullptr;
2834 LibLODocument_Impl* pDocument = new LibLODocument_Impl(xComponent, nThisDocumentId);
2836 // After loading the document, its initial view is the "current" view.
2837 if (pLib->mpCallback)
2839 int nState = doc_getSignatureState(pDocument);
2840 pLib->mpCallback(LOK_CALLBACK_SIGNATURE_STATUS, OString::number(nState).getStr(), pLib->mpCallbackData);
2843 auto aFontMappingUseData = OutputDevice::FinishTrackingFontMappingUse();
2845 if (aFontMappingUseData.size() > 0)
2847 SAL_INFO("lok.fontsubst", "================ Original substitutions:");
2848 for (const auto &i : aFontMappingUseData)
2850 SAL_INFO("lok.fontsubst", i.mOriginalFont);
2851 for (const auto &j : i.mUsedFonts)
2852 SAL_INFO("lok.fontsubst", " " << j);
2856 // Filter out font substitutions that actually aren't any substitutions, like "Liberation
2857 // Serif" -> "Liberation Serif/Regular". If even one of the "substitutions" of a font is to
2858 // the same font, don't count that as a missing font.
2860 aFontMappingUseData.erase
2861 (std::remove_if(aFontMappingUseData.begin(), aFontMappingUseData.end(),
2862 [](OutputDevice::FontMappingUseItem x)
2864 // If the original font had an empty style and one of its
2865 // replacement fonts has the same family name, we assume the font is
2866 // present. The root problem here is that the code that collects
2867 // font substitutions tends to get just empty styles for the font
2868 // that is being substituted, as vcl::Font::GetStyleName() tends to
2869 // return an empty string. (Italicness is instead indicated by what
2870 // vcl::Font::GetItalic() returns and boldness by what
2871 // vcl::Font::GetWeight() returns.)
2873 if (x.mOriginalFont.indexOf('/') == -1)
2874 for (const auto &j : x.mUsedFonts)
2875 if (j == x.mOriginalFont ||
2876 j.startsWith(Concat2View(x.mOriginalFont + "/")))
2877 return true;
2879 return false;
2881 aFontMappingUseData.end());
2883 // Filter out substitutions where a proprietary font has been substituted by a
2884 // metric-compatible one. Obviously this is just a heuristic and implemented only for some
2885 // well-known cases.
2887 aFontMappingUseData.erase
2888 (std::remove_if(aFontMappingUseData.begin(), aFontMappingUseData.end(),
2889 [](OutputDevice::FontMappingUseItem x)
2891 // Again, handle only cases where the original font does not include
2892 // a style. Unclear whether there ever will be a style part included
2893 // in the mOriginalFont.
2895 if (x.mOriginalFont.indexOf('/') == -1)
2896 for (const auto &j : x.mUsedFonts)
2897 if ((x.mOriginalFont == "Arial" &&
2898 j.startsWith("Liberation Sans/")) ||
2899 (x.mOriginalFont == "Times New Roman" &&
2900 j.startsWith("Liberation Serif/")) ||
2901 (x.mOriginalFont == "Courier New" &&
2902 j.startsWith("Liberation Mono/")) ||
2903 (x.mOriginalFont == "Arial Narrow" &&
2904 j.startsWith("Liberation Sans Narrow/")) ||
2905 (x.mOriginalFont == "Cambria" &&
2906 j.startsWith("Caladea/")) ||
2907 (x.mOriginalFont == "Calibri" &&
2908 j.startsWith("Carlito/")) ||
2909 (x.mOriginalFont == "Palatino Linotype" &&
2910 j.startsWith("P052/")) ||
2911 // Perhaps a risky heuristic? If some glyphs from Symbol
2912 // have been mapped to ones in OpenSymbol, don't warn
2913 // that Symbol is missing.
2914 (x.mOriginalFont == "Symbol" &&
2915 j.startsWith("OpenSymbol/")))
2917 return true;
2920 return false;
2922 aFontMappingUseData.end());
2924 if (aFontMappingUseData.size() > 0)
2926 SAL_INFO("lok.fontsubst", "================ Pruned substitutions:");
2927 for (const auto &i : aFontMappingUseData)
2929 SAL_INFO("lok.fontsubst", i.mOriginalFont);
2930 for (const auto &j : i.mUsedFonts)
2931 SAL_INFO("lok.fontsubst", " " << j);
2935 for (std::size_t i = 0; i < aFontMappingUseData.size(); ++i)
2937 pDocument->maFontsMissing.insert(aFontMappingUseData[i].mOriginalFont);
2940 return pDocument;
2942 catch (const uno::Exception& exception)
2944 pLib->maLastExceptionMsg = exception.Message;
2945 TOOLS_INFO_EXCEPTION("lok", "Document can't be loaded");
2948 return nullptr;
2951 static int lo_runMacro(LibreOfficeKit* pThis, const char *pURL)
2953 comphelper::ProfileZone aZone("lo_runMacro");
2955 SolarMutexGuard aGuard;
2957 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
2958 pLib->maLastExceptionMsg.clear();
2960 OUString sURL( pURL, strlen(pURL), RTL_TEXTENCODING_UTF8 );
2961 if (sURL.isEmpty())
2963 pLib->maLastExceptionMsg = "Macro to run was not provided.";
2964 SAL_INFO("lok", "Macro URL is empty");
2965 return false;
2968 if (!sURL.startsWith("macro://"))
2970 pLib->maLastExceptionMsg = "This doesn't look like macro URL";
2971 SAL_INFO("lok", "Macro URL is invalid");
2972 return false;
2975 pLib->maLastExceptionMsg.clear();
2977 if (!xContext.is())
2979 pLib->maLastExceptionMsg = "ComponentContext is not available";
2980 SAL_INFO("lok", "ComponentContext is not available");
2981 return false;
2984 util::URL aURL;
2985 aURL.Complete = sURL;
2987 uno::Reference < util::XURLTransformer > xParser( util::URLTransformer::create( xContext ) );
2989 if( xParser.is() )
2990 xParser->parseStrict( aURL );
2992 uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext);
2994 if (!xComponentLoader.is())
2996 pLib->maLastExceptionMsg = "ComponentLoader is not available";
2997 SAL_INFO("lok", "ComponentLoader is not available");
2998 return false;
3001 xFactory = xContext->getServiceManager();
3003 if (!xFactory)
3004 return false;
3006 uno::Reference<frame::XDispatchProvider> xDP;
3007 xSFactory.set(xFactory, uno::UNO_QUERY_THROW);
3008 xDP.set( xSFactory->createInstance("com.sun.star.comp.sfx2.SfxMacroLoader"), uno::UNO_QUERY );
3009 uno::Reference<frame::XDispatch> xD = xDP->queryDispatch( aURL, OUString(), 0);
3011 if (!xD.is())
3013 pLib->maLastExceptionMsg = "Macro loader is not available";
3014 SAL_INFO("lok", "Macro loader is not available");
3015 return false;
3018 uno::Reference < frame::XSynchronousDispatch > xSyncDisp( xD, uno::UNO_QUERY_THROW );
3019 uno::Sequence<css::beans::PropertyValue> aEmpty;
3020 css::beans::PropertyValue aErr;
3021 uno::Any aRet = xSyncDisp->dispatchWithReturnValue( aURL, aEmpty );
3022 aRet >>= aErr;
3024 if (aErr.Name == "ErrorCode")
3026 sal_uInt32 nErrCode = 0; // ERRCODE_NONE
3027 aErr.Value >>= nErrCode;
3029 pLib->maLastExceptionMsg = "An error occurred running macro (error code: " + OUString::number( nErrCode ) + ")";
3030 SAL_INFO("lok", "Macro execution terminated with error code " << nErrCode);
3032 return false;
3035 return true;
3038 static bool lo_signDocument(LibreOfficeKit* /*pThis*/,
3039 const char* pURL,
3040 const unsigned char* pCertificateBinary,
3041 const int nCertificateBinarySize,
3042 const unsigned char* pPrivateKeyBinary,
3043 const int nPrivateKeyBinarySize)
3045 comphelper::ProfileZone aZone("lo_signDocument");
3047 OUString aURL(getAbsoluteURL(pURL));
3048 if (aURL.isEmpty())
3049 return false;
3051 if (!xContext.is())
3052 return false;
3054 uno::Sequence<sal_Int8> aCertificateSequence;
3056 std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
3057 std::string aCertificateBase64String = extractCertificate(aCertificateString);
3058 if (!aCertificateBase64String.empty())
3060 OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String);
3061 comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
3063 else
3065 aCertificateSequence.realloc(nCertificateBinarySize);
3066 std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.getArray());
3069 uno::Sequence<sal_Int8> aPrivateKeySequence;
3070 std::string aPrivateKeyString(reinterpret_cast<const char*>(pPrivateKeyBinary), nPrivateKeyBinarySize);
3071 std::string aPrivateKeyBase64String = extractPrivateKey(aPrivateKeyString);
3072 if (!aPrivateKeyBase64String.empty())
3074 OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String);
3075 comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString);
3077 else
3079 aPrivateKeySequence.realloc(nPrivateKeyBinarySize);
3080 std::copy(pPrivateKeyBinary, pPrivateKeyBinary + nPrivateKeyBinarySize, aPrivateKeySequence.getArray());
3083 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
3084 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
3085 if (!xSecurityContext.is())
3086 return false;
3088 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
3089 uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
3091 if (!xCertificateCreator.is())
3092 return false;
3094 uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence);
3096 if (!xCertificate.is())
3097 return false;
3099 sfx2::DocumentSigner aDocumentSigner(aURL);
3100 if (!aDocumentSigner.signDocument(xCertificate))
3101 return false;
3103 return true;
3107 static char* lo_extractRequest(LibreOfficeKit* /*pThis*/, const char* pFilePath)
3109 uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext);
3110 uno::Reference< css::lang::XComponent > xComp;
3111 OUString aURL(getAbsoluteURL(pFilePath));
3112 OUString result;
3113 if (!aURL.isEmpty())
3115 if (xComponentLoader.is())
3119 uno::Sequence<css::beans::PropertyValue> aFilterOptions(comphelper::InitPropertySequence(
3121 {"Hidden", css::uno::Any(true)},
3122 {"ReadOnly", css::uno::Any(true)}
3123 }));
3124 xComp = xComponentLoader->loadComponentFromURL( aURL, "_blank", 0, aFilterOptions );
3126 catch ( const lang::IllegalArgumentException& ex )
3128 SAL_WARN("lok", "lo_extractRequest: IllegalArgumentException: " << ex.Message);
3129 result = "{ }";
3130 return convertOUString(result);
3132 catch (...)
3134 SAL_WARN("lok", "lo_extractRequest: Exception on loadComponentFromURL, url= " << aURL);
3135 result = "{ }";
3136 return convertOUString(result);
3139 if (xComp.is())
3141 uno::Reference< document::XLinkTargetSupplier > xLTS( xComp, uno::UNO_QUERY );
3143 if( xLTS.is() )
3145 OUStringBuffer jsonText("{ \"Targets\": { ");
3146 bool lastParentheses = extractLinks(xLTS->getLinks(), false, jsonText);
3147 jsonText.append("} }");
3148 if (!lastParentheses)
3149 jsonText.append(" }");
3151 OUString res(jsonText.makeStringAndClear());
3152 return convertOUString(res);
3154 xComp->dispose();
3156 else
3158 result = "{ }";
3159 return convertOUString(result);
3164 result = "{ }";
3165 return convertOUString(result);
3168 static void lo_trimMemory(LibreOfficeKit* /* pThis */, int nTarget)
3170 vcl::lok::trimMemory(nTarget);
3171 if (nTarget > 1000)
3173 #ifdef HAVE_MALLOC_TRIM
3174 malloc_trim(0);
3175 #endif
3179 static void lo_registerCallback (LibreOfficeKit* pThis,
3180 LibreOfficeKitCallback pCallback,
3181 void* pData)
3183 SolarMutexGuard aGuard;
3185 Application* pApp = GetpApp();
3186 assert(pApp);
3188 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
3189 pLib->maLastExceptionMsg.clear();
3191 pApp->m_pCallback = pLib->mpCallback = pCallback;
3192 pApp->m_pCallbackData = pLib->mpCallbackData = pData;
3195 static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* sUrl, const char* pFormat, const char* pFilterOptions)
3197 comphelper::ProfileZone aZone("doc_saveAs");
3199 SolarMutexGuard aGuard;
3200 SetLastExceptionMsg();
3202 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
3204 OUString sFormat = getUString(pFormat);
3205 OUString aURL(getAbsoluteURL(sUrl));
3207 uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
3209 if (aURL.isEmpty())
3211 SetLastExceptionMsg("Filename to save to was not provided.");
3212 SAL_INFO("lok", "URL for save is empty");
3213 return false;
3218 const ExtensionMap* pMap;
3220 switch (doc_getDocumentType(pThis))
3222 case LOK_DOCTYPE_SPREADSHEET:
3223 pMap = aCalcExtensionMap;
3224 break;
3225 case LOK_DOCTYPE_PRESENTATION:
3226 pMap = aImpressExtensionMap;
3227 break;
3228 case LOK_DOCTYPE_DRAWING:
3229 pMap = aDrawExtensionMap;
3230 break;
3231 case LOK_DOCTYPE_TEXT:
3232 pMap = aWriterExtensionMap;
3233 break;
3234 case LOK_DOCTYPE_OTHER:
3235 default:
3236 SAL_INFO("lok", "Can't save document - unsupported document type.");
3237 return false;
3240 if (pFormat == nullptr)
3242 // sniff from the extension
3243 sal_Int32 idx = aURL.lastIndexOf(".");
3244 if( idx > 0 )
3246 sFormat = aURL.copy( idx + 1 );
3248 else
3250 SetLastExceptionMsg("input URL '" + aURL + "' lacks a suffix");
3251 return false;
3255 OUString aFilterName;
3256 for (sal_Int32 i = 0; pMap[i].extn; ++i)
3258 if (sFormat.equalsIgnoreAsciiCaseAscii(pMap[i].extn))
3260 aFilterName = getUString(pMap[i].filterName);
3261 break;
3264 if (aFilterName.isEmpty())
3266 SetLastExceptionMsg("no output filter found for provided suffix");
3267 return false;
3270 OUString aFilterOptions = getUString(pFilterOptions);
3272 // Check if watermark for pdf is passed by filteroptions...
3273 // It is not a real filter option so it must be filtered out.
3274 OUString watermarkText;
3275 std::u16string_view sFullSheetPreview;
3276 int aIndex = -1;
3277 if ((aIndex = aFilterOptions.indexOf(",Watermark=")) >= 0)
3279 int bIndex = aFilterOptions.indexOf("WATERMARKEND");
3280 watermarkText = aFilterOptions.subView(aIndex+11, bIndex-(aIndex+11));
3281 aFilterOptions = OUString::Concat(aFilterOptions.subView(0, aIndex)) + aFilterOptions.subView(bIndex+12);
3284 if ((aIndex = aFilterOptions.indexOf(",FullSheetPreview=")) >= 0)
3286 int bIndex = aFilterOptions.indexOf("FULLSHEETPREVEND");
3287 sFullSheetPreview = aFilterOptions.subView(aIndex+18, bIndex-(aIndex+18));
3288 aFilterOptions = OUString::Concat(aFilterOptions.subView(0, aIndex)) + aFilterOptions.subView(bIndex+16);
3291 bool bFullSheetPreview = sFullSheetPreview == u"true";
3293 // Select a pdf version if specified a valid one. If not specified then ignore.
3294 // If invalid then fail.
3295 sal_Int32 pdfVer = 0;
3296 if ((aIndex = aFilterOptions.indexOf(",PDFVer=")) >= 0)
3298 int bIndex = aFilterOptions.indexOf("PDFVEREND");
3299 std::u16string_view sPdfVer = aFilterOptions.subView(aIndex+8, bIndex-(aIndex+8));
3300 aFilterOptions = OUString::Concat(aFilterOptions.subView(0, aIndex)) + aFilterOptions.subView(bIndex+9);
3302 if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF/A-1b"))
3303 pdfVer = 1;
3304 else if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF/A-2b"))
3305 pdfVer = 2;
3306 else if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF/A-3b"))
3307 pdfVer = 3;
3308 else if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF-1.5"))
3309 pdfVer = 15;
3310 else if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF-1.6"))
3311 pdfVer = 16;
3312 else
3314 SetLastExceptionMsg("wrong PDF version");
3315 return false;
3319 // 'TakeOwnership' == this is a 'real' SaveAs (that is, the document
3320 // gets a new name). When this is not provided, the meaning of
3321 // saveAs() is more like save-a-copy, which allows saving to any
3322 // random format like PDF or PNG.
3323 // It is not a real filter option, so we have to filter it out.
3324 const uno::Sequence<OUString> aOptionSeq = comphelper::string::convertCommaSeparated(aFilterOptions);
3325 std::vector<OUString> aFilteredOptionVec;
3326 bool bTakeOwnership = false;
3327 MediaDescriptor aSaveMediaDescriptor;
3328 for (const auto& rOption : aOptionSeq)
3330 if (rOption == "TakeOwnership")
3331 bTakeOwnership = true;
3332 else if (rOption == "NoFileSync")
3333 aSaveMediaDescriptor["NoFileSync"] <<= true;
3334 else
3335 aFilteredOptionVec.push_back(rOption);
3338 aSaveMediaDescriptor["Overwrite"] <<= true;
3339 aSaveMediaDescriptor["FilterName"] <<= aFilterName;
3341 auto aFilteredOptionSeq = comphelper::containerToSequence<OUString>(aFilteredOptionVec);
3342 aFilterOptions = comphelper::string::convertCommaSeparated(aFilteredOptionSeq);
3343 aSaveMediaDescriptor[MediaDescriptor::PROP_FILTEROPTIONS] <<= aFilterOptions;
3345 comphelper::SequenceAsHashMap aFilterDataMap;
3347 // If filter options is JSON string, then make sure aFilterDataMap stays empty, otherwise we
3348 // would ignore the filter options.
3349 if (!aFilterOptions.startsWith("{"))
3351 setFormatSpecificFilterData(sFormat, aFilterDataMap);
3354 if (!watermarkText.isEmpty())
3355 aFilterDataMap["TiledWatermark"] <<= watermarkText;
3357 if (bFullSheetPreview)
3358 aFilterDataMap["SinglePageSheets"] <<= true;
3360 if (pdfVer)
3361 aFilterDataMap["SelectPdfVersion"] <<= pdfVer;
3363 if (!aFilterDataMap.empty())
3365 aSaveMediaDescriptor["FilterData"] <<= aFilterDataMap.getAsConstPropertyValueList();
3368 // add interaction handler too
3369 if (gImpl)
3371 // gImpl does not have to exist when running from a unit test
3372 rtl::Reference<LOKInteractionHandler> const pInteraction(
3373 new LOKInteractionHandler("saveas", gImpl, pDocument));
3374 uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction);
3376 aSaveMediaDescriptor[MediaDescriptor::PROP_INTERACTIONHANDLER] <<= xInteraction;
3380 if (bTakeOwnership)
3381 xStorable->storeAsURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList());
3382 else
3383 xStorable->storeToURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList());
3385 return true;
3387 catch (const uno::Exception& exception)
3389 SetLastExceptionMsg("exception: " + exception.Message);
3391 return false;
3395 * Initialize UNO commands, in the sense that from now on, the LOK client gets updates for status
3396 * changes of these commands. This is necessary, because (unlike in the desktop case) there are no
3397 * toolbars hosting widgets these UNO commands, so no such status updates would be sent to the
3398 * headless LOK clients out of the box.
3400 static void doc_iniUnoCommands ()
3402 SolarMutexGuard aGuard;
3403 SetLastExceptionMsg();
3405 OUString sUnoCommands[] =
3407 OUString(".uno:AlignLeft"),
3408 OUString(".uno:AlignHorizontalCenter"),
3409 OUString(".uno:AlignRight"),
3410 OUString(".uno:BackColor"),
3411 OUString(".uno:BackgroundColor"),
3412 OUString(".uno:TableCellBackgroundColor"),
3413 OUString(".uno:Bold"),
3414 OUString(".uno:CenterPara"),
3415 OUString(".uno:CharBackColor"),
3416 OUString(".uno:CharBackgroundExt"),
3417 OUString(".uno:CharFontName"),
3418 OUString(".uno:Color"),
3419 OUString(".uno:ControlCodes"),
3420 OUString(".uno:DecrementIndent"),
3421 OUString(".uno:DefaultBullet"),
3422 OUString(".uno:DefaultNumbering"),
3423 OUString(".uno:FontColor"),
3424 OUString(".uno:FontHeight"),
3425 OUString(".uno:IncrementIndent"),
3426 OUString(".uno:Italic"),
3427 OUString(".uno:JustifyPara"),
3428 OUString(".uno:JumpToMark"),
3429 OUString(".uno:OutlineFont"),
3430 OUString(".uno:LeftPara"),
3431 OUString(".uno:LanguageStatus"),
3432 OUString(".uno:RightPara"),
3433 OUString(".uno:Shadowed"),
3434 OUString(".uno:SubScript"),
3435 OUString(".uno:SuperScript"),
3436 OUString(".uno:Strikeout"),
3437 OUString(".uno:StyleApply"),
3438 OUString(".uno:Underline"),
3439 OUString(".uno:ModifiedStatus"),
3440 OUString(".uno:Undo"),
3441 OUString(".uno:Redo"),
3442 OUString(".uno:InsertPage"),
3443 OUString(".uno:DeletePage"),
3444 OUString(".uno:DuplicatePage"),
3445 OUString(".uno:InsertSlide"),
3446 OUString(".uno:DeleteSlide"),
3447 OUString(".uno:DuplicateSlide"),
3448 OUString(".uno:ChangeTheme"),
3449 OUString(".uno:Cut"),
3450 OUString(".uno:Copy"),
3451 OUString(".uno:Paste"),
3452 OUString(".uno:SelectAll"),
3453 OUString(".uno:ReplyComment"),
3454 OUString(".uno:ResolveComment"),
3455 OUString(".uno:ResolveCommentThread"),
3456 OUString(".uno:InsertRowsBefore"),
3457 OUString(".uno:InsertRowsAfter"),
3458 OUString(".uno:InsertColumnsBefore"),
3459 OUString(".uno:InsertColumnsAfter"),
3460 OUString(".uno:DeleteRows"),
3461 OUString(".uno:DeleteColumns"),
3462 OUString(".uno:DeleteTable"),
3463 OUString(".uno:SelectTable"),
3464 OUString(".uno:EntireRow"),
3465 OUString(".uno:EntireColumn"),
3466 OUString(".uno:EntireCell"),
3467 OUString(".uno:AssignLayout"),
3468 OUString(".uno:StatusDocPos"),
3469 OUString(".uno:RowColSelCount"),
3470 OUString(".uno:StatusPageStyle"),
3471 OUString(".uno:InsertMode"),
3472 OUString(".uno:SpellOnline"),
3473 OUString(".uno:StatusSelectionMode"),
3474 OUString(".uno:StateTableCell"),
3475 OUString(".uno:StatusBarFunc"),
3476 OUString(".uno:StatePageNumber"),
3477 OUString(".uno:StateWordCount"),
3478 OUString(".uno:SelectionMode"),
3479 OUString(".uno:PageStatus"),
3480 OUString(".uno:LayoutStatus"),
3481 OUString(".uno:Scale"),
3482 OUString(".uno:Context"),
3483 OUString(".uno:WrapText"),
3484 OUString(".uno:ToggleMergeCells"),
3485 OUString(".uno:NumberFormatCurrency"),
3486 OUString(".uno:NumberFormatPercent"),
3487 OUString(".uno:NumberFormatDecimal"),
3488 OUString(".uno:NumberFormatDate"),
3489 OUString(".uno:EditHeaderAndFooter"),
3490 OUString(".uno:FrameLineColor"),
3491 OUString(".uno:SortAscending"),
3492 OUString(".uno:SortDescending"),
3493 OUString(".uno:TrackChanges"),
3494 OUString(".uno:ShowTrackedChanges"),
3495 OUString(".uno:NextTrackedChange"),
3496 OUString(".uno:PreviousTrackedChange"),
3497 OUString(".uno:AcceptAllTrackedChanges"),
3498 OUString(".uno:RejectAllTrackedChanges"),
3499 OUString(".uno:TableDialog"),
3500 OUString(".uno:FormatCellDialog"),
3501 OUString(".uno:FontDialog"),
3502 OUString(".uno:ParagraphDialog"),
3503 OUString(".uno:OutlineBullet"),
3504 OUString(".uno:InsertIndexesEntry"),
3505 OUString(".uno:DocumentRepair"),
3506 OUString(".uno:TransformDialog"),
3507 OUString(".uno:InsertPageHeader"),
3508 OUString(".uno:InsertPageFooter"),
3509 OUString(".uno:OnlineAutoFormat"),
3510 OUString(".uno:InsertObjectChart"),
3511 OUString(".uno:InsertSection"),
3512 OUString(".uno:InsertAnnotation"),
3513 OUString(".uno:DeleteAnnotation"),
3514 OUString(".uno:InsertPagebreak"),
3515 OUString(".uno:InsertColumnBreak"),
3516 OUString(".uno:HyperlinkDialog"),
3517 OUString(".uno:InsertSymbol"),
3518 OUString(".uno:EditRegion"),
3519 OUString(".uno:ThesaurusDialog"),
3520 OUString(".uno:FormatArea"),
3521 OUString(".uno:FormatLine"),
3522 OUString(".uno:FormatColumns"),
3523 OUString(".uno:Watermark"),
3524 OUString(".uno:ResetAttributes"),
3525 OUString(".uno:Orientation"),
3526 OUString(".uno:ObjectAlignLeft"),
3527 OUString(".uno:ObjectAlignRight"),
3528 OUString(".uno:AlignCenter"),
3529 OUString(".uno:TransformPosX"),
3530 OUString(".uno:TransformPosY"),
3531 OUString(".uno:TransformWidth"),
3532 OUString(".uno:TransformHeight"),
3533 OUString(".uno:ObjectBackOne"),
3534 OUString(".uno:SendToBack"),
3535 OUString(".uno:ObjectForwardOne"),
3536 OUString(".uno:BringToFront"),
3537 OUString(".uno:WrapRight"),
3538 OUString(".uno:WrapThrough"),
3539 OUString(".uno:WrapLeft"),
3540 OUString(".uno:WrapIdeal"),
3541 OUString(".uno:WrapOn"),
3542 OUString(".uno:WrapOff"),
3543 OUString(".uno:UpdateCurIndex"),
3544 OUString(".uno:InsertCaptionDialog"),
3545 OUString(".uno:FormatGroup"),
3546 OUString(".uno:SplitTable"),
3547 OUString(".uno:SplitCell"),
3548 OUString(".uno:MergeCells"),
3549 OUString(".uno:DeleteNote"),
3550 OUString(".uno:AcceptChanges"),
3551 OUString(".uno:FormatPaintbrush"),
3552 OUString(".uno:SetDefault"),
3553 OUString(".uno:ParaLeftToRight"),
3554 OUString(".uno:ParaRightToLeft"),
3555 OUString(".uno:ParaspaceIncrease"),
3556 OUString(".uno:ParaspaceDecrease"),
3557 OUString(".uno:AcceptTrackedChange"),
3558 OUString(".uno:RejectTrackedChange"),
3559 OUString(".uno:ShowResolvedAnnotations"),
3560 OUString(".uno:InsertBreak"),
3561 OUString(".uno:InsertEndnote"),
3562 OUString(".uno:InsertFootnote"),
3563 OUString(".uno:InsertReferenceField"),
3564 OUString(".uno:InsertBookmark"),
3565 OUString(".uno:InsertAuthoritiesEntry"),
3566 OUString(".uno:InsertMultiIndex"),
3567 OUString(".uno:InsertField"),
3568 OUString(".uno:PageNumberWizard"),
3569 OUString(".uno:InsertPageNumberField"),
3570 OUString(".uno:InsertPageCountField"),
3571 OUString(".uno:InsertDateField"),
3572 OUString(".uno:InsertTitleField"),
3573 OUString(".uno:InsertFieldCtrl"),
3574 OUString(".uno:CharmapControl"),
3575 OUString(".uno:EnterGroup"),
3576 OUString(".uno:LeaveGroup"),
3577 OUString(".uno:AlignUp"),
3578 OUString(".uno:AlignMiddle"),
3579 OUString(".uno:AlignDown"),
3580 OUString(".uno:TraceChangeMode"),
3581 OUString(".uno:Combine"),
3582 OUString(".uno:Merge"),
3583 OUString(".uno:Dismantle"),
3584 OUString(".uno:Substract"),
3585 OUString(".uno:DistributeSelection"),
3586 OUString(".uno:Intersect"),
3587 OUString(".uno:BorderInner"),
3588 OUString(".uno:BorderOuter"),
3589 OUString(".uno:FreezePanes"),
3590 OUString(".uno:FreezePanesColumn"),
3591 OUString(".uno:FreezePanesRow"),
3592 OUString(".uno:Sidebar"),
3593 OUString(".uno:SheetRightToLeft"),
3594 OUString(".uno:RunMacro"),
3595 OUString(".uno:SpacePara1"),
3596 OUString(".uno:SpacePara15"),
3597 OUString(".uno:SpacePara2"),
3598 OUString(".uno:InsertSparkline"),
3599 OUString(".uno:DeleteSparkline"),
3600 OUString(".uno:DeleteSparklineGroup"),
3601 OUString(".uno:EditSparklineGroup"),
3602 OUString(".uno:EditSparkline"),
3603 OUString(".uno:GroupSparklines"),
3604 OUString(".uno:UngroupSparklines"),
3605 OUString(".uno:FormatSparklineMenu"),
3606 OUString(".uno:Protect"),
3607 OUString(".uno:UnsetCellsReadOnly"),
3608 OUString(".uno:ContentControlProperties"),
3609 OUString(".uno:InsertCheckboxContentControl"),
3610 OUString(".uno:InsertContentControl"),
3611 OUString(".uno:InsertDateContentControl"),
3612 OUString(".uno:InsertDropdownContentControl"),
3613 OUString(".uno:InsertPlainTextContentControl"),
3614 OUString(".uno:InsertPictureContentControl")
3617 util::URL aCommandURL;
3618 SfxViewShell* pViewShell = SfxViewShell::Current();
3619 SfxViewFrame* pViewFrame = pViewShell ? &pViewShell->GetViewFrame() : nullptr;
3621 // check if Frame-Controller were created.
3622 if (!pViewFrame)
3624 SAL_WARN("lok", "iniUnoCommands: No Frame-Controller created.");
3625 return;
3628 if (!xContext.is())
3629 xContext = comphelper::getProcessComponentContext();
3630 if (!xContext.is())
3632 SAL_WARN("lok", "iniUnoCommands: Component context is not available");
3633 return;
3636 #if !defined IOS && !defined ANDROID && !defined __EMSCRIPTEN__
3637 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
3638 if (!xSEInitializer.is())
3640 SAL_WARN("lok", "iniUnoCommands: XSEInitializer is not available");
3641 return;
3644 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext =
3645 xSEInitializer->createSecurityContext(OUString());
3646 if (!xSecurityContext.is())
3648 SAL_WARN("lok", "iniUnoCommands: failed to create security context");
3650 #endif
3652 SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(pViewFrame);
3653 uno::Reference<util::XURLTransformer> xParser(util::URLTransformer::create(xContext));
3655 for (const auto & sUnoCommand : sUnoCommands)
3657 aCommandURL.Complete = sUnoCommand;
3658 xParser->parseStrict(aCommandURL);
3660 // when null, this command is not supported by the given component
3661 // (like eg. Calc does not have ".uno:DefaultBullet" etc.)
3662 if (const SfxSlot* pSlot = rSlotPool.GetUnoSlot(aCommandURL.Path))
3664 // Initialize slot to dispatch .uno: Command.
3665 pViewFrame->GetBindings().GetDispatch(pSlot, aCommandURL, false);
3670 static int doc_getDocumentType (LibreOfficeKitDocument* pThis)
3672 comphelper::ProfileZone aZone("doc_getDocumentType");
3674 SolarMutexGuard aGuard;
3675 return getDocumentType(pThis);
3678 static int doc_getParts (LibreOfficeKitDocument* pThis)
3680 comphelper::ProfileZone aZone("doc_getParts");
3682 SolarMutexGuard aGuard;
3684 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3685 if (!pDoc)
3687 SetLastExceptionMsg("Document doesn't support tiled rendering");
3688 return 0;
3691 return pDoc->getParts();
3694 static int doc_getPart (LibreOfficeKitDocument* pThis)
3696 comphelper::ProfileZone aZone("doc_getPart");
3698 SolarMutexGuard aGuard;
3699 SetLastExceptionMsg();
3701 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3702 if (!pDoc)
3704 SetLastExceptionMsg("Document doesn't support tiled rendering");
3705 return 0;
3708 return pDoc->getPart();
3711 static void doc_setPartImpl(LibreOfficeKitDocument* pThis, int nPart, bool bAllowChangeFocus = true)
3713 comphelper::ProfileZone aZone("doc_setPart");
3715 SolarMutexGuard aGuard;
3716 SetLastExceptionMsg();
3718 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3719 if (!pDoc)
3721 SetLastExceptionMsg("Document doesn't support tiled rendering");
3722 return;
3725 pDoc->setPart( nPart, bAllowChangeFocus );
3728 static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart)
3730 doc_setPartImpl(pThis, nPart, true);
3733 static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart)
3735 comphelper::ProfileZone aZone("doc_getPartInfo");
3737 SolarMutexGuard aGuard;
3738 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3739 if (!pDoc)
3741 SetLastExceptionMsg("Document doesn't support tiled rendering");
3742 return nullptr;
3745 return convertOUString(pDoc->getPartInfo(nPart));
3748 static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect)
3750 SolarMutexGuard aGuard;
3751 if (gImpl)
3752 gImpl->maLastExceptionMsg.clear();
3754 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3755 if (!pDoc)
3757 gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
3758 return;
3761 pDoc->selectPart( nPart, nSelect );
3764 static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate)
3766 SolarMutexGuard aGuard;
3767 if (gImpl)
3768 gImpl->maLastExceptionMsg.clear();
3770 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3771 if (!pDoc)
3773 gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
3774 return;
3777 pDoc->moveSelectedParts(nPosition, bDuplicate);
3780 static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis)
3782 comphelper::ProfileZone aZone("doc_getPartPageRectangles");
3784 SolarMutexGuard aGuard;
3785 SetLastExceptionMsg();
3787 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3788 if (!pDoc)
3790 SetLastExceptionMsg("Document doesn't support tiled rendering");
3791 return nullptr;
3794 return convertOUString(pDoc->getPartPageRectangles());
3797 static char* doc_getA11yFocusedParagraph(LibreOfficeKitDocument* pThis)
3799 SolarMutexGuard aGuard;
3800 SetLastExceptionMsg();
3802 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3803 if (!pDoc)
3805 SetLastExceptionMsg("Document doesn't support tiled rendering");
3806 return nullptr;
3809 if (SfxViewShell* pViewShell = SfxViewShell::Current())
3811 return convertOUString(pViewShell->getA11yFocusedParagraph());
3814 return nullptr;
3817 static int doc_getA11yCaretPosition(LibreOfficeKitDocument* pThis)
3819 SolarMutexGuard aGuard;
3820 SetLastExceptionMsg();
3822 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3823 if (!pDoc)
3825 SetLastExceptionMsg("Document doesn't support tiled rendering");
3826 return -1;
3828 if (SfxViewShell* pViewShell = SfxViewShell::Current())
3830 return pViewShell->getA11yCaretPosition();
3833 return -1;
3837 static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart)
3839 comphelper::ProfileZone aZone("doc_getPartName");
3841 SolarMutexGuard aGuard;
3842 SetLastExceptionMsg();
3844 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3845 if (!pDoc)
3847 SetLastExceptionMsg("Document doesn't support tiled rendering");
3848 return nullptr;
3851 return convertOUString(pDoc->getPartName(nPart));
3854 static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart)
3856 comphelper::ProfileZone aZone("doc_getPartHash");
3858 SolarMutexGuard aGuard;
3859 SetLastExceptionMsg();
3861 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3862 if (!pDoc)
3864 SetLastExceptionMsg("Document doesn't support tiled rendering");
3865 return nullptr;
3868 return convertOUString(pDoc->getPartHash(nPart));
3871 static void doc_setPartMode(LibreOfficeKitDocument* pThis,
3872 int nPartMode)
3874 comphelper::ProfileZone aZone("doc_setPartMode");
3876 SolarMutexGuard aGuard;
3877 SetLastExceptionMsg();
3879 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3880 if (!pDoc)
3882 SetLastExceptionMsg("Document doesn't support tiled rendering");
3883 return;
3887 int nCurrentPart = pDoc->getPart();
3889 pDoc->setPartMode(nPartMode);
3891 // We need to make sure the internal state is updated, just changing the mode
3892 // might not update the relevant shells (i.e. impress will keep rendering the
3893 // previous mode unless we do this).
3894 // TODO: we might want to do this within the relevant components rather than
3895 // here, but that's also dependent on how we implement embedded object
3896 // rendering I guess?
3897 // TODO: we could be clever and e.g. set to 0 when we change to/from
3898 // embedded object mode, and not when changing between slide/notes/combined
3899 // modes?
3900 if ( nCurrentPart < pDoc->getParts() )
3902 pDoc->setPart( nCurrentPart );
3904 else
3906 pDoc->setPart( 0 );
3910 static int doc_getEditMode(LibreOfficeKitDocument* pThis)
3912 comphelper::ProfileZone aZone("doc_getEditMode");
3914 SolarMutexGuard aGuard;
3915 SetLastExceptionMsg();
3917 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3918 if (!pDoc)
3920 SetLastExceptionMsg("Document doesn't support tiled rendering");
3921 return 0;
3924 return pDoc->getEditMode();
3927 static void doc_paintTile(LibreOfficeKitDocument* pThis,
3928 unsigned char* pBuffer,
3929 const int nCanvasWidth, const int nCanvasHeight,
3930 const int nTilePosX, const int nTilePosY,
3931 const int nTileWidth, const int nTileHeight)
3933 comphelper::ProfileZone aZone("doc_paintTile");
3935 SolarMutexGuard aGuard;
3936 SetLastExceptionMsg();
3938 SAL_INFO( "lok.tiledrendering", "paintTile: painting [" << nTileWidth << "x" << nTileHeight <<
3939 "]@(" << nTilePosX << ", " << nTilePosY << ") to [" <<
3940 nCanvasWidth << "x" << nCanvasHeight << "]px" );
3942 ITiledRenderable* pDoc = getTiledRenderable(pThis);
3943 if (!pDoc)
3945 SetLastExceptionMsg("Document doesn't support tiled rendering");
3946 return;
3949 #if defined(UNX) && !defined(MACOSX) || defined(_WIN32)
3951 // Painting of zoomed or HiDPI spreadsheets is special, we actually draw everything at 100%,
3952 // and only set cairo's (or CoreGraphic's, in the iOS case) scale factor accordingly, so that
3953 // everything is painted bigger or smaller. This is different to what Calc's internal scaling
3954 // would do - because that one is trying to fit the lines between cells to integer multiples of
3955 // pixels.
3956 comphelper::ScopeGuard dpiScaleGuard([]() { comphelper::LibreOfficeKit::setDPIScale(1.0); });
3958 #if defined(IOS)
3959 double fDPIScale = 1.0;
3961 CGContextRef pCGContext = CGBitmapContextCreate(pBuffer, nCanvasWidth, nCanvasHeight, 8,
3962 nCanvasWidth * 4, CGColorSpaceCreateDeviceRGB(),
3963 kCGImageAlphaPremultipliedFirst | kCGImageByteOrder32Little);
3965 CGContextTranslateCTM(pCGContext, 0, nCanvasHeight);
3966 CGContextScaleCTM(pCGContext, fDPIScale, -fDPIScale);
3968 SAL_INFO( "lok.tiledrendering", "doc_paintTile: painting [" << nTileWidth << "x" << nTileHeight <<
3969 "]@(" << nTilePosX << ", " << nTilePosY << ") to [" <<
3970 nCanvasWidth << "x" << nCanvasHeight << "]px" );
3972 Size aCanvasSize(nCanvasWidth, nCanvasHeight);
3974 SystemGraphicsData aData;
3975 aData.rCGContext = reinterpret_cast<CGContextRef>(pCGContext);
3977 ScopedVclPtrInstance<VirtualDevice> pDevice(aData, Size(1, 1), DeviceFormat::WITHOUT_ALPHA);
3978 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3979 pDevice->SetOutputSizePixel(aCanvasSize);
3980 pDoc->paintTile(*pDevice, aCanvasSize.Width(), aCanvasSize.Height(),
3981 nTilePosX, nTilePosY, nTileWidth, nTileHeight);
3983 CGContextRelease(pCGContext);
3984 #else
3985 ScopedVclPtrInstance< VirtualDevice > pDevice(DeviceFormat::WITHOUT_ALPHA);
3987 // Set background to transparent by default.
3988 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3990 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(
3991 Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(),
3992 pBuffer);
3994 pDoc->paintTile(*pDevice, nCanvasWidth, nCanvasHeight,
3995 nTilePosX, nTilePosY, nTileWidth, nTileHeight);
3997 static bool bDebug = getenv("LOK_DEBUG_TILES") != nullptr;
3998 if (bDebug)
4000 // Draw a small red rectangle in the top left corner so that it's easy to see where a new tile begins.
4001 tools::Rectangle aRect(0, 0, 5, 5);
4002 aRect = pDevice->PixelToLogic(aRect);
4003 pDevice->Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
4004 pDevice->SetFillColor(COL_LIGHTRED);
4005 pDevice->SetLineColor();
4006 pDevice->DrawRect(aRect);
4007 pDevice->Pop();
4010 #ifdef _WIN32
4011 // pBuffer was not used there
4012 pDevice->EnableMapMode(false);
4013 BitmapEx aBmpEx = pDevice->GetBitmapEx({ 0, 0 }, { nCanvasWidth, nCanvasHeight });
4014 Bitmap aBmp = aBmpEx.GetBitmap();
4015 Bitmap aAlpha = aBmpEx.GetAlphaMask();
4016 Bitmap::ScopedReadAccess sraBmp(aBmp);
4017 Bitmap::ScopedReadAccess sraAlpha(aAlpha);
4019 assert(sraBmp->Height() == nCanvasHeight);
4020 assert(sraBmp->Width() == nCanvasWidth);
4021 assert(!sraAlpha || sraBmp->Height() == sraAlpha->Height());
4022 assert(!sraAlpha || sraBmp->Width() == sraAlpha->Width());
4023 auto p = pBuffer;
4024 for (tools::Long y = 0; y < sraBmp->Height(); ++y)
4026 Scanline dataBmp = sraBmp->GetScanline(y);
4027 Scanline dataAlpha = sraAlpha ? sraAlpha->GetScanline(y) : nullptr;
4028 for (tools::Long x = 0; x < sraBmp->Width(); ++x)
4030 BitmapColor color = sraBmp->GetPixelFromData(dataBmp, x);
4031 sal_uInt8 alpha = dataAlpha ? sraAlpha->GetPixelFromData(dataAlpha, x).GetBlue() : 255;
4032 *p++ = color.GetBlue();
4033 *p++ = color.GetGreen();
4034 *p++ = color.GetRed();
4035 *p++ = alpha;
4038 #endif
4039 #endif
4041 #else
4042 (void) pBuffer;
4043 #endif
4046 static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
4047 unsigned char* pBuffer,
4048 const int nPart,
4049 const int nMode,
4050 const int nCanvasWidth, const int nCanvasHeight,
4051 const int nTilePosX, const int nTilePosY,
4052 const int nTileWidth, const int nTileHeight)
4054 comphelper::ProfileZone aZone("doc_paintPartTile");
4056 SolarMutexGuard aGuard;
4057 SetLastExceptionMsg();
4059 SAL_INFO( "lok.tiledrendering", "paintPartTile: painting @ " << nPart << " : " << nMode << " ["
4060 << nTileWidth << "x" << nTileHeight << "]@("
4061 << nTilePosX << ", " << nTilePosY << ") to ["
4062 << nCanvasWidth << "x" << nCanvasHeight << "]px" );
4064 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4065 int nOrigViewId = doc_getView(pThis);
4067 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4068 if (!pDoc)
4070 SetLastExceptionMsg("Document doesn't support tiled rendering");
4071 return;
4074 if (nOrigViewId < 0)
4076 // tile painting always needs a SfxViewShell::Current(), but actually
4077 // it does not really matter which one - all of them should paint the
4078 // same thing. It's important to get a view for the correct document,
4079 // though.
4080 // doc_getViewsCount() returns the count of views for the document in the current view.
4081 int viewCount = doc_getViewsCount(pThis);
4082 if (viewCount == 0)
4083 return;
4085 std::vector<int> viewIds(viewCount);
4086 doc_getViewIds(pThis, viewIds.data(), viewCount);
4088 nOrigViewId = viewIds[0];
4089 doc_setView(pThis, nOrigViewId);
4092 // Disable callbacks while we are painting.
4093 if (nOrigViewId >= 0)
4095 const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId);
4096 if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
4097 handlerIt->second->disableCallbacks();
4102 // Text documents have a single coordinate system; don't change part.
4103 int nOrigPart = 0;
4104 const int aType = doc_getDocumentType(pThis);
4105 const bool isText = (aType == LOK_DOCTYPE_TEXT);
4106 const bool isCalc = (aType == LOK_DOCTYPE_SPREADSHEET);
4107 int nOrigEditMode = 0;
4108 bool bPaintTextEdit = true;
4109 int nViewId = nOrigViewId;
4110 int nLastNonEditorView = -1;
4111 int nViewMatchingMode = -1;
4112 if (!isText)
4114 // Check if just switching to another view is enough, that has
4115 // less side-effects.
4116 if (nPart != doc_getPart(pThis) || nMode != pDoc->getEditMode())
4118 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
4119 while (pViewShell)
4121 bool bIsInEdit = pViewShell->GetDrawView() &&
4122 pViewShell->GetDrawView()->GetTextEditOutliner();
4123 if (!bIsInEdit)
4124 nLastNonEditorView = pViewShell->GetViewShellId().get();
4126 if (pViewShell->getPart() == nPart &&
4127 pViewShell->getEditMode() == nMode &&
4128 !bIsInEdit)
4130 nViewId = pViewShell->GetViewShellId().get();
4131 nViewMatchingMode = nViewId;
4132 nLastNonEditorView = nViewId;
4133 doc_setView(pThis, nViewId);
4134 break;
4136 else if (pViewShell->getEditMode() == nMode && !bIsInEdit)
4138 nViewMatchingMode = pViewShell->GetViewShellId().get();
4141 pViewShell = SfxViewShell::GetNext(*pViewShell);
4145 // if not found view with correct part
4146 // - at least avoid rendering active textbox, This is for Impress.
4147 // - prefer view with the same mode
4148 SfxViewShell* pCurrentViewShell = SfxViewShell::Current();
4149 if (nViewMatchingMode >= 0 && nViewMatchingMode != nViewId)
4151 nViewId = nViewMatchingMode;
4152 doc_setView(pThis, nViewId);
4154 else if (!isCalc && nLastNonEditorView >= 0 && nLastNonEditorView != nViewId &&
4155 pCurrentViewShell && pCurrentViewShell->GetDrawView() &&
4156 pCurrentViewShell->GetDrawView()->GetTextEditOutliner())
4158 nViewId = nLastNonEditorView;
4159 doc_setView(pThis, nViewId);
4162 // Disable callbacks while we are painting - after setting the view
4163 if (nViewId != nOrigViewId && nViewId >= 0)
4165 const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nViewId);
4166 if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
4167 handlerIt->second->disableCallbacks();
4170 nOrigPart = doc_getPart(pThis);
4171 if (nPart != nOrigPart)
4173 doc_setPartImpl(pThis, nPart, false);
4176 nOrigEditMode = pDoc->getEditMode();
4177 if (nOrigEditMode != nMode)
4179 SfxLokHelper::setEditMode(nMode, pDoc);
4182 bPaintTextEdit = (nPart == nOrigPart && nMode == nOrigEditMode);
4183 pDoc->setPaintTextEdit(bPaintTextEdit);
4186 doc_paintTile(pThis, pBuffer, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
4188 if (!isText)
4190 pDoc->setPaintTextEdit(true);
4192 if (nMode != nOrigEditMode)
4194 SfxLokHelper::setEditMode(nOrigEditMode, pDoc);
4197 if (nPart != nOrigPart)
4199 doc_setPartImpl(pThis, nOrigPart, false);
4202 if (nViewId != nOrigViewId)
4204 if (nViewId >= 0)
4206 const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nViewId);
4207 if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
4208 handlerIt->second->enableCallbacks();
4211 doc_setView(pThis, nOrigViewId);
4215 catch (const std::exception&)
4217 // Nothing to do but restore the PartTilePainting flag.
4220 if (nOrigViewId >= 0)
4222 const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId);
4223 if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
4224 handlerIt->second->enableCallbacks();
4228 static int doc_getTileMode(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
4230 SetLastExceptionMsg();
4231 return LOK_TILEMODE_BGRA;
4234 static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
4235 long* pWidth,
4236 long* pHeight)
4238 comphelper::ProfileZone aZone("doc_getDocumentSize");
4240 SolarMutexGuard aGuard;
4241 SetLastExceptionMsg();
4243 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4244 if (pDoc)
4246 Size aDocumentSize = pDoc->getDocumentSize();
4247 *pWidth = aDocumentSize.Width();
4248 *pHeight = aDocumentSize.Height();
4250 else
4252 SetLastExceptionMsg("Document doesn't support tiled rendering");
4256 static void doc_getDataArea(LibreOfficeKitDocument* pThis,
4257 long nTab,
4258 long* pCol,
4259 long* pRow)
4261 comphelper::ProfileZone aZone("doc_getDataArea");
4263 SolarMutexGuard aGuard;
4264 SetLastExceptionMsg();
4266 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4267 if (pDoc)
4269 Size aDocumentSize = pDoc->getDataArea(nTab);
4270 *pCol = aDocumentSize.Width();
4271 *pRow = aDocumentSize.Height();
4273 else
4275 SetLastExceptionMsg("Document doesn't support tiled rendering");
4279 static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
4280 const char* pArguments)
4282 comphelper::ProfileZone aZone("doc_initializeForRendering");
4284 SolarMutexGuard aGuard;
4285 SetLastExceptionMsg();
4287 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4288 if (pDoc)
4290 doc_iniUnoCommands();
4291 pDoc->initializeForTiledRendering(
4292 comphelper::containerToSequence(jsonToPropertyValuesVector(pArguments)));
4296 static void doc_registerCallback(LibreOfficeKitDocument* pThis,
4297 LibreOfficeKitCallback pCallback,
4298 void* pData)
4300 SolarMutexGuard aGuard;
4301 SetLastExceptionMsg();
4303 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4305 const int nView = SfxLokHelper::getView();
4306 if (nView < 0)
4307 return;
4309 const size_t nId = nView;
4310 if (pCallback != nullptr)
4312 for (auto& pair : pDocument->mpCallbackFlushHandlers)
4314 if (pair.first == nId)
4315 continue;
4317 pair.second->addViewStates(nView);
4320 else
4322 for (auto& pair : pDocument->mpCallbackFlushHandlers)
4324 if (pair.first == nId)
4325 continue;
4327 pair.second->removeViewStates(nView);
4331 pDocument->mpCallbackFlushHandlers[nView] = std::make_shared<CallbackFlushHandler>(pThis, pCallback, pData);
4333 if (pCallback != nullptr)
4335 for (const auto& pair : pDocument->mpCallbackFlushHandlers)
4337 if (pair.first == nId)
4338 continue;
4340 pDocument->mpCallbackFlushHandlers[nView]->addViewStates(pair.first);
4343 if (SfxViewShell* pViewShell = SfxViewShell::Current())
4345 pDocument->mpCallbackFlushHandlers[nView]->setViewId(pViewShell->GetViewShellId().get());
4346 pViewShell->setLibreOfficeKitViewCallback(pDocument->mpCallbackFlushHandlers[nView].get());
4349 if (pDocument->maFontsMissing.size() != 0)
4351 std::string sPayload = "{ \"fontsmissing\": [ ";
4352 bool bFirst = true;
4353 for (const auto &f : pDocument->maFontsMissing)
4355 if (bFirst)
4356 bFirst = false;
4357 else
4358 sPayload += ", ";
4359 sPayload += "\"" + std::string(f.toUtf8()) + "\"";
4361 sPayload += " ] }";
4362 pCallback(LOK_CALLBACK_FONTS_MISSING, sPayload.c_str(), pData);
4363 pDocument->maFontsMissing.clear();
4366 else
4368 if (SfxViewShell* pViewShell = SfxViewShell::Current())
4370 pViewShell->setLibreOfficeKitViewCallback(nullptr);
4371 pDocument->mpCallbackFlushHandlers[nView]->setViewId(-1);
4376 /// Returns the JSON representation of all the comments in the document
4377 static char* getPostIts(LibreOfficeKitDocument* pThis)
4379 SetLastExceptionMsg();
4380 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4381 if (!pDoc)
4383 SetLastExceptionMsg("Document doesn't support tiled rendering");
4384 return nullptr;
4386 tools::JsonWriter aJsonWriter;
4387 pDoc->getPostIts(aJsonWriter);
4388 return strdup(aJsonWriter.finishAndGetAsOString().getStr());
4391 /// Returns the JSON representation of the positions of all the comments in the document
4392 static char* getPostItsPos(LibreOfficeKitDocument* pThis)
4394 SetLastExceptionMsg();
4395 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4396 if (!pDoc)
4398 SetLastExceptionMsg("Document doesn't support tiled rendering");
4399 return nullptr;
4401 tools::JsonWriter aJsonWriter;
4402 pDoc->getPostItsPos(aJsonWriter);
4403 return strdup(aJsonWriter.finishAndGetAsOString().getStr());
4406 static char* getRulerState(LibreOfficeKitDocument* pThis)
4408 SetLastExceptionMsg();
4409 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4410 if (!pDoc)
4412 SetLastExceptionMsg("Document doesn't support tiled rendering");
4413 return nullptr;
4415 tools::JsonWriter aJsonWriter;
4416 pDoc->getRulerState(aJsonWriter);
4417 return strdup(aJsonWriter.finishAndGetAsOString().getStr());
4420 static void doc_postKeyEvent(LibreOfficeKitDocument* pThis, int nType, int nCharCode, int nKeyCode)
4422 comphelper::ProfileZone aZone("doc_postKeyEvent");
4424 SolarMutexGuard aGuard;
4425 SetLastExceptionMsg();
4427 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4428 if (!pDoc)
4430 SetLastExceptionMsg("Document doesn't support tiled rendering");
4431 return;
4436 pDoc->postKeyEvent(nType, nCharCode, nKeyCode);
4438 catch (const uno::Exception& exception)
4440 SetLastExceptionMsg(exception.Message);
4441 SAL_INFO("lok", "Failed to postKeyEvent " << exception.Message);
4445 static void doc_setBlockedCommandList(LibreOfficeKitDocument* /*pThis*/, int nViewId, const char* blockedCommandList)
4447 SolarMutexGuard aGuard;
4448 SfxLokHelper::setBlockedCommandList(nViewId, blockedCommandList);
4451 static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis, unsigned nWindowId, int nType, const char* pText)
4453 comphelper::ProfileZone aZone("doc_postWindowExtTextInputEvent");
4455 SolarMutexGuard aGuard;
4456 VclPtr<vcl::Window> pWindow;
4457 if (nWindowId == 0)
4459 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4460 if (!pDoc)
4462 SetLastExceptionMsg("Document doesn't support tiled rendering");
4463 return;
4465 pWindow = pDoc->getDocWindow();
4467 else
4469 pWindow = vcl::Window::FindLOKWindow(nWindowId);
4472 if (!pWindow)
4474 SetLastExceptionMsg("No window found for window id: " + OUString::number(nWindowId));
4475 return;
4478 SfxLokHelper::postExtTextEventAsync(pWindow, nType, OUString::fromUtf8(std::string_view(pText, strlen(pText))));
4481 static void doc_removeTextContext(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, int nCharBefore, int nCharAfter)
4483 SolarMutexGuard aGuard;
4484 VclPtr<vcl::Window> pWindow;
4485 if (nLOKWindowId == 0)
4487 ITiledRenderable* pDoc = getTiledRenderable(pThis);
4488 if (!pDoc)
4490 gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
4491 return;
4493 pWindow = pDoc->getDocWindow();
4495 else
4497 pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
4500 if (!pWindow)
4502 gImpl->maLastExceptionMsg = "No window found for window id: " + OUString::number(nLOKWindowId);
4503 return;
4506 // Annoyingly - backspace and delete are handled in the apps via an accelerator
4507 // which are PostMessage'd by SfxViewShell::ExecKey_Impl so to stay in the same
4508 // order we do this synchronously here, unless we're in a dialog.
4509 if (nCharBefore > 0)
4511 // backspace
4512 if (nLOKWindowId == 0)
4514 KeyEvent aEvt(8, 1283);
4515 for (int i = 0; i < nCharBefore; ++i)
4516 pWindow->KeyInput(aEvt);
4518 else
4519 SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 8, 1283, nCharBefore - 1);
4522 if (nCharAfter > 0)
4524 // delete (forward)
4525 if (nLOKWindowId == 0)
4527 KeyEvent aEvt(46, 1286);
4528 for (int i = 0; i < nCharAfter; ++i)
4529 pWindow->KeyInput(aEvt);
4531 else
4532 SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 46, 1286, nCharAfter - 1);
4536 static void doc_postWindowKeyEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nCharCode, int nKeyCode)
4538 comphelper::ProfileZone aZone("doc_postWindowKeyEvent");
4540 SolarMutexGuard aGuard;
4541 SetLastExceptionMsg();
4543 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
4544 if (!pWindow)
4546 SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
4547 return;
4550 KeyEvent aEvent(nCharCode, nKeyCode, 0);
4552 switch (nType)
4554 case LOK_KEYEVENT_KEYINPUT:
4555 Application::PostKeyEvent(VclEventId::WindowKeyInput, pWindow, &aEvent);
4556 break;
4557 case LOK_KEYEVENT_KEYUP:
4558 Application::PostKeyEvent(VclEventId::WindowKeyUp, pWindow, &aEvent);
4559 break;
4560 default:
4561 assert(false);
4562 break;
4566 static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput)
4568 comphelper::ProfileZone aZone("doc_renderShapeSelection");
4570 SolarMutexGuard aGuard;
4571 SetLastExceptionMsg();
4573 LokChartHelper aChartHelper(SfxViewShell::Current());
4575 if (aChartHelper.GetWindow())
4576 return 0;
4580 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4582 uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
4584 SvMemoryStream aOutStream;
4585 uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aOutStream);
4587 utl::MediaDescriptor aMediaDescriptor;
4588 switch (doc_getDocumentType(pThis))
4590 case LOK_DOCTYPE_PRESENTATION:
4591 aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export");
4592 break;
4593 case LOK_DOCTYPE_DRAWING:
4594 aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export");
4595 break;
4596 case LOK_DOCTYPE_TEXT:
4597 aMediaDescriptor["FilterName"] <<= OUString("writer_svg_Export");
4598 break;
4599 case LOK_DOCTYPE_SPREADSHEET:
4600 aMediaDescriptor["FilterName"] <<= OUString("calc_svg_Export");
4601 break;
4602 default:
4603 SAL_WARN("lok", "Failed to render shape selection: Document type is not supported");
4605 aMediaDescriptor["SelectionOnly"] <<= true;
4606 aMediaDescriptor["OutputStream"] <<= xOut;
4607 aMediaDescriptor["IsPreview"] <<= true; // will down-scale graphics
4609 xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
4611 if (pOutput)
4613 const size_t nOutputSize = aOutStream.GetEndOfData();
4614 *pOutput = static_cast<char*>(malloc(nOutputSize));
4615 if (*pOutput)
4617 std::memcpy(*pOutput, aOutStream.GetData(), nOutputSize);
4618 return nOutputSize;
4622 catch (const uno::Exception& exception)
4624 css::uno::Any exAny( cppu::getCaughtException() );
4625 SetLastExceptionMsg(exception.Message);
4626 SAL_WARN("lok", "Failed to render shape selection: " << exceptionToString(exAny));
4629 return 0;
4632 namespace {
4634 /** Class to react on finishing of a dispatched command.
4636 This will call a LOK_COMMAND_FINISHED callback when postUnoCommand was
4637 called with the parameter requesting the notification.
4639 @see LibreOfficeKitCallbackType::LOK_CALLBACK_UNO_COMMAND_RESULT.
4641 class DispatchResultListener : public cppu::WeakImplHelper<css::frame::XDispatchResultListener>
4643 const OString maCommand; ///< Command for which this is the result.
4644 const std::shared_ptr<CallbackFlushHandler> mpCallback; ///< Callback to call.
4645 const std::chrono::steady_clock::time_point mSaveTime; //< The time we started saving.
4646 const bool mbWasModified; //< Whether or not the document was modified before saving.
4648 public:
4649 DispatchResultListener(const char* pCommand, std::shared_ptr<CallbackFlushHandler> pCallback)
4650 : maCommand(pCommand)
4651 , mpCallback(std::move(pCallback))
4652 , mSaveTime(std::chrono::steady_clock::now())
4653 , mbWasModified(SfxObjectShell::Current()->IsModified())
4655 assert(mpCallback);
4658 virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& rEvent) override
4660 tools::JsonWriter aJson;
4661 aJson.put("commandName", maCommand);
4663 if (rEvent.State != frame::DispatchResultState::DONTKNOW)
4665 bool bSuccess = (rEvent.State == frame::DispatchResultState::SUCCESS);
4666 aJson.put("success", bSuccess);
4669 unoAnyToJson(aJson, "result", rEvent.Result);
4670 aJson.put("wasModified", mbWasModified);
4671 aJson.put("startUnixTimeMics",
4672 std::chrono::time_point_cast<std::chrono::microseconds>(mSaveTime)
4673 .time_since_epoch()
4674 .count());
4675 aJson.put("saveDurationMics", std::chrono::duration_cast<std::chrono::microseconds>(
4676 std::chrono::steady_clock::now() - mSaveTime)
4677 .count());
4678 mpCallback->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.finishAndGetAsOString());
4681 virtual void SAL_CALL disposing(const css::lang::EventObject&) override {}
4684 } // anonymous namespace
4687 static void lcl_sendDialogEvent(unsigned long long int nWindowId, const char* pArguments)
4689 SolarMutexGuard aGuard;
4691 StringMap aMap(jsdialog::jsonToStringMap(pArguments));
4693 if (aMap.find("id") == aMap.end())
4694 return;
4696 sal_uInt64 nCurrentShellId = reinterpret_cast<sal_uInt64>(SfxViewShell::Current());
4700 OUString sControlId = aMap["id"];
4702 // dialogs send own id but notebookbar and sidebar controls are remembered by SfxViewShell id
4703 if (jsdialog::ExecuteAction(OUString::number(nWindowId), sControlId, aMap))
4704 return;
4705 auto sCurrentShellId = OUString::number(nCurrentShellId);
4706 if (jsdialog::ExecuteAction(sCurrentShellId + "sidebar", sControlId, aMap))
4707 return;
4708 if (jsdialog::ExecuteAction(sCurrentShellId + "notebookbar", sControlId, aMap))
4709 return;
4710 if (jsdialog::ExecuteAction(sCurrentShellId + "formulabar", sControlId, aMap))
4711 return;
4712 // this is needed for dialogs shown before document is loaded: MacroWarning dialog, etc...
4713 // these dialogs are created with WindowId "0"
4714 if (!SfxViewShell::Current() && jsdialog::ExecuteAction("0", sControlId, aMap))
4715 return;
4717 // force resend - used in mobile-wizard
4718 jsdialog::SendFullUpdate(sCurrentShellId + "sidebar", "Panel");
4720 } catch(...) {}
4724 static void doc_sendDialogEvent(LibreOfficeKitDocument* /*pThis*/, unsigned long long int nWindowId, const char* pArguments)
4726 lcl_sendDialogEvent(nWindowId, pArguments);
4729 static void lo_sendDialogEvent(LibreOfficeKit* /*pThis*/, unsigned long long int nWindowId, const char* pArguments)
4731 lcl_sendDialogEvent(nWindowId, pArguments);
4734 static void lo_setOption(LibreOfficeKit* /*pThis*/, const char *pOption, const char* pValue)
4736 static char* pCurrentSalLogOverride = nullptr;
4738 if (strcmp(pOption, "traceeventrecording") == 0)
4740 if (strcmp(pValue, "start") == 0)
4742 comphelper::TraceEvent::setBufferSizeAndCallback(100, TraceEventDumper::flushRecordings);
4743 comphelper::TraceEvent::startRecording();
4744 if (traceEventDumper == nullptr)
4745 traceEventDumper = new TraceEventDumper();
4747 else if (strcmp(pValue, "stop") == 0)
4748 comphelper::TraceEvent::stopRecording();
4750 else if (strcmp(pOption, "sallogoverride") == 0)
4752 if (pCurrentSalLogOverride != nullptr)
4753 free(pCurrentSalLogOverride);
4754 if (pValue == nullptr)
4755 pCurrentSalLogOverride = nullptr;
4756 else
4757 pCurrentSalLogOverride = strdup(pValue);
4759 if (pCurrentSalLogOverride == nullptr || pCurrentSalLogOverride[0] == '\0')
4760 sal_detail_set_log_selector(nullptr);
4761 else
4762 sal_detail_set_log_selector(pCurrentSalLogOverride);
4764 #ifdef LINUX
4765 else if (strcmp(pOption, "addfont") == 0)
4767 if (memcmp(pValue, "file://", 7) == 0)
4768 pValue += 7;
4770 int fd = open(pValue, O_RDONLY);
4771 if (fd == -1)
4773 std::cerr << "Could not open font file '" << pValue << "': " << strerror(errno) << std::endl;
4774 return;
4777 OUString sMagicFileName = "file:///:FD:/" + OUString::number(fd);
4779 OutputDevice *pDevice = Application::GetDefaultDevice();
4780 OutputDevice::ImplClearAllFontData(false);
4781 pDevice->AddTempDevFont(sMagicFileName, "");
4782 OutputDevice::ImplRefreshAllFontData(false);
4784 #endif
4787 static void lo_dumpState (LibreOfficeKit* pThis, const char* /* pOptions */, char** pState)
4789 if (!pState)
4790 return;
4792 // NB. no SolarMutexGuard since this may be caused in some extremis / deadlock
4793 SetLastExceptionMsg();
4795 *pState = nullptr;
4796 OStringBuffer aState(4096*256);
4798 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
4800 pLib->dumpState(aState);
4802 OString aStr = aState.makeStringAndClear();
4803 *pState = strdup(aStr.getStr());
4806 void LibLibreOffice_Impl::dumpState(rtl::OStringBuffer &rState)
4808 rState.append("LibreOfficeKit state:");
4809 rState.append("\n\tLastExceptionMsg:\t");
4810 rState.append(rtl::OUStringToOString(maLastExceptionMsg, RTL_TEXTENCODING_UTF8));
4811 rState.append("\n\tUnipoll:\t");
4812 rState.append(vcl::lok::isUnipoll() ? "yes" : "no: events on thread");
4813 rState.append("\n\tOptionalFeatures:\t0x");
4814 rState.append(static_cast<sal_Int64>(mOptionalFeatures), 16);
4815 rState.append("\n\tCallbackData:\t0x");
4816 rState.append(reinterpret_cast<sal_Int64>(mpCallback), 16);
4817 // TODO: dump mInteractionMap
4818 SfxLokHelper::dumpState(rState);
4819 vcl::lok::dumpState(rState);
4822 static void doc_postUnoCommand(LibreOfficeKitDocument* pThis, const char* pCommand, const char* pArguments, bool bNotifyWhenFinished)
4824 comphelper::ProfileZone aZone("doc_postUnoCommand");
4826 SolarMutexGuard aGuard;
4827 SetLastExceptionMsg();
4829 SfxObjectShell* pDocSh = SfxObjectShell::Current();
4830 OUString aCommand(pCommand, strlen(pCommand), RTL_TEXTENCODING_UTF8);
4831 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4833 std::vector<beans::PropertyValue> aPropertyValuesVector(jsonToPropertyValuesVector(pArguments));
4835 if (!vcl::lok::isUnipoll())
4837 beans::PropertyValue aSynchronMode;
4838 aSynchronMode.Name = "SynchronMode";
4839 aSynchronMode.Value <<= false;
4840 aPropertyValuesVector.push_back(aSynchronMode);
4843 int nView = SfxLokHelper::getView();
4844 if (nView < 0)
4845 return;
4847 if (gImpl && aCommand == ".uno:ToggleOrientation")
4849 ExecuteOrientationChange();
4850 return;
4853 // handle potential interaction
4854 if (gImpl && aCommand == ".uno:Save")
4856 // Check if saving a PDF file
4857 OUString aMimeType = lcl_getCurrentDocumentMimeType(pDocument);
4858 if (pDocSh && pDocSh->IsModified() && aMimeType == "application/pdf")
4860 // If we have a PDF file (for saving annotations for example), we need
4861 // to run save-as to the same file as the opened document. Plain save
4862 // doesn't work as the PDF is not a "native" format.
4863 uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
4864 OUString aURL = xStorable->getLocation();
4865 OString aURLUtf8 = OUStringToOString(aURL, RTL_TEXTENCODING_UTF8);
4866 bool bResult = doc_saveAs(pThis, aURLUtf8.getStr(), "pdf", nullptr);
4868 // Send the result of save
4869 tools::JsonWriter aJson;
4870 aJson.put("commandName", pCommand);
4871 aJson.put("success", bResult);
4872 pDocument->mpCallbackFlushHandlers[nView]->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.finishAndGetAsOString());
4873 return;
4877 rtl::Reference<LOKInteractionHandler> const pInteraction(
4878 new LOKInteractionHandler("save", gImpl, pDocument));
4879 uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction);
4881 beans::PropertyValue aValue;
4882 aValue.Name = "InteractionHandler";
4883 aValue.Value <<= xInteraction;
4884 aPropertyValuesVector.push_back(aValue);
4886 bool bDontSaveIfUnmodified = false;
4887 aPropertyValuesVector.erase(std::remove_if(aPropertyValuesVector.begin(),
4888 aPropertyValuesVector.end(),
4889 [&bDontSaveIfUnmodified](const beans::PropertyValue& aItem){
4890 if (aItem.Name == "DontSaveIfUnmodified")
4892 bDontSaveIfUnmodified = aItem.Value.get<bool>();
4893 return true;
4895 return false;
4896 }), aPropertyValuesVector.end());
4898 // skip saving and tell the result via UNO_COMMAND_RESULT
4899 if (bDontSaveIfUnmodified && (!pDocSh || !pDocSh->IsModified()))
4901 tools::JsonWriter aJson;
4902 aJson.put("commandName", pCommand);
4903 aJson.put("success", false);
4904 // Add the reason for not saving
4906 auto resultNode = aJson.startNode("result");
4907 aJson.put("type", "string");
4908 aJson.put("value", "unmodified");
4910 pDocument->mpCallbackFlushHandlers[nView]->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.finishAndGetAsOString());
4911 return;
4914 else if (gImpl && aCommand == ".uno:TransformDialog")
4916 bool bNeedConversion = false;
4917 SfxViewShell* pViewShell = SfxViewShell::Current();
4918 LokChartHelper aChartHelper(pViewShell);
4920 if (aChartHelper.GetWindow() )
4922 bNeedConversion = true;
4924 else if (const SdrView* pView = pViewShell->GetDrawView())
4926 if (OutputDevice* pOutputDevice = pView->GetFirstOutputDevice())
4928 bNeedConversion = (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM);
4932 if (bNeedConversion)
4934 sal_Int32 value;
4935 for (beans::PropertyValue& rPropValue: aPropertyValuesVector)
4937 if (rPropValue.Name == "TransformPosX"
4938 || rPropValue.Name == "TransformPosY"
4939 || rPropValue.Name == "TransformWidth"
4940 || rPropValue.Name == "TransformHeight"
4941 || rPropValue.Name == "TransformRotationX"
4942 || rPropValue.Name == "TransformRotationY")
4944 rPropValue.Value >>= value;
4945 value = o3tl::convert(value, o3tl::Length::twip, o3tl::Length::mm100);
4946 rPropValue.Value <<= value;
4951 if (aChartHelper.GetWindow() && aPropertyValuesVector.size() > 0)
4953 if (aPropertyValuesVector[0].Name != "Action")
4955 tools::Rectangle aChartBB = aChartHelper.GetChartBoundingBox();
4957 int nLeft = o3tl::convert(aChartBB.Left(), o3tl::Length::twip, o3tl::Length::mm100);
4958 int nTop = o3tl::convert(aChartBB.Top(), o3tl::Length::twip, o3tl::Length::mm100);
4960 for (beans::PropertyValue& rPropValue: aPropertyValuesVector)
4962 if (rPropValue.Name == "TransformPosX" || rPropValue.Name == "TransformRotationX")
4964 auto const value = *o3tl::doAccess<sal_Int32>(rPropValue.Value);
4965 rPropValue.Value <<= value - nLeft;
4967 else if (rPropValue.Name == "TransformPosY" || rPropValue.Name == "TransformRotationY")
4969 auto const value = *o3tl::doAccess<sal_Int32>(rPropValue.Value);
4970 rPropValue.Value <<= value - nTop;
4974 util::URL aCommandURL;
4975 aCommandURL.Path = "LOKTransform";
4976 css::uno::Reference<css::frame::XDispatch>& aChartDispatcher = aChartHelper.GetXDispatcher();
4977 aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector));
4978 return;
4981 else if (gImpl && aCommand == ".uno:LOKSidebarWriterPage")
4983 setupSidebar(u"WriterPageDeck");
4984 return;
4986 else if (gImpl && aCommand == ".uno:SidebarShow")
4988 setupSidebar();
4989 return;
4991 else if (gImpl && aCommand == ".uno:SidebarHide")
4993 hideSidebar();
4994 return;
4997 bool bResult = false;
4998 LokChartHelper aChartHelper(SfxViewShell::Current());
5000 if (aChartHelper.GetWindow() && aCommand != ".uno:Save" )
5002 util::URL aCommandURL;
5003 aCommandURL.Path = aCommand.copy(5);
5004 css::uno::Reference<css::frame::XDispatch>& aChartDispatcher = aChartHelper.GetXDispatcher();
5005 aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector));
5006 return;
5008 if (LokStarMathHelper aMathHelper(SfxViewShell::Current());
5009 aMathHelper.GetGraphicWindow() && aCommand != ".uno:Save")
5011 aMathHelper.Dispatch(aCommand, comphelper::containerToSequence(aPropertyValuesVector));
5012 return;
5014 if (bNotifyWhenFinished && pDocument->mpCallbackFlushHandlers.count(nView))
5016 bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector),
5017 new DispatchResultListener(pCommand, pDocument->mpCallbackFlushHandlers[nView]));
5019 else
5020 bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector));
5022 if (!bResult)
5024 SetLastExceptionMsg("Failed to dispatch " + aCommand);
5028 static void doc_postMouseEvent(LibreOfficeKitDocument* pThis, int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
5030 comphelper::ProfileZone aZone("doc_postMouseEvent");
5032 SolarMutexGuard aGuard;
5033 SetLastExceptionMsg();
5035 ITiledRenderable* pDoc = getTiledRenderable(pThis);
5036 if (!pDoc)
5038 SetLastExceptionMsg("Document doesn't support tiled rendering");
5039 return;
5043 pDoc->postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier);
5045 catch (const uno::Exception& exception)
5047 SetLastExceptionMsg(exception.Message);
5048 SAL_INFO("lok", "Failed to postMouseEvent " << exception.Message);
5052 static void doc_postWindowMouseEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
5054 comphelper::ProfileZone aZone("doc_postWindowMouseEvent");
5056 SolarMutexGuard aGuard;
5057 SetLastExceptionMsg();
5059 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
5060 if (!pWindow)
5062 SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
5063 return;
5066 const Point aPos(nX, nY);
5068 MouseEvent aEvent(aPos, nCount, MouseEventModifiers::SIMPLECLICK, nButtons, nModifier);
5070 vcl::EnableDialogInput(pWindow);
5072 switch (nType)
5074 case LOK_MOUSEEVENT_MOUSEBUTTONDOWN:
5075 Application::PostMouseEvent(VclEventId::WindowMouseButtonDown, pWindow, &aEvent);
5076 break;
5077 case LOK_MOUSEEVENT_MOUSEBUTTONUP:
5078 Application::PostMouseEvent(VclEventId::WindowMouseButtonUp, pWindow, &aEvent);
5079 break;
5080 case LOK_MOUSEEVENT_MOUSEMOVE:
5081 Application::PostMouseEvent(VclEventId::WindowMouseMove, pWindow, &aEvent);
5082 break;
5083 default:
5084 assert(false);
5085 break;
5089 static void doc_postWindowGestureEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, const char* pType, int nX, int nY, int nOffset)
5091 comphelper::ProfileZone aZone("doc_postWindowGestureEvent");
5093 SolarMutexGuard aGuard;
5094 SetLastExceptionMsg();
5096 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
5097 if (!pWindow)
5099 SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
5100 return;
5103 OString aType(pType);
5104 GestureEventPanType eEventType = GestureEventPanType::Update;
5106 if (aType == "panBegin")
5107 eEventType = GestureEventPanType::Begin;
5108 else if (aType == "panEnd")
5109 eEventType = GestureEventPanType::End;
5111 GestureEventPan aEvent {
5112 sal_Int32(nX),
5113 sal_Int32(nY),
5114 eEventType,
5115 sal_Int32(nOffset),
5116 PanningOrientation::Vertical,
5119 vcl::EnableDialogInput(pWindow);
5121 Application::PostGestureEvent(VclEventId::WindowGestureEvent, pWindow, &aEvent);
5124 static void doc_setTextSelection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY)
5126 comphelper::ProfileZone aZone("doc_setTextSelection");
5128 SolarMutexGuard aGuard;
5129 SetLastExceptionMsg();
5131 ITiledRenderable* pDoc = getTiledRenderable(pThis);
5132 if (!pDoc)
5134 SetLastExceptionMsg("Document doesn't support tiled rendering");
5135 return;
5138 pDoc->setTextSelection(nType, nX, nY);
5141 static void doc_setWindowTextSelection(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, bool swap, int nX, int nY)
5143 comphelper::ProfileZone aZone("doc_setWindowTextSelection");
5145 SolarMutexGuard aGuard;
5146 SetLastExceptionMsg();
5148 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
5149 if (!pWindow)
5151 SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
5152 return;
5156 Size aOffset(pWindow->GetOutOffXPixel(), pWindow->GetOutOffYPixel());
5157 Point aCursorPos(nX, nY);
5158 aCursorPos.Move(aOffset);
5159 sal_uInt16 nModifier = swap ? KEY_MOD1 + KEY_MOD2 : KEY_SHIFT;
5161 MouseEvent aCursorEvent(aCursorPos, 1, MouseEventModifiers::SIMPLECLICK, 0, nModifier);
5162 Application::PostMouseEvent(VclEventId::WindowMouseButtonDown, pWindow, &aCursorEvent);
5163 Application::PostMouseEvent(VclEventId::WindowMouseButtonUp, pWindow, &aCursorEvent);
5166 static bool getFromTransferable(
5167 const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
5168 const OString &aInMimeType, OString &aRet);
5170 static bool encodeImageAsHTML(
5171 const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
5172 const OString &aMimeType, OString &aRet)
5174 if (!getFromTransferable(xTransferable, aMimeType, aRet))
5175 return false;
5177 // Encode in base64.
5178 auto aSeq = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()),
5179 aRet.getLength());
5180 OStringBuffer aBase64Data;
5181 comphelper::Base64::encode(aBase64Data, aSeq);
5183 // Embed in HTML.
5184 aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
5185 "<html><head>"
5186 "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta "
5187 "name=\"generator\" content=\""
5188 + getGenerator().toUtf8()
5189 + "\"/>"
5190 "</head><body><img src=\"data:" + aMimeType + ";base64,"
5191 + aBase64Data + "\"/></body></html>";
5193 return true;
5196 static bool encodeTextAsHTML(
5197 const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
5198 const OString &aMimeType, OString &aRet)
5200 if (!getFromTransferable(xTransferable, aMimeType, aRet))
5201 return false;
5203 // Embed in HTML - FIXME: needs some escaping.
5204 aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
5205 "<html><head>"
5206 "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta "
5207 "name=\"generator\" content=\""
5208 + getGenerator().toUtf8()
5209 + "\"/></head><body><pre>" + aRet + "</pre></body></html>";
5211 return true;
5214 static bool getFromTransferable(
5215 const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
5216 const OString &aInMimeType, OString &aRet)
5218 OString aMimeType(aInMimeType);
5220 // Take care of UTF-8 text here.
5221 bool bConvert = false;
5222 sal_Int32 nIndex = 0;
5223 if (o3tl::getToken(aMimeType, 0, ';', nIndex) == "text/plain")
5225 if (o3tl::getToken(aMimeType, 0, ';', nIndex) == "charset=utf-8")
5227 aMimeType = "text/plain;charset=utf-16";
5228 bConvert = true;
5232 datatransfer::DataFlavor aFlavor;
5233 aFlavor.MimeType = OUString::fromUtf8(aMimeType);
5234 if (aMimeType == "text/plain;charset=utf-16")
5235 aFlavor.DataType = cppu::UnoType<OUString>::get();
5236 else
5237 aFlavor.DataType = cppu::UnoType< uno::Sequence<sal_Int8> >::get();
5239 if (!xTransferable->isDataFlavorSupported(aFlavor))
5241 // Try harder for HTML it is our copy/paste meta-file format
5242 if (aInMimeType == "text/html")
5244 // Desperate measures - convert text to HTML instead.
5245 if (encodeTextAsHTML(xTransferable, "text/plain;charset=utf-8", aRet))
5246 return true;
5247 // If html is not supported, might be a graphic-selection,
5248 if (encodeImageAsHTML(xTransferable, "image/png", aRet))
5249 return true;
5252 SetLastExceptionMsg("Flavor " + aFlavor.MimeType + " is not supported");
5253 return false;
5256 uno::Any aAny;
5259 aAny = xTransferable->getTransferData(aFlavor);
5261 catch (const css::datatransfer::UnsupportedFlavorException& e)
5263 SetLastExceptionMsg("Unsupported flavor " + aFlavor.MimeType + " exception " + e.Message);
5264 return false;
5266 catch (const css::uno::Exception& e)
5268 SetLastExceptionMsg("Exception getting " + aFlavor.MimeType + " exception " + e.Message);
5269 return false;
5272 if (aFlavor.DataType == cppu::UnoType<OUString>::get())
5274 OUString aString;
5275 aAny >>= aString;
5276 if (bConvert)
5277 aRet = OUStringToOString(aString, RTL_TEXTENCODING_UTF8);
5278 else
5279 aRet = OString(reinterpret_cast<const char *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode));
5281 else
5283 uno::Sequence<sal_Int8> aSequence;
5284 aAny >>= aSequence;
5285 aRet = OString(reinterpret_cast<const char*>(aSequence.getConstArray()), aSequence.getLength());
5288 return true;
5291 static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pUsedMimeType)
5293 comphelper::ProfileZone aZone("doc_getTextSelection");
5295 SolarMutexGuard aGuard;
5296 SetLastExceptionMsg();
5298 ITiledRenderable* pDoc = getTiledRenderable(pThis);
5299 if (!pDoc)
5301 SetLastExceptionMsg("Document doesn't support tiled rendering");
5302 return nullptr;
5305 css::uno::Reference<css::datatransfer::XTransferable> xTransferable = pDoc->getSelection();
5306 if (!xTransferable)
5308 SetLastExceptionMsg("No selection available");
5309 return nullptr;
5312 const char *pType = pMimeType;
5313 if (!pType || pType[0] == '\0')
5314 pType = "text/plain;charset=utf-8";
5316 OString aRet;
5317 bool bSuccess = getFromTransferable(xTransferable, OString(pType), aRet);
5318 if (!bSuccess)
5319 return nullptr;
5321 if (pUsedMimeType) // legacy
5323 if (pMimeType)
5324 *pUsedMimeType = strdup(pMimeType);
5325 else
5326 *pUsedMimeType = nullptr;
5329 return convertOString(aRet);
5332 static int doc_getSelectionType(LibreOfficeKitDocument* pThis)
5334 comphelper::ProfileZone aZone("doc_getSelectionType");
5336 SolarMutexGuard aGuard;
5337 SetLastExceptionMsg();
5339 ITiledRenderable* pDoc = getTiledRenderable(pThis);
5340 if (!pDoc)
5342 SetLastExceptionMsg("Document doesn't support tiled rendering");
5343 return LOK_SELTYPE_NONE;
5346 css::uno::Reference<css::datatransfer::XTransferable> xTransferable = pDoc->getSelection();
5347 if (!xTransferable)
5349 SetLastExceptionMsg("No selection available");
5350 return LOK_SELTYPE_NONE;
5353 css::uno::Reference<css::datatransfer::XTransferable2> xTransferable2(xTransferable, css::uno::UNO_QUERY);
5354 if (xTransferable2.is() && xTransferable2->isComplex())
5355 return LOK_SELTYPE_COMPLEX;
5357 OString aRet;
5358 bool bSuccess = getFromTransferable(xTransferable, "text/plain;charset=utf-8", aRet);
5359 if (!bSuccess)
5360 return LOK_SELTYPE_NONE;
5362 if (aRet.getLength() > 10000)
5363 return LOK_SELTYPE_COMPLEX;
5365 return !aRet.isEmpty() ? LOK_SELTYPE_TEXT : LOK_SELTYPE_NONE;
5368 static int doc_getSelectionTypeAndText(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pText, char** pUsedMimeType)
5370 // The purpose of this function is to avoid double call to pDoc->getSelection(),
5371 // which may be expensive.
5372 comphelper::ProfileZone aZone("doc_getSelectionTypeAndText");
5374 SolarMutexGuard aGuard;
5375 SetLastExceptionMsg();
5377 ITiledRenderable* pDoc = getTiledRenderable(pThis);
5378 if (!pDoc)
5380 SetLastExceptionMsg("Document doesn't support tiled rendering");
5381 return LOK_SELTYPE_NONE;
5384 css::uno::Reference<css::datatransfer::XTransferable> xTransferable = pDoc->getSelection();
5385 if (!xTransferable)
5387 SetLastExceptionMsg("No selection available");
5388 return LOK_SELTYPE_NONE;
5391 css::uno::Reference<css::datatransfer::XTransferable2> xTransferable2(xTransferable, css::uno::UNO_QUERY);
5392 if (xTransferable2.is() && xTransferable2->isComplex())
5393 return LOK_SELTYPE_COMPLEX;
5395 const char *pType = pMimeType;
5396 if (!pType || pType[0] == '\0')
5397 pType = "text/plain;charset=utf-8";
5399 OString aRet;
5400 bool bSuccess = getFromTransferable(xTransferable, OString(pType), aRet);
5401 if (!bSuccess)
5402 return LOK_SELTYPE_NONE;
5404 if (aRet.getLength() > 10000)
5405 return LOK_SELTYPE_COMPLEX;
5407 if (aRet.isEmpty())
5408 return LOK_SELTYPE_NONE;
5410 if (pText)
5411 *pText = convertOString(aRet);
5413 if (pUsedMimeType) // legacy
5415 if (pMimeType)
5416 *pUsedMimeType = strdup(pMimeType);
5417 else
5418 *pUsedMimeType = nullptr;
5421 return LOK_SELTYPE_TEXT;
5424 static int doc_getClipboard(LibreOfficeKitDocument* pThis,
5425 const char **pMimeTypes,
5426 size_t *pOutCount,
5427 char ***pOutMimeTypes,
5428 size_t **pOutSizes,
5429 char ***pOutStreams)
5431 #ifdef IOS
5432 (void) pThis;
5433 (void) pMimeTypes;
5434 (void) pOutCount;
5435 (void) pOutMimeTypes;
5436 (void) pOutSizes;
5437 (void) pOutStreams;
5439 assert(!"doc_getClipboard should not be called on iOS");
5441 return 0;
5442 #else
5443 comphelper::ProfileZone aZone("doc_getClipboard");
5445 SolarMutexGuard aGuard;
5446 SetLastExceptionMsg();
5448 assert (pOutCount);
5449 assert (pOutMimeTypes);
5450 assert (pOutSizes);
5451 assert (pOutStreams);
5453 *pOutCount = 0;
5454 *pOutMimeTypes = nullptr;
5455 *pOutSizes = nullptr;
5456 *pOutStreams = nullptr;
5458 ITiledRenderable* pDoc = getTiledRenderable(pThis);
5459 if (!pDoc)
5461 SetLastExceptionMsg("Document doesn't support tiled rendering");
5462 return 0;
5465 rtl::Reference<LOKClipboard> xClip(LOKClipboardFactory::getClipboardForCurView());
5467 css::uno::Reference<css::datatransfer::XTransferable> xTransferable = xClip->getContents();
5468 SAL_INFO("lok", "Got from clip: " << xClip.get() << " transferable: " << xTransferable);
5469 if (!xTransferable)
5471 SetLastExceptionMsg("No clipboard content available");
5472 return 0;
5475 std::vector<OString> aMimeTypes;
5476 if (!pMimeTypes) // everything
5478 const uno::Sequence< css::datatransfer::DataFlavor > flavors = xTransferable->getTransferDataFlavors();
5479 if (!flavors.getLength())
5481 SetLastExceptionMsg("Flavourless selection");
5482 return 0;
5484 for (const auto &it : flavors)
5485 aMimeTypes.push_back(OUStringToOString(it.MimeType, RTL_TEXTENCODING_UTF8));
5487 else
5489 for (size_t i = 0; pMimeTypes[i]; ++i)
5490 aMimeTypes.push_back(OString(pMimeTypes[i]));
5493 *pOutCount = aMimeTypes.size();
5494 *pOutSizes = static_cast<size_t *>(malloc(*pOutCount * sizeof(size_t)));
5495 *pOutMimeTypes = static_cast<char **>(malloc(*pOutCount * sizeof(char *)));
5496 *pOutStreams = static_cast<char **>(malloc(*pOutCount * sizeof(char *)));
5497 for (size_t i = 0; i < aMimeTypes.size(); ++i)
5499 if (aMimeTypes[i] == "text/plain;charset=utf-16")
5500 (*pOutMimeTypes)[i] = strdup("text/plain;charset=utf-8");
5501 else
5502 (*pOutMimeTypes)[i] = strdup(aMimeTypes[i].getStr());
5504 OString aRet;
5505 bool bSuccess = getFromTransferable(xTransferable, (*pOutMimeTypes)[i], aRet);
5506 if (!bSuccess || aRet.getLength() < 1)
5508 (*pOutSizes)[i] = 0;
5509 (*pOutStreams)[i] = nullptr;
5511 else
5513 (*pOutSizes)[i] = aRet.getLength();
5514 (*pOutStreams)[i] = convertOString(aRet);
5518 return 1;
5519 #endif
5522 static int doc_setClipboard(LibreOfficeKitDocument* pThis,
5523 const size_t nInCount,
5524 const char **pInMimeTypes,
5525 const size_t *pInSizes,
5526 const char **pInStreams)
5528 #ifdef IOS
5529 (void) pThis;
5530 (void) nInCount;
5531 (void) pInMimeTypes;
5532 (void) pInSizes;
5533 (void) pInStreams;
5534 #else
5535 comphelper::ProfileZone aZone("doc_setClipboard");
5537 SolarMutexGuard aGuard;
5538 SetLastExceptionMsg();
5540 ITiledRenderable* pDoc = getTiledRenderable(pThis);
5541 if (!pDoc)
5543 SetLastExceptionMsg("Document doesn't support tiled rendering");
5544 return false;
5547 uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(nInCount, pInMimeTypes, pInSizes, pInStreams));
5549 auto xClip = forceSetClipboardForCurrentView(pThis);
5550 xClip->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>());
5552 SAL_INFO("lok", "Set clip: " << xClip.get() << " to: " << xTransferable);
5554 if (!pDoc->isMimeTypeSupported())
5556 SetLastExceptionMsg("Document doesn't support this mime type");
5557 return false;
5559 #endif
5560 return true;
5563 static bool doc_paste(LibreOfficeKitDocument* pThis, const char* pMimeType, const char* pData, size_t nSize)
5565 comphelper::ProfileZone aZone("doc_paste");
5567 SolarMutexGuard aGuard;
5569 const char *pInMimeTypes[1];
5570 const char *pInStreams[1];
5571 size_t pInSizes[1];
5572 pInMimeTypes[0] = pMimeType;
5573 pInSizes[0] = nSize;
5574 pInStreams[0] = pData;
5576 if (!doc_setClipboard(pThis, 1, pInMimeTypes, pInSizes, pInStreams))
5577 return false;
5579 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
5581 {"AnchorType", uno::Any(static_cast<sal_uInt16>(css::text::TextContentAnchorType_AS_CHARACTER))},
5582 {"IgnoreComments", uno::Any(true)},
5583 }));
5584 if (!comphelper::dispatchCommand(".uno:Paste", aPropertyValues))
5586 SetLastExceptionMsg("Failed to dispatch the .uno: command");
5587 return false;
5590 return true;
5593 static void doc_setGraphicSelection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY)
5595 comphelper::ProfileZone aZone("doc_setGraphicSelection");
5597 SolarMutexGuard aGuard;
5598 SetLastExceptionMsg();
5600 ITiledRenderable* pDoc = getTiledRenderable(pThis);
5601 if (!pDoc)
5603 SetLastExceptionMsg("Document doesn't support tiled rendering");
5604 return;
5607 pDoc->setGraphicSelection(nType, nX, nY);
5610 static void getDocLanguages(LibreOfficeKitDocument* pThis, uno::Sequence<lang::Locale>& rSeq)
5612 SfxViewFrame* pViewFrame = SfxViewFrame::Current();
5613 if (!pViewFrame)
5614 return;
5616 SfxDispatcher* pDispatcher = pViewFrame->GetBindings().GetDispatcher();
5617 if (!pDispatcher)
5618 return;
5620 css::uno::Any aLangStatus;
5621 pDispatcher->QueryState(SID_LANGUAGE_STATUS, aLangStatus);
5623 OUString sCurrent;
5624 OUString sKeyboard;
5625 OUString sGuessText;
5626 SvtScriptType eScriptType = SvtScriptType::LATIN | SvtScriptType::ASIAN
5627 | SvtScriptType::COMPLEX;
5629 Sequence<OUString> aSeqLang;
5630 if (aLangStatus >>= aSeqLang)
5632 if (aSeqLang.getLength() == 4)
5634 sCurrent = aSeqLang[0];
5635 eScriptType = static_cast<SvtScriptType>(aSeqLang[1].toInt32());
5636 sKeyboard = aSeqLang[1];
5637 sGuessText = aSeqLang[2];
5640 else
5642 aLangStatus >>= sCurrent;
5645 LanguageType nLangType;
5646 std::set<LanguageType> aLangItems;
5648 if (!sCurrent.isEmpty())
5650 nLangType = SvtLanguageTable::GetLanguageType(sCurrent);
5651 if (nLangType != LANGUAGE_DONTKNOW)
5653 aLangItems.insert(nLangType);
5657 const AllSettings& rAllSettings = Application::GetSettings();
5658 nLangType = rAllSettings.GetLanguageTag().getLanguageType();
5659 if (nLangType != LANGUAGE_DONTKNOW &&
5660 (eScriptType & SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType)))
5662 aLangItems.insert(nLangType);
5665 nLangType = rAllSettings.GetUILanguageTag().getLanguageType();
5666 if (nLangType != LANGUAGE_DONTKNOW &&
5667 (eScriptType & SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType)))
5669 aLangItems.insert(nLangType);
5672 if (!sKeyboard.isEmpty())
5674 nLangType = SvtLanguageTable::GetLanguageType(sKeyboard);
5675 if (nLangType != LANGUAGE_DONTKNOW &&
5676 (eScriptType & SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType)))
5678 aLangItems.insert(nLangType);
5682 if (!sGuessText.isEmpty())
5684 Reference<linguistic2::XLanguageGuessing> xLangGuesser;
5687 xLangGuesser = linguistic2::LanguageGuessing::create(xContext);
5689 catch(...)
5693 if (xLangGuesser.is())
5695 lang::Locale aLocale = xLangGuesser->guessPrimaryLanguage(sGuessText, 0,
5696 sGuessText.getLength());
5697 LanguageTag aLanguageTag(aLocale);
5698 nLangType = aLanguageTag.getLanguageType(false);
5699 if (nLangType != LANGUAGE_DONTKNOW &&
5700 (eScriptType & SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType)))
5702 aLangItems.insert(nLangType);
5707 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
5708 Reference<document::XDocumentLanguages> xDocumentLanguages(pDocument->mxComponent, UNO_QUERY);
5709 if (xDocumentLanguages.is())
5711 const Sequence<lang::Locale> aLocales(xDocumentLanguages->getDocumentLanguages(
5712 static_cast<sal_Int16>(eScriptType), 64));
5714 for (const lang::Locale& aLocale : aLocales)
5716 nLangType = SvtLanguageTable::GetLanguageType(aLocale.Language);
5717 if (nLangType != LANGUAGE_DONTKNOW &&
5718 (eScriptType & SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType)))
5720 aLangItems.insert(nLangType);
5725 int nLocale = 0;
5726 Sequence<lang::Locale> aLocales(aLangItems.size());
5727 auto pLocales = aLocales.getArray();
5728 for (const LanguageType& itLang : aLangItems)
5730 pLocales[nLocale++] = LanguageTag::convertToLocale(itLang);
5733 rSeq = aLocales;
5736 static void doc_resetSelection(LibreOfficeKitDocument* pThis)
5738 comphelper::ProfileZone aZone("doc_resetSelection");
5740 SolarMutexGuard aGuard;
5741 SetLastExceptionMsg();
5743 ITiledRenderable* pDoc = getTiledRenderable(pThis);
5744 if (!pDoc)
5746 SetLastExceptionMsg("Document doesn't support tiled rendering");
5747 return;
5750 pDoc->resetSelection();
5753 static char* getLanguages(LibreOfficeKitDocument* pThis, const char* pCommand)
5755 css::uno::Sequence< css::lang::Locale > aLocales;
5757 if (xContext.is())
5759 css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv = css::linguistic2::LinguServiceManager::create(xContext);
5760 if (xLangSrv.is())
5762 css::uno::Reference<css::linguistic2::XSpellChecker> xSpell = xLangSrv->getSpellChecker();
5763 if (xSpell.is())
5764 aLocales = xSpell->getLocales();
5767 /* FIXME: To obtain the document languages the spell checker can be disabled,
5768 so a future re-work of the getLanguages function is needed in favor to use
5769 getDocLanguages */
5770 if (!aLocales.hasElements())
5772 uno::Sequence< css::lang::Locale > aSeq;
5773 getDocLanguages(pThis, aSeq);
5774 aLocales = aSeq;
5778 boost::property_tree::ptree aTree;
5779 aTree.put("commandName", pCommand);
5780 boost::property_tree::ptree aValues;
5781 boost::property_tree::ptree aChild;
5782 OUString sLanguage;
5783 for ( css::lang::Locale const & locale : std::as_const(aLocales) )
5785 const LanguageTag aLanguageTag( locale );
5786 sLanguage = SvtLanguageTable::GetLanguageString(aLanguageTag.getLanguageType());
5787 if (sLanguage.startsWith("{") && sLanguage.endsWith("}"))
5788 continue;
5790 sLanguage += ";" + aLanguageTag.getBcp47(false);
5791 aChild.put("", sLanguage.toUtf8());
5792 aValues.push_back(std::make_pair("", aChild));
5794 aTree.add_child("commandValues", aValues);
5795 std::stringstream aStream;
5796 boost::property_tree::write_json(aStream, aTree);
5797 char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
5798 assert(pJson); // Don't handle OOM conditions
5799 strcpy(pJson, aStream.str().c_str());
5800 pJson[aStream.str().size()] = '\0';
5801 return pJson;
5804 static char* getFonts (const char* pCommand)
5806 SfxObjectShell* pDocSh = SfxObjectShell::Current();
5807 if (!pDocSh)
5808 return nullptr;
5809 const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(
5810 pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
5811 const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
5813 boost::property_tree::ptree aTree;
5814 aTree.put("commandName", pCommand);
5815 boost::property_tree::ptree aValues;
5816 if ( pList )
5818 sal_uInt16 nFontCount = pList->GetFontNameCount();
5819 for (sal_uInt16 i = 0; i < nFontCount; ++i)
5821 boost::property_tree::ptree aChildren;
5822 const FontMetric& rFontMetric = pList->GetFontName(i);
5823 const int* pAry = FontList::GetStdSizeAry();
5824 sal_uInt16 nSizeCount = 0;
5825 while (pAry[nSizeCount])
5827 boost::property_tree::ptree aChild;
5828 aChild.put("", static_cast<float>(pAry[nSizeCount]) / 10);
5829 aChildren.push_back(std::make_pair("", aChild));
5830 nSizeCount++;
5832 aValues.add_child(rFontMetric.GetFamilyName().toUtf8().getStr(), aChildren);
5835 aTree.add_child("commandValues", aValues);
5836 std::stringstream aStream;
5837 boost::property_tree::write_json(aStream, aTree);
5838 char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
5839 assert(pJson); // Don't handle OOM conditions
5840 strcpy(pJson, aStream.str().c_str());
5841 pJson[aStream.str().size()] = '\0';
5842 return pJson;
5845 static char* getFontSubset (std::string_view aFontName)
5847 OUString aFoundFont(::rtl::Uri::decode(OStringToOUString(aFontName, RTL_TEXTENCODING_UTF8), rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8));
5849 boost::property_tree::ptree aTree;
5850 aTree.put("commandName", ".uno:FontSubset");
5851 boost::property_tree::ptree aValues;
5853 if (const vcl::Font* pFont = FindFont(aFoundFont))
5855 FontCharMapRef xFontCharMap (new FontCharMap());
5856 auto aDevice(VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA));
5858 aDevice->SetFont(*pFont);
5859 aDevice->GetFontCharMap(xFontCharMap);
5860 SubsetMap aSubMap(xFontCharMap);
5862 for (auto const& subset : aSubMap.GetSubsetMap())
5864 boost::property_tree::ptree aChild;
5865 aChild.put("", static_cast<int>(ublock_getCode(subset.GetRangeMin())));
5866 aValues.push_back(std::make_pair("", aChild));
5870 aTree.add_child("commandValues", aValues);
5871 std::stringstream aStream;
5872 boost::property_tree::write_json(aStream, aTree);
5873 char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
5874 assert(pJson); // Don't handle OOM conditions
5875 strcpy(pJson, aStream.str().c_str());
5876 pJson[aStream.str().size()] = '\0';
5877 return pJson;
5880 static char* getStyles(LibreOfficeKitDocument* pThis, const char* pCommand)
5882 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
5884 boost::property_tree::ptree aTree;
5885 aTree.put("commandName", pCommand);
5886 uno::Reference<css::style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(pDocument->mxComponent, uno::UNO_QUERY);
5887 const uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
5888 const uno::Sequence<OUString> aStyleFamilies = xStyleFamilies->getElementNames();
5890 static const std::vector<OUString> aWriterStyles =
5892 "Text body",
5893 "Quotations",
5894 "Title",
5895 "Subtitle",
5896 "Heading 1",
5897 "Heading 2",
5898 "Heading 3"
5901 // We need to keep a list of the default style names
5902 // in order to filter these out later when processing
5903 // the full list of styles.
5904 std::set<OUString> aDefaultStyleNames;
5906 boost::property_tree::ptree aValues;
5907 for (OUString const & sStyleFam : aStyleFamilies)
5909 boost::property_tree::ptree aChildren;
5910 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName(sStyleFam), uno::UNO_QUERY);
5912 // Writer provides a huge number of styles, we have a list of 7 "default" styles which
5913 // should be shown in the normal dropdown, which we should add to the start of the list
5914 // to simplify their selection.
5915 if (sStyleFam == "ParagraphStyles"
5916 && doc_getDocumentType(pThis) == LOK_DOCTYPE_TEXT)
5918 for (const OUString& rStyle: aWriterStyles)
5920 aDefaultStyleNames.insert( rStyle );
5922 boost::property_tree::ptree aChild;
5923 aChild.put("", rStyle.toUtf8());
5924 aChildren.push_back(std::make_pair("", aChild));
5928 const uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames();
5929 for (const OUString& rStyle: aStyles )
5931 // Filter out the default styles - they are already at the top
5932 // of the list
5933 if (aDefaultStyleNames.find(rStyle) == aDefaultStyleNames.end() ||
5934 (sStyleFam != "ParagraphStyles" || doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT) )
5936 boost::property_tree::ptree aChild;
5937 aChild.put("", rStyle.toUtf8());
5938 aChildren.push_back(std::make_pair("", aChild));
5941 aValues.add_child(sStyleFam.toUtf8().getStr(), aChildren);
5944 // Header & Footer Styles
5946 boost::property_tree::ptree aChild;
5947 boost::property_tree::ptree aChildren;
5948 static const OUStringLiteral sPageStyles(u"PageStyles");
5949 uno::Reference<beans::XPropertySet> xProperty;
5950 uno::Reference<container::XNameContainer> xContainer;
5952 if (xStyleFamilies->hasByName(sPageStyles) && (xStyleFamilies->getByName(sPageStyles) >>= xContainer))
5954 const uno::Sequence<OUString> aSeqNames = xContainer->getElementNames();
5955 for (OUString const & sName : aSeqNames)
5957 bool bIsPhysical;
5958 xProperty.set(xContainer->getByName(sName), uno::UNO_QUERY);
5959 if (xProperty.is() && (xProperty->getPropertyValue("IsPhysical") >>= bIsPhysical) && bIsPhysical)
5961 OUString displayName;
5962 xProperty->getPropertyValue("DisplayName") >>= displayName;
5963 aChild.put("", displayName.toUtf8());
5964 aChildren.push_back(std::make_pair("", aChild));
5967 aValues.add_child("HeaderFooter", aChildren);
5972 boost::property_tree::ptree aCommandList;
5975 boost::property_tree::ptree aChild;
5977 OUString sClearFormat = SvxResId(RID_SVXSTR_CLEARFORM);
5979 boost::property_tree::ptree aName;
5980 aName.put("", sClearFormat.toUtf8());
5981 aChild.push_back(std::make_pair("text", aName));
5983 boost::property_tree::ptree aCommand;
5984 aCommand.put("", ".uno:ResetAttributes");
5985 aChild.push_back(std::make_pair("id", aCommand));
5987 aCommandList.push_back(std::make_pair("", aChild));
5990 aValues.add_child("Commands", aCommandList);
5993 aTree.add_child("commandValues", aValues);
5994 std::stringstream aStream;
5995 boost::property_tree::write_json(aStream, aTree);
5996 char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
5997 assert(pJson); // Don't handle OOM conditions
5998 strcpy(pJson, aStream.str().c_str());
5999 pJson[aStream.str().size()] = '\0';
6000 return pJson;
6003 namespace {
6005 enum class UndoOrRedo
6007 UNDO,
6008 REDO
6013 /// Returns the JSON representation of either an undo or a redo stack.
6014 static char* getUndoOrRedo(LibreOfficeKitDocument* pThis, UndoOrRedo eCommand)
6016 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
6018 auto pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
6019 if (!pBaseModel)
6020 return nullptr;
6022 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
6023 if (!pObjectShell)
6024 return nullptr;
6026 SfxUndoManager* pUndoManager = pObjectShell->GetUndoManager();
6027 if (!pUndoManager)
6028 return nullptr;
6030 OUString aString;
6031 if (eCommand == UndoOrRedo::UNDO)
6032 aString = pUndoManager->GetUndoActionsInfo();
6033 else
6034 aString = pUndoManager->GetRedoActionsInfo();
6035 char* pJson = strdup(aString.toUtf8().getStr());
6036 return pJson;
6039 /// Returns the JSON representation of the redline stack.
6040 static char* getTrackedChanges(LibreOfficeKitDocument* pThis)
6042 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
6044 uno::Reference<document::XRedlinesSupplier> xRedlinesSupplier(pDocument->mxComponent, uno::UNO_QUERY);
6045 tools::JsonWriter aJson;
6046 // We want positions of the track changes also which is not possible from
6047 // UNO. Enable positioning information for text documents only for now, so
6048 // construct the tracked changes JSON from inside the sw/, not here using UNO
6049 if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT && xRedlinesSupplier.is())
6051 auto redlinesNode = aJson.startArray("redlines");
6052 uno::Reference<container::XEnumeration> xRedlines = xRedlinesSupplier->getRedlines()->createEnumeration();
6053 for (size_t nIndex = 0; xRedlines->hasMoreElements(); ++nIndex)
6055 uno::Reference<beans::XPropertySet> xRedline(xRedlines->nextElement(), uno::UNO_QUERY);
6056 auto redlineNode = aJson.startStruct();
6057 aJson.put("index", static_cast<sal_Int32>(nIndex));
6059 OUString sAuthor;
6060 xRedline->getPropertyValue("RedlineAuthor") >>= sAuthor;
6061 aJson.put("author", sAuthor);
6063 OUString sType;
6064 xRedline->getPropertyValue("RedlineType") >>= sType;
6065 aJson.put("type", sType);
6067 OUString sComment;
6068 xRedline->getPropertyValue("RedlineComment") >>= sComment;
6069 aJson.put("comment", sComment);
6071 OUString sDescription;
6072 xRedline->getPropertyValue("RedlineDescription") >>= sDescription;
6073 aJson.put("description", sDescription);
6075 util::DateTime aDateTime;
6076 xRedline->getPropertyValue("RedlineDateTime") >>= aDateTime;
6077 OUString sDateTime = utl::toISO8601(aDateTime);
6078 aJson.put("dateTime", sDateTime);
6081 else
6083 ITiledRenderable* pDoc = getTiledRenderable(pThis);
6084 if (!pDoc)
6086 SetLastExceptionMsg("Document doesn't support tiled rendering");
6087 return nullptr;
6089 pDoc->getTrackedChanges(aJson);
6092 return strdup(aJson.finishAndGetAsOString().getStr());
6096 /// Returns the JSON representation of the redline author table.
6097 static char* getTrackedChangeAuthors(LibreOfficeKitDocument* pThis)
6099 ITiledRenderable* pDoc = getTiledRenderable(pThis);
6100 if (!pDoc)
6102 SetLastExceptionMsg("Document doesn't support tiled rendering");
6103 return nullptr;
6105 tools::JsonWriter aJsonWriter;
6106 pDoc->getTrackedChangeAuthors(aJsonWriter);
6107 return strdup(aJsonWriter.finishAndGetAsOString().getStr());
6110 static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand)
6112 comphelper::ProfileZone aZone("doc_getCommandValues");
6114 SolarMutexGuard aGuard;
6115 SetLastExceptionMsg();
6117 const std::string_view aCommand(pCommand);
6118 static constexpr OStringLiteral aViewRowColumnHeaders(".uno:ViewRowColumnHeaders");
6119 static constexpr OStringLiteral aSheetGeometryData(".uno:SheetGeometryData");
6120 static constexpr OStringLiteral aCellCursor(".uno:CellCursor");
6121 static constexpr OStringLiteral aFontSubset(".uno:FontSubset&name=");
6123 ITiledRenderable* pDoc = getTiledRenderable(pThis);
6124 if (!pDoc)
6126 SetLastExceptionMsg("Document doesn't support tiled rendering");
6127 return nullptr;
6130 if (!strcmp(pCommand, ".uno:LanguageStatus"))
6132 return getLanguages(pThis, pCommand);
6134 else if (!strcmp(pCommand, ".uno:CharFontName"))
6136 return getFonts(pCommand);
6138 else if (!strcmp(pCommand, ".uno:StyleApply"))
6140 return getStyles(pThis, pCommand);
6142 else if (aCommand == ".uno:Undo")
6144 return getUndoOrRedo(pThis, UndoOrRedo::UNDO);
6146 else if (aCommand == ".uno:Redo")
6148 return getUndoOrRedo(pThis, UndoOrRedo::REDO);
6150 else if (aCommand == ".uno:AcceptTrackedChanges")
6152 return getTrackedChanges(pThis);
6154 else if (aCommand == ".uno:TrackedChangeAuthors")
6156 return getTrackedChangeAuthors(pThis);
6158 else if (aCommand == ".uno:ViewAnnotations")
6160 return getPostIts(pThis);
6162 else if (aCommand == ".uno:ViewAnnotationsPosition")
6164 return getPostItsPos(pThis);
6166 else if (aCommand == ".uno:RulerState")
6168 return getRulerState(pThis);
6170 else if (o3tl::starts_with(aCommand, aViewRowColumnHeaders))
6172 tools::Rectangle aRectangle;
6173 if (aCommand.size() > o3tl::make_unsigned(aViewRowColumnHeaders.getLength()))
6175 // Command has parameters.
6176 int nX = 0;
6177 int nY = 0;
6178 int nWidth = 0;
6179 int nHeight = 0;
6180 std::string_view aArguments = aCommand.substr(aViewRowColumnHeaders.getLength() + 1);
6181 sal_Int32 nParamIndex = 0;
6184 std::string_view aParamToken = o3tl::getToken(aArguments, 0, '&', nParamIndex);
6185 sal_Int32 nIndex = 0;
6186 std::string_view aKey;
6187 std::string_view aValue;
6190 std::string_view aToken = o3tl::getToken(aParamToken, 0, '=', nIndex);
6191 if (aKey.empty())
6192 aKey = aToken;
6193 else
6194 aValue = aToken;
6196 while (nIndex >= 0);
6197 if (aKey == "x")
6198 nX = o3tl::toInt32(aValue);
6199 else if (aKey == "y")
6200 nY = o3tl::toInt32(aValue);
6201 else if (aKey == "width")
6202 nWidth = o3tl::toInt32(aValue);
6203 else if (aKey == "height")
6204 nHeight = o3tl::toInt32(aValue);
6206 while (nParamIndex >= 0);
6208 aRectangle = tools::Rectangle(nX, nY, nX + nWidth, nY + nHeight);
6211 tools::JsonWriter aJsonWriter;
6212 pDoc->getRowColumnHeaders(aRectangle, aJsonWriter);
6213 return strdup(aJsonWriter.finishAndGetAsOString().getStr());
6215 else if (o3tl::starts_with(aCommand, aSheetGeometryData))
6217 bool bColumns = true;
6218 bool bRows = true;
6219 bool bSizes = true;
6220 bool bHidden = true;
6221 bool bFiltered = true;
6222 bool bGroups = true;
6223 if (aCommand.size() > o3tl::make_unsigned(aSheetGeometryData.getLength()))
6225 bColumns = bRows = bSizes = bHidden = bFiltered = bGroups = false;
6227 std::string_view aArguments = aCommand.substr(aSheetGeometryData.getLength() + 1);
6228 sal_Int32 nParamIndex = 0;
6231 std::string_view aParamToken = o3tl::getToken(aArguments, 0, '&', nParamIndex);
6232 sal_Int32 nIndex = 0;
6233 std::string_view aKey;
6234 std::string_view aValue;
6237 std::string_view aToken = o3tl::getToken(aParamToken, 0, '=', nIndex);
6238 if (aKey.empty())
6239 aKey = aToken;
6240 else
6241 aValue = aToken;
6243 } while (nIndex >= 0);
6245 bool bEnableFlag = aValue.empty() ||
6246 o3tl::equalsIgnoreAsciiCase(aValue, "true") || o3tl::toInt32(aValue) > 0;
6247 if (!bEnableFlag)
6248 continue;
6250 if (aKey == "columns")
6251 bColumns = true;
6252 else if (aKey == "rows")
6253 bRows = true;
6254 else if (aKey == "sizes")
6255 bSizes = true;
6256 else if (aKey == "hidden")
6257 bHidden = true;
6258 else if (aKey == "filtered")
6259 bFiltered = true;
6260 else if (aKey == "groups")
6261 bGroups = true;
6263 } while (nParamIndex >= 0);
6266 OString aGeomDataStr
6267 = pDoc->getSheetGeometryData(bColumns, bRows, bSizes, bHidden, bFiltered, bGroups);
6269 if (aGeomDataStr.isEmpty())
6270 return nullptr;
6272 return convertOString(aGeomDataStr);
6274 else if (o3tl::starts_with(aCommand, aCellCursor))
6276 // Ignore command's deprecated parameters.
6277 tools::JsonWriter aJsonWriter;
6278 pDoc->getCellCursor(aJsonWriter);
6279 return strdup(aJsonWriter.finishAndGetAsOString().getStr());
6281 else if (o3tl::starts_with(aCommand, aFontSubset))
6283 return getFontSubset(aCommand.substr(aFontSubset.getLength()));
6285 else if (pDoc->supportsCommand(INetURLObject(OUString::fromUtf8(aCommand)).GetURLPath()))
6287 tools::JsonWriter aJsonWriter;
6288 pDoc->getCommandValues(aJsonWriter, aCommand);
6289 return strdup(aJsonWriter.finishAndGetAsOString().getStr());
6291 else
6293 SetLastExceptionMsg("Unknown command, no values returned");
6294 return nullptr;
6298 static void doc_setClientZoom(LibreOfficeKitDocument* pThis, int nTilePixelWidth, int nTilePixelHeight,
6299 int nTileTwipWidth, int nTileTwipHeight)
6301 comphelper::ProfileZone aZone("doc_setClientZoom");
6303 SolarMutexGuard aGuard;
6304 SetLastExceptionMsg();
6306 ITiledRenderable* pDoc = getTiledRenderable(pThis);
6307 if (!pDoc)
6309 SetLastExceptionMsg("Document doesn't support tiled rendering");
6310 return;
6313 pDoc->setClientZoom(nTilePixelWidth, nTilePixelHeight, nTileTwipWidth, nTileTwipHeight);
6316 static void doc_setClientVisibleArea(LibreOfficeKitDocument* pThis, int nX, int nY, int nWidth, int nHeight)
6318 comphelper::ProfileZone aZone("doc_setClientVisibleArea");
6320 SolarMutexGuard aGuard;
6321 SetLastExceptionMsg();
6323 ITiledRenderable* pDoc = getTiledRenderable(pThis);
6324 if (!pDoc)
6326 SetLastExceptionMsg("Document doesn't support tiled rendering");
6327 return;
6330 tools::Rectangle aRectangle(Point(nX, nY), Size(nWidth, nHeight));
6331 pDoc->setClientVisibleArea(aRectangle);
6334 static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int nLevel, int nIndex, bool bHidden)
6336 comphelper::ProfileZone aZone("doc_setOutlineState");
6338 SolarMutexGuard aGuard;
6339 SetLastExceptionMsg();
6341 ITiledRenderable* pDoc = getTiledRenderable(pThis);
6342 if (!pDoc)
6344 SetLastExceptionMsg("Document doesn't support tiled rendering");
6345 return;
6348 pDoc->setOutlineState(bColumn, nLevel, nIndex, bHidden);
6351 static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis,
6352 const char* pOptions)
6354 comphelper::ProfileZone aZone("doc_createView");
6356 SolarMutexGuard aGuard;
6357 SetLastExceptionMsg();
6359 OUString aOptions = getUString(pOptions);
6360 const OUString aLanguage = extractParameter(aOptions, u"Language");
6362 if (!aLanguage.isEmpty())
6364 // Set the LOK language tag, used for dialog tunneling.
6365 comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(aLanguage));
6366 comphelper::LibreOfficeKit::setLocale(LanguageTag(aLanguage));
6369 const OUString aDeviceFormFactor = extractParameter(aOptions, u"DeviceFormFactor");
6370 SfxLokHelper::setDeviceFormFactor(aDeviceFormFactor);
6372 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
6373 const int nId = SfxLokHelper::createView(pDocument->mnDocumentId);
6375 vcl::lok::numberOfViewsChanged(SfxLokHelper::getViewsCount(pDocument->mnDocumentId));
6377 #ifdef IOS
6378 (void) pThis;
6379 #else
6380 forceSetClipboardForCurrentView(pThis);
6381 #endif
6383 return nId;
6386 static int doc_createView(LibreOfficeKitDocument* pThis)
6388 return doc_createViewWithOptions(pThis, nullptr); // No options.
6391 static void doc_destroyView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* pThis, int nId)
6393 comphelper::ProfileZone aZone("doc_destroyView");
6395 SolarMutexGuard aGuard;
6396 SetLastExceptionMsg();
6398 #ifndef IOS
6399 LOKClipboardFactory::releaseClipboardForView(nId);
6400 #endif
6402 SfxLokHelper::destroyView(nId);
6404 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
6405 vcl::lok::numberOfViewsChanged(SfxLokHelper::getViewsCount(pDocument->mnDocumentId));
6408 static void doc_setView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId)
6410 comphelper::ProfileZone aZone("doc_setView");
6412 SolarMutexGuard aGuard;
6413 SetLastExceptionMsg();
6415 SfxLokHelper::setView(nId);
6418 static int doc_getView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
6420 comphelper::ProfileZone aZone("doc_getView");
6422 SolarMutexGuard aGuard;
6423 SetLastExceptionMsg();
6425 return SfxLokHelper::getView();
6428 static int doc_getViewsCount(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* pThis)
6430 comphelper::ProfileZone aZone("doc_getViewsCount");
6432 SolarMutexGuard aGuard;
6433 SetLastExceptionMsg();
6435 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
6436 return SfxLokHelper::getViewsCount(pDocument->mnDocumentId);
6439 static bool doc_getViewIds(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* pThis, int* pArray, size_t nSize)
6441 comphelper::ProfileZone aZone("doc_getViewsIds");
6443 SolarMutexGuard aGuard;
6444 SetLastExceptionMsg();
6446 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
6447 return SfxLokHelper::getViewIds(pDocument->mnDocumentId, pArray, nSize);
6450 static void doc_setViewLanguage(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId, const char* language)
6452 comphelper::ProfileZone aZone("doc_setViewLanguage");
6454 SolarMutexGuard aGuard;
6455 SetLastExceptionMsg();
6457 OUString sLanguage = OStringToOUString(language, RTL_TEXTENCODING_UTF8);
6458 SfxLokHelper::setViewLanguage(nId, sLanguage);
6459 SfxLokHelper::setViewLocale(nId, sLanguage);
6462 unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis,
6463 const char* pFontName,
6464 const char* pChar,
6465 int* pFontWidth,
6466 int* pFontHeight)
6468 return doc_renderFontOrientation(pThis, pFontName, pChar, pFontWidth, pFontHeight, 0);
6471 unsigned char* doc_renderFontOrientation(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/,
6472 const char* pFontName,
6473 const char* pChar,
6474 int* pFontWidth,
6475 int* pFontHeight,
6476 int pOrientation)
6478 comphelper::ProfileZone aZone("doc_renderFont");
6480 SolarMutexGuard aGuard;
6481 SetLastExceptionMsg();
6483 const int nDefaultFontSize = 25;
6485 auto aFont = FindFont_FallbackToDefault(OStringToOUString(pFontName, RTL_TEXTENCODING_UTF8));
6487 OUString aText(OStringToOUString(pChar, RTL_TEXTENCODING_UTF8));
6488 if (aText.isEmpty())
6489 aText = aFont.GetFamilyName();
6491 auto aDevice(VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA));
6492 ::tools::Rectangle aRect;
6493 aFont.SetFontSize(Size(0, nDefaultFontSize));
6494 aFont.SetOrientation(Degree10(pOrientation));
6495 aDevice->SetFont(aFont);
6496 aDevice->GetTextBoundRect(aRect, aText);
6497 if (aRect.IsEmpty())
6498 return nullptr;
6500 int nFontWidth = aRect.Right() + 1;
6501 int nFontHeight = aRect.Bottom() + 1;
6503 if (nFontWidth <= 0 || nFontHeight <= 0)
6504 return nullptr;
6506 if (*pFontWidth > 0 && *pFontHeight > 0)
6508 double fScaleX = *pFontWidth / static_cast<double>(nFontWidth) / 1.5;
6509 double fScaleY = *pFontHeight / static_cast<double>(nFontHeight) / 1.5;
6511 double fScale = std::min(fScaleX, fScaleY);
6513 if (fScale >= 1.0)
6515 int nFontSize = fScale * nDefaultFontSize;
6516 aFont.SetFontSize(Size(0, nFontSize));
6517 aDevice->SetFont(aFont);
6520 aRect = tools::Rectangle(0, 0, *pFontWidth, *pFontHeight);
6522 nFontWidth = *pFontWidth;
6523 nFontHeight = *pFontHeight;
6527 unsigned char* pBuffer = static_cast<unsigned char*>(malloc(4 * nFontWidth * nFontHeight));
6528 if (!pBuffer)
6529 return nullptr;
6531 memset(pBuffer, 0, nFontWidth * nFontHeight * 4);
6532 aDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
6533 aDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(
6534 Size(nFontWidth, nFontHeight), Fraction(1.0), Point(),
6535 pBuffer);
6537 if (*pFontWidth > 0 && *pFontHeight > 0)
6539 DrawTextFlags const nStyle =
6540 DrawTextFlags::Center
6541 | DrawTextFlags::VCenter
6542 | DrawTextFlags::MultiLine
6543 | DrawTextFlags::WordBreak;// | DrawTextFlags::WordBreakHyphenation ;
6545 aDevice->DrawText(aRect, aText, nStyle);
6547 else
6549 *pFontWidth = nFontWidth;
6550 *pFontHeight = nFontHeight;
6552 aDevice->DrawText(Point(0,0), aText);
6556 return pBuffer;
6560 static void doc_paintWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
6561 unsigned char* pBuffer,
6562 const int nX, const int nY,
6563 const int nWidth, const int nHeight)
6565 doc_paintWindowDPI(pThis, nLOKWindowId, pBuffer, nX, nY, nWidth, nHeight, 1.0);
6568 static void doc_paintWindowDPI(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
6569 unsigned char* pBuffer,
6570 const int nX, const int nY,
6571 const int nWidth, const int nHeight,
6572 const double fDPIScale)
6574 doc_paintWindowForView(pThis, nLOKWindowId, pBuffer, nX, nY, nWidth, nHeight, fDPIScale, -1);
6577 static void doc_paintWindowForView(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
6578 unsigned char* pBuffer, const int nX, const int nY,
6579 const int nWidth, const int nHeight,
6580 const double fDPIScale, int viewId)
6582 comphelper::ProfileZone aZone("doc_paintWindowDPI");
6584 SolarMutexGuard aGuard;
6585 SetLastExceptionMsg();
6587 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
6588 if (!pWindow)
6590 SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
6591 return;
6594 // Used to avoid work in setView if set.
6595 comphelper::LibreOfficeKit::setDialogPainting(true);
6597 if (viewId >= 0)
6598 doc_setView(pThis, viewId);
6600 // Setup cairo (or CoreGraphics, in the iOS case) to draw with the changed DPI scale (and return
6601 // back to 1.0 when the painting finishes)
6602 comphelper::ScopeGuard dpiScaleGuard([]() { comphelper::LibreOfficeKit::setDPIScale(1.0); });
6603 comphelper::LibreOfficeKit::setDPIScale(fDPIScale);
6605 #if defined(IOS)
6607 CGContextRef cgc = CGBitmapContextCreate(pBuffer, nWidth, nHeight, 8, nWidth*4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst | kCGImageByteOrder32Little);
6609 CGContextTranslateCTM(cgc, 0, nHeight);
6610 CGContextScaleCTM(cgc, fDPIScale, -fDPIScale);
6612 SystemGraphicsData aData;
6613 aData.rCGContext = cgc;
6615 ScopedVclPtrInstance<VirtualDevice> pDevice(aData, Size(1, 1), DeviceFormat::WITHOUT_ALPHA);
6616 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
6618 pDevice->SetOutputSizePixel(Size(nWidth, nHeight));
6620 MapMode aMapMode(pDevice->GetMapMode());
6621 aMapMode.SetOrigin(Point(-(nX / fDPIScale), -(nY / fDPIScale)));
6622 pDevice->SetMapMode(aMapMode);
6624 pWindow->PaintToDevice(pDevice.get(), Point(0, 0));
6626 CGContextRelease(cgc);
6628 #else
6630 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA);
6631 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
6633 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nWidth, nHeight), Fraction(1.0), Point(), pBuffer);
6635 MapMode aMapMode(pDevice->GetMapMode());
6636 aMapMode.SetOrigin(Point(-(nX / fDPIScale), -(nY / fDPIScale)));
6637 pDevice->SetMapMode(aMapMode);
6639 pWindow->PaintToDevice(pDevice.get(), Point(0, 0));
6640 #endif
6642 comphelper::LibreOfficeKit::setDialogPainting(false);
6645 static void doc_postWindow(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nAction, const char* pData)
6647 comphelper::ProfileZone aZone("doc_postWindow");
6649 SolarMutexGuard aGuard;
6650 SetLastExceptionMsg();
6652 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
6653 if (!pWindow)
6655 SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
6656 return;
6659 if (nAction == LOK_WINDOW_CLOSE)
6661 vcl::CloseTopLevel(pWindow);
6663 else if (nAction == LOK_WINDOW_PASTE)
6665 #ifndef IOS
6666 OUString aMimeType;
6667 css::uno::Sequence<sal_Int8> aData;
6668 std::vector<beans::PropertyValue> aArgs(jsonToPropertyValuesVector(pData));
6670 aArgs.size() == 2 &&
6671 aArgs[0].Name == "MimeType" && (aArgs[0].Value >>= aMimeType) &&
6672 aArgs[1].Name == "Data" && (aArgs[1].Value >>= aData);
6675 if (!aMimeType.isEmpty() && aData.hasElements())
6677 uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(aMimeType, aData));
6678 uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(new LOKClipboard);
6679 xClipboard->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>());
6680 pWindow->SetClipboard(xClipboard);
6682 KeyEvent aEvent(0, KEY_PASTE, 0);
6683 Application::PostKeyEvent(VclEventId::WindowKeyInput, pWindow, &aEvent);
6685 else
6686 SetLastExceptionMsg("Window command 'paste': wrong parameters.");
6687 #else
6688 (void) pData;
6689 assert(!"doc_postWindow() with LOK_WINDOW_PASTE should not be called on iOS");
6690 #endif
6694 // CERTIFICATE AND DOCUMENT SIGNING
6695 static bool doc_insertCertificate(LibreOfficeKitDocument* pThis,
6696 const unsigned char* pCertificateBinary, const int nCertificateBinarySize,
6697 const unsigned char* pPrivateKeyBinary, const int nPrivateKeySize)
6699 comphelper::ProfileZone aZone("doc_insertCertificate");
6701 if (!xContext.is())
6702 return false;
6704 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
6706 if (!pDocument->mxComponent.is())
6707 return false;
6709 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
6710 if (!pBaseModel)
6711 return false;
6713 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
6715 if (!pObjectShell)
6716 return false;
6718 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
6719 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
6720 if (!xSecurityContext.is())
6721 return false;
6723 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
6724 uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
6726 if (!xCertificateCreator.is())
6727 return false;
6729 uno::Sequence<sal_Int8> aCertificateSequence;
6731 std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
6732 std::string aCertificateBase64String = extractCertificate(aCertificateString);
6733 if (!aCertificateBase64String.empty())
6735 OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String);
6736 comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
6738 else
6740 aCertificateSequence.realloc(nCertificateBinarySize);
6741 std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.getArray());
6744 uno::Sequence<sal_Int8> aPrivateKeySequence;
6745 std::string aPrivateKeyString(reinterpret_cast<const char*>(pPrivateKeyBinary), nPrivateKeySize);
6746 std::string aPrivateKeyBase64String = extractPrivateKey(aPrivateKeyString);
6747 if (!aPrivateKeyBase64String.empty())
6749 OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String);
6750 comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString);
6752 else
6754 aPrivateKeySequence.realloc(nPrivateKeySize);
6755 std::copy(pPrivateKeyBinary, pPrivateKeyBinary + nPrivateKeySize, aPrivateKeySequence.getArray());
6758 uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence);
6760 if (!xCertificate.is())
6761 return false;
6763 SolarMutexGuard aGuard;
6765 return pObjectShell->SignDocumentContentUsingCertificate(xCertificate);
6768 static bool doc_addCertificate(LibreOfficeKitDocument* pThis,
6769 const unsigned char* pCertificateBinary, const int nCertificateBinarySize)
6771 comphelper::ProfileZone aZone("doc_addCertificate");
6773 if (!xContext.is())
6774 return false;
6776 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
6778 if (!pDocument->mxComponent.is())
6779 return false;
6781 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
6782 if (!pBaseModel)
6783 return false;
6785 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
6787 if (!pObjectShell)
6788 return false;
6790 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
6791 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
6792 if (!xSecurityContext.is())
6793 return false;
6795 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
6796 uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
6798 if (!xCertificateCreator.is())
6799 return false;
6801 uno::Sequence<sal_Int8> aCertificateSequence;
6803 std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
6804 std::string aCertificateBase64String = extractCertificate(aCertificateString);
6805 if (!aCertificateBase64String.empty())
6807 OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String);
6808 comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
6810 else
6812 aCertificateSequence.realloc(nCertificateBinarySize);
6813 std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.getArray());
6816 uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->addDERCertificateToTheDatabase(aCertificateSequence, "TCu,Cu,Tu");
6818 if (!xCertificate.is())
6819 return false;
6821 SAL_INFO("lok", "Certificate Added = IssuerName: " << xCertificate->getIssuerName() << " SubjectName: " << xCertificate->getSubjectName());
6823 return true;
6826 static int doc_getSignatureState(LibreOfficeKitDocument* pThis)
6828 comphelper::ProfileZone aZone("doc_getSignatureState");
6830 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
6832 if (!pDocument->mxComponent.is())
6833 return int(SignatureState::UNKNOWN);
6835 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
6836 if (!pBaseModel)
6837 return int(SignatureState::UNKNOWN);
6839 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
6840 if (!pObjectShell)
6841 return int(SignatureState::UNKNOWN);
6843 SolarMutexGuard aGuard;
6845 pObjectShell->RecheckSignature(false);
6847 return int(pObjectShell->GetDocumentSignatureState());
6850 static void doc_resizeWindow(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId,
6851 const int nWidth, const int nHeight)
6853 SolarMutexGuard aGuard;
6854 if (gImpl)
6855 gImpl->maLastExceptionMsg.clear();
6857 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
6858 if (!pWindow)
6860 gImpl->maLastExceptionMsg = "Document doesn't support dialog resizing, or window not found.";
6861 return;
6864 pWindow->SetSizePixel(Size(nWidth, nHeight));
6867 static void doc_completeFunction(LibreOfficeKitDocument* pThis, const char* pFunctionName)
6869 SolarMutexGuard aGuard;
6870 SetLastExceptionMsg();
6872 ITiledRenderable* pDoc = getTiledRenderable(pThis);
6873 if (!pDoc)
6875 SetLastExceptionMsg("Document doesn't support tiled rendering");
6876 return;
6879 pDoc->completeFunction(OUString::fromUtf8(pFunctionName));
6883 static void doc_sendFormFieldEvent(LibreOfficeKitDocument* pThis, const char* pArguments)
6885 SolarMutexGuard aGuard;
6887 // Supported in Writer only
6888 if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT)
6889 return;
6891 StringMap aMap(jsdialog::jsonToStringMap(pArguments));
6892 ITiledRenderable* pDoc = getTiledRenderable(pThis);
6893 if (!pDoc)
6895 SetLastExceptionMsg("Document doesn't support tiled rendering!");
6896 return;
6899 // Sanity check
6900 if (aMap.find("type") == aMap.end() || aMap.find("cmd") == aMap.end())
6902 SetLastExceptionMsg("Wrong arguments for sendFormFieldEvent!");
6903 return;
6906 pDoc->executeFromFieldEvent(aMap);
6909 static bool doc_renderSearchResult(LibreOfficeKitDocument* pThis,
6910 const char* pSearchResult, unsigned char** pBitmapBuffer,
6911 int* pWidth, int* pHeight, size_t* pByteSize)
6913 if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT)
6914 return false;
6916 if (pBitmapBuffer == nullptr)
6917 return false;
6919 if (!pSearchResult || pSearchResult[0] == '\0')
6920 return false;
6922 ITiledRenderable* pDoc = getTiledRenderable(pThis);
6923 if (!pDoc)
6925 SetLastExceptionMsg("Document doesn't support tiled rendering");
6926 return false;
6929 auto aRectangleVector = pDoc->getSearchResultRectangles(pSearchResult);
6931 // combine into a rectangle union
6932 basegfx::B2DRange aRangeUnion;
6933 for (basegfx::B2DRange const & rRange : aRectangleVector)
6935 aRangeUnion.expand(rRange);
6938 int aPixelWidth = o3tl::convert(aRangeUnion.getWidth(), o3tl::Length::twip, o3tl::Length::px);
6939 int aPixelHeight = o3tl::convert(aRangeUnion.getHeight(), o3tl::Length::twip, o3tl::Length::px);
6941 size_t nByteSize = aPixelWidth * aPixelHeight * 4;
6943 *pWidth = aPixelWidth;
6944 *pHeight = aPixelHeight;
6945 *pByteSize = nByteSize;
6947 auto* pBuffer = static_cast<unsigned char*>(std::malloc(nByteSize));
6949 doc_paintTile(pThis, pBuffer,
6950 aPixelWidth, aPixelHeight,
6951 aRangeUnion.getMinX(), aRangeUnion.getMinY(),
6952 aRangeUnion.getWidth(), aRangeUnion.getHeight());
6954 *pBitmapBuffer = pBuffer;
6956 return true;
6959 static void doc_sendContentControlEvent(LibreOfficeKitDocument* pThis, const char* pArguments)
6961 SolarMutexGuard aGuard;
6963 // Supported in Writer only
6964 if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT)
6966 return;
6969 StringMap aMap(jsdialog::jsonToStringMap(pArguments));
6970 ITiledRenderable* pDoc = getTiledRenderable(pThis);
6971 if (!pDoc)
6973 SetLastExceptionMsg("Document doesn't support tiled rendering");
6974 return;
6977 // Sanity check
6978 if (aMap.find("type") == aMap.end())
6980 SetLastExceptionMsg("Missing 'type' argument for sendContentControlEvent");
6981 return;
6984 pDoc->executeContentControlEvent(aMap);
6987 static void doc_setViewTimezone(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId,
6988 const char* pTimezone)
6990 comphelper::ProfileZone aZone("doc_setViewTimezone");
6992 SolarMutexGuard aGuard;
6993 SetLastExceptionMsg();
6995 // Leave the default if we get a null timezone.
6996 if (pTimezone)
6998 OUString sTimezone = OStringToOUString(pTimezone, RTL_TEXTENCODING_UTF8);
6999 SfxLokHelper::setViewTimezone(nId, true, sTimezone);
7003 static void doc_setAccessibilityState(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* pThis, int nId, bool nEnabled)
7005 SolarMutexGuard aGuard;
7006 SetLastExceptionMsg();
7008 int nDocType = getDocumentType(pThis);
7009 if (nDocType != LOK_DOCTYPE_TEXT)
7010 return;
7012 SfxLokHelper::setAccessibilityState(nId, nEnabled);
7015 static char* lo_getError (LibreOfficeKit *pThis)
7017 comphelper::ProfileZone aZone("lo_getError");
7019 SolarMutexGuard aGuard;
7021 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
7022 return convertOUString(pLib->maLastExceptionMsg);
7025 static void lo_freeError(char* pFree)
7027 free(pFree);
7030 static char* lo_getFilterTypes(LibreOfficeKit* pThis)
7032 SolarMutexGuard aGuard;
7033 SetLastExceptionMsg();
7035 LibLibreOffice_Impl* pImpl = static_cast<LibLibreOffice_Impl*>(pThis);
7037 if (!xSFactory.is())
7038 xSFactory = comphelper::getProcessServiceFactory();
7040 if (!xSFactory.is())
7042 pImpl->maLastExceptionMsg = "Service factory is not available";
7043 return nullptr;
7046 uno::Reference<container::XNameAccess> xTypeDetection(xSFactory->createInstance("com.sun.star.document.TypeDetection"), uno::UNO_QUERY);
7047 const uno::Sequence<OUString> aTypes = xTypeDetection->getElementNames();
7048 tools::JsonWriter aJson;
7049 for (const OUString& rType : aTypes)
7051 uno::Sequence<beans::PropertyValue> aValues;
7052 if (xTypeDetection->getByName(rType) >>= aValues)
7054 auto it = std::find_if(std::cbegin(aValues), std::cend(aValues), [](const beans::PropertyValue& rValue) { return rValue.Name == "MediaType"; });
7055 OUString aValue;
7056 if (it != std::cend(aValues) && (it->Value >>= aValue) && !aValue.isEmpty())
7058 auto typeNode = aJson.startNode(rType.toUtf8());
7059 aJson.put("MediaType", aValue.toUtf8());
7064 return strdup(aJson.finishAndGetAsOString().getStr());
7067 static void lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long const features)
7069 comphelper::ProfileZone aZone("lo_setOptionalFeatures");
7071 SolarMutexGuard aGuard;
7072 SetLastExceptionMsg();
7074 LibLibreOffice_Impl *const pLib = static_cast<LibLibreOffice_Impl*>(pThis);
7075 pLib->mOptionalFeatures = features;
7076 if (features & LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK)
7077 comphelper::LibreOfficeKit::setPartInInvalidation(true);
7078 if (features & LOK_FEATURE_NO_TILED_ANNOTATIONS)
7079 comphelper::LibreOfficeKit::setTiledAnnotations(false);
7080 if (features & LOK_FEATURE_RANGE_HEADERS)
7081 comphelper::LibreOfficeKit::setRangeHeaders(true);
7082 if (features & LOK_FEATURE_VIEWID_IN_VISCURSOR_INVALIDATION_CALLBACK)
7083 comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
7086 static void lo_setDocumentPassword(LibreOfficeKit* pThis,
7087 const char* pURL, const char* pPassword)
7089 comphelper::ProfileZone aZone("lo_setDocumentPassword");
7091 SolarMutexGuard aGuard;
7092 SetLastExceptionMsg();
7094 assert(pThis);
7095 assert(pURL);
7096 LibLibreOffice_Impl *const pLib = static_cast<LibLibreOffice_Impl*>(pThis);
7097 assert(pLib->mInteractionMap.find(OString(pURL)) != pLib->mInteractionMap.end());
7098 pLib->mInteractionMap.find(OString(pURL))->second->SetPassword(pPassword);
7101 static char* lo_getVersionInfo(SAL_UNUSED_PARAMETER LibreOfficeKit* /*pThis*/)
7103 SetLastExceptionMsg();
7104 return convertOUString(ReplaceStringHookProc(
7105 "{ "
7106 "\"ProductName\": \"%PRODUCTNAME\", "
7107 "\"ProductVersion\": \"%PRODUCTVERSION\", "
7108 "\"ProductExtension\": \"%PRODUCTEXTENSION\", "
7109 "\"BuildId\": \"%BUILDID\", "
7110 "\"BuildConfig\": \"" BUILDCONFIG "\" "
7111 "}"));
7114 static void aBasicErrorFunc(const OUString& rError, const OUString& rAction)
7116 OString aBuffer = "Unexpected dialog: " +
7117 OUStringToOString(rAction, RTL_TEXTENCODING_ASCII_US) +
7118 " Error: " +
7119 OUStringToOString(rError, RTL_TEXTENCODING_ASCII_US);
7121 fprintf(stderr, "Unexpected basic error dialog '%s'\n", aBuffer.getStr());
7124 static bool initialize_uno(const OUString& aAppProgramURL)
7126 #ifdef IOS
7127 // For iOS we already hardcode the inifile as "rc" in the .app directory.
7128 rtl::Bootstrap::setIniFilename(aAppProgramURL + "/" SAL_CONFIGFILE("fundamental"));
7129 xContext = cppu::defaultBootstrap_InitialComponentContext(aAppProgramURL + "/rc");
7130 #elif defined MACOSX
7131 rtl::Bootstrap::setIniFilename(aAppProgramURL + "/../Resources/" SAL_CONFIGFILE("soffice"));
7132 xContext = cppu::defaultBootstrap_InitialComponentContext();
7133 #else
7134 rtl::Bootstrap::setIniFilename(aAppProgramURL + "/" SAL_CONFIGFILE("soffice"));
7135 xContext = cppu::defaultBootstrap_InitialComponentContext();
7136 #endif
7138 if (!xContext.is())
7140 SetLastExceptionMsg("XComponentContext could not be created");
7141 SAL_INFO("lok", "XComponentContext could not be created");
7142 return false;
7145 xFactory = xContext->getServiceManager();
7146 if (!xFactory.is())
7148 SetLastExceptionMsg("XMultiComponentFactory could not be created");
7149 SAL_INFO("lok", "XMultiComponentFactory could not be created");
7150 return false;
7153 xSFactory.set(xFactory, uno::UNO_QUERY_THROW);
7154 comphelper::setProcessServiceFactory(xSFactory);
7156 SAL_INFO("lok", "Uno initialized - " << xContext.is());
7158 // set UserInstallation to user profile dir in test/user-template
7159 // rtl::Bootstrap aDefaultVars;
7160 // aDefaultVars.set(OUString("UserInstallation"), aAppProgramURL + "../registry" );
7161 // configmgr setup ?
7163 return true;
7166 // pre-unipoll version.
7167 static void lo_startmain(void*)
7169 osl_setThreadName("lo_startmain");
7171 if (comphelper::SolarMutex::get())
7172 Application::GetSolarMutex().tryToAcquire();
7174 Application::UpdateMainThread();
7176 soffice_main();
7178 Application::ReleaseSolarMutex();
7181 // unipoll version.
7182 static void lo_runLoop(LibreOfficeKit* /*pThis*/,
7183 LibreOfficeKitPollCallback pPollCallback,
7184 LibreOfficeKitWakeCallback pWakeCallback,
7185 void* pData)
7187 #if defined(IOS) || defined(ANDROID) || defined(__EMSCRIPTEN__)
7188 Application::GetSolarMutex().acquire();
7189 #endif
7192 SolarMutexGuard aGuard;
7194 vcl::lok::registerPollCallbacks(pPollCallback, pWakeCallback, pData);
7195 Application::UpdateMainThread();
7196 soffice_main();
7198 #if defined(IOS) || defined(ANDROID) || defined(__EMSCRIPTEN__)
7199 vcl::lok::unregisterPollCallbacks();
7200 Application::ReleaseSolarMutex();
7201 #endif
7204 static bool bInitialized = false;
7206 static void lo_status_indicator_callback(void *data, comphelper::LibreOfficeKit::statusIndicatorCallbackType type, int percent, const char* pText)
7208 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(data);
7210 if (!pLib->mpCallback)
7211 return;
7213 switch (type)
7215 case comphelper::LibreOfficeKit::statusIndicatorCallbackType::Start:
7216 pLib->mpCallback(LOK_CALLBACK_STATUS_INDICATOR_START, pText, pLib->mpCallbackData);
7217 break;
7218 case comphelper::LibreOfficeKit::statusIndicatorCallbackType::SetValue:
7219 pLib->mpCallback(LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE,
7220 OUString(OUString::number(percent)).toUtf8().getStr(), pLib->mpCallbackData);
7221 break;
7222 case comphelper::LibreOfficeKit::statusIndicatorCallbackType::Finish:
7223 pLib->mpCallback(LOK_CALLBACK_STATUS_INDICATOR_FINISH, nullptr, pLib->mpCallbackData);
7224 break;
7228 /// Used by preloadData (LibreOfficeKit) for providing different shortcuts for different languages.
7229 static void preLoadShortCutAccelerators()
7231 std::unordered_map<OUString, css::uno::Reference<com::sun::star::ui::XAcceleratorConfiguration>>& acceleratorConfs = SfxLokHelper::getAcceleratorConfs();
7232 css::uno::Sequence<OUString> installedLocales(officecfg::Setup::Office::InstalledLocales::get()->getElementNames());
7233 OUString actualLang = officecfg::Setup::L10N::ooLocale::get();
7235 for (sal_Int32 i = 0; i < installedLocales.getLength(); i++)
7237 OUString language = LanguageTag(installedLocales[i]).getLocale().Language;
7239 if (!comphelper::LibreOfficeKit::isAllowlistedLanguage(language))
7241 // Language is listed by COOL and also installed in core. We can create the short cut accelerator.
7243 // Set the UI language to current one, before creating the accelerator.
7244 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
7245 officecfg::Setup::L10N::ooLocale::set(installedLocales[i], batch);
7246 batch->commit();
7248 // Supported module names: Writer, Calc, Draw, Impress
7249 std::vector<OUString> supportedModuleNames = { "com.sun.star.text.TextDocument", "com.sun.star.sheet.SpreadsheetDocument", "com.sun.star.drawing.DrawingDocument", "com.sun.star.presentation.PresentationDocument" };
7250 // Create the accelerators.
7251 for (std::size_t j = 0; j < supportedModuleNames.size(); j++)
7253 OUString key = supportedModuleNames[j] + installedLocales[i];
7254 acceleratorConfs[key] = svt::AcceleratorExecute::lok_createNewAcceleratorConfiguration(::comphelper::getProcessComponentContext(), supportedModuleNames[j]);
7257 else
7259 std::cerr << "Language is installed in core but not in the list of COOL languages: " << language << "\n";
7263 // Set the UI language back to default one.
7264 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
7265 officecfg::Setup::L10N::ooLocale::set(actualLang, batch);
7266 batch->commit();
7269 /// Used only by LibreOfficeKit when used by Online to pre-initialize
7270 static void preloadData()
7272 comphelper::ProfileZone aZone("preload data");
7274 // Create user profile in the temp directory for loading the dictionaries
7275 OUString sUserPath;
7276 rtl::Bootstrap::get("UserInstallation", sUserPath);
7277 utl::TempFileNamed aTempDir(nullptr, true);
7278 aTempDir.EnableKillingFile();
7279 rtl::Bootstrap::set("UserInstallation", aTempDir.GetURL());
7281 // Register the bundled extensions
7282 desktop::Desktop::SynchronizeExtensionRepositories(true);
7283 bool bAbort = desktop::Desktop::CheckExtensionDependencies();
7284 if(bAbort)
7285 std::cerr << "CheckExtensionDependencies failed" << std::endl;
7287 // preload all available dictionaries
7288 css::uno::Reference<css::linguistic2::XLinguServiceManager> xLngSvcMgr =
7289 css::linguistic2::LinguServiceManager::create(comphelper::getProcessComponentContext());
7290 css::uno::Reference<linguistic2::XSpellChecker> xSpellChecker(xLngSvcMgr->getSpellChecker());
7292 std::cerr << "Preloading dictionaries: ";
7293 css::uno::Reference<linguistic2::XSupportedLocales> xSpellLocales(xSpellChecker, css::uno::UNO_QUERY_THROW);
7294 uno::Sequence< css::lang::Locale > aLocales = xSpellLocales->getLocales();
7295 for (auto &it : std::as_const(aLocales))
7297 std::cerr << LanguageTag::convertToBcp47(it) << " ";
7298 css::beans::PropertyValues aNone;
7299 xSpellChecker->isValid("forcefed", it, aNone);
7301 std::cerr << "\n";
7303 // Hack to load and cache the module liblocaledata_others.so which is not loaded normally
7304 // (when loading dictionaries of just non-Asian locales). Creating a XCalendar4 of one Asian locale
7305 // will cheaply load this missing "others" locale library. Appending an Asian locale in
7306 // LOK_ALLOWLIST_LANGUAGES env-var also works but at the cost of loading that dictionary.
7307 css::uno::Reference< css::i18n::XCalendar4 > xCal = css::i18n::LocaleCalendar2::create(comphelper::getProcessComponentContext());
7308 css::lang::Locale aAsianLocale = {"hi", "IN", ""};
7309 xCal->loadDefaultCalendar(aAsianLocale);
7311 // preload all available thesauri
7312 css::uno::Reference<linguistic2::XThesaurus> xThesaurus(xLngSvcMgr->getThesaurus());
7313 css::uno::Reference<linguistic2::XSupportedLocales> xThesLocales(xSpellChecker, css::uno::UNO_QUERY_THROW);
7314 aLocales = xThesLocales->getLocales();
7315 std::cerr << "Preloading thesauri: ";
7316 for (auto &it : std::as_const(aLocales))
7318 std::cerr << LanguageTag::convertToBcp47(it) << " ";
7319 css::beans::PropertyValues aNone;
7320 xThesaurus->queryMeanings("forcefed", it, aNone);
7322 std::cerr << "\n";
7324 css::uno::Reference< css::ui::XAcceleratorConfiguration > xGlobalCfg = css::ui::GlobalAcceleratorConfiguration::create(
7325 comphelper::getProcessComponentContext());
7326 xGlobalCfg->getAllKeyEvents();
7328 std::cerr << "Preload icons\n";
7329 ImageTree &images = ImageTree::get();
7330 images.getImageUrl("forcefed.png", "style", "FO_oo");
7332 std::cerr << "Preload short cut accelerators\n";
7333 preLoadShortCutAccelerators();
7335 std::cerr << "Preload languages\n";
7337 // force load language singleton
7338 SvtLanguageTable::HasLanguageType(LANGUAGE_SYSTEM);
7339 (void)LanguageTag::isValidBcp47("foo", nullptr);
7341 std::cerr << "Preload fonts\n";
7343 // Initialize fonts.
7344 css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv = css::linguistic2::LinguServiceManager::create(xContext);
7345 if (xLangSrv.is())
7347 css::uno::Reference<css::linguistic2::XSpellChecker> xSpell = xLangSrv->getSpellChecker();
7348 if (xSpell.is())
7349 aLocales = xSpell->getLocales();
7352 for (const auto& aLocale : std::as_const(aLocales))
7354 //TODO: Add more types and cache more aggressively. For now this initializes the fontcache.
7355 using namespace ::com::sun::star::i18n::ScriptType;
7356 LanguageType nLang;
7357 nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType(aLocale, false), LATIN);
7358 OutputDevice::GetDefaultFont(DefaultFontType::LATIN_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne);
7359 nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType(aLocale, false), ASIAN);
7360 OutputDevice::GetDefaultFont(DefaultFontType::CJK_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne);
7361 nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType(aLocale, false), COMPLEX);
7362 OutputDevice::GetDefaultFont(DefaultFontType::CTL_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne);
7365 std::cerr << "Preload config\n";
7366 #if defined __GNUC__ || defined __clang__
7367 #pragma GCC diagnostic push
7368 #pragma GCC diagnostic ignored "-Wunused-variable"
7369 #endif
7370 static SvtOptionsDialogOptions aDialogOptions;
7371 static SvtCTLOptions aSvtCTLOptions;
7372 static SvtAccessibilityOptions aSvtAccessibilityOptions;
7373 static svtools::ColorConfig aColorConfig;
7374 static SvtMiscOptions aSvtMiscOptions;
7375 static SvtSlideSorterBarOptions aSvtSlideSorterBarOptions;
7376 static SvtCommandOptions aSvtCommandOptions;
7377 static SvtCompatibilityOptions aSvtCompatibilityOptions;
7378 static SvtFilterOptions aSvtFilterOptions;
7379 static SvtLinguConfig aSvtLinguConfig;
7380 static SvtModuleOptions aSvtModuleOptions;
7381 static SvtPathOptions aSvtPathOptions;
7382 static SvtSearchOptions aSvtSearchOptions;
7383 static SvtSysLocaleOptions aSvtSysLocaleOptions;
7384 static SvtUserOptions aSvtUserOptions;
7385 //static SvtViewOptions aSvtViewOptions;
7386 static MouseSettings aMouseSettings;
7387 static StyleSettings aStyleSettings;
7388 static MiscSettings aMiscSettings;
7389 static HelpSettings aHelpSettings;
7390 static AllSettings aAllSettings;
7391 #if defined __GNUC__ || defined __clang__
7392 #pragma GCC diagnostic pop
7393 #endif
7395 // Set user profile's path back to the original one
7396 rtl::Bootstrap::set("UserInstallation", sUserPath);
7399 namespace {
7401 static void activateNotebookbar(std::u16string_view rApp)
7403 OUString aPath = OUString::Concat("org.openoffice.Office.UI.ToolbarMode/Applications/") + rApp;
7405 const utl::OConfigurationTreeRoot aAppNode(xContext, aPath, true);
7407 if (aAppNode.isValid())
7409 aAppNode.setNodeValue("Active", Any(OUString("notebookbar_online.ui")));
7410 aAppNode.commit();
7414 void setCertificateDir()
7416 const char* pEnvVarString = ::getenv("LO_CERTIFICATE_DATABASE_PATH");
7417 if (pEnvVarString)
7419 OUString aCertificateDatabasePath = OStringToOUString(pEnvVarString, RTL_TEXTENCODING_UTF8);
7422 std::shared_ptr<comphelper::ConfigurationChanges> pBatch(comphelper::ConfigurationChanges::create());
7423 officecfg::Office::Common::Security::Scripting::CertDir::set(aCertificateDatabasePath, pBatch);
7424 officecfg::Office::Common::Security::Scripting::ManualCertDir::set(aCertificateDatabasePath, pBatch);
7425 pBatch->commit();
7427 catch (uno::Exception const& rException)
7429 SAL_WARN("lok", "Failed to set the NSS certificate database directory: " << rException.Message);
7434 void setDeeplConfig()
7436 const char* pAPIUrlString = ::getenv("DEEPL_API_URL");
7437 const char* pAuthKeyString = ::getenv("DEEPL_AUTH_KEY");
7438 if (pAPIUrlString && pAuthKeyString)
7440 OUString aAPIUrl = OStringToOUString(pAPIUrlString, RTL_TEXTENCODING_UTF8);
7441 OUString aAuthKey = OStringToOUString(pAuthKeyString, RTL_TEXTENCODING_UTF8);
7444 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
7445 officecfg::Office::Linguistic::Translation::Deepl::ApiURL::set(aAPIUrl, batch);
7446 officecfg::Office::Linguistic::Translation::Deepl::AuthKey::set(aAuthKey, batch);
7447 batch->commit();
7449 catch(uno::Exception const& rException)
7451 SAL_WARN("lok", "Failed to set Deepl API settings: " << rException.Message);
7456 void setLanguageToolConfig()
7458 const char* pEnabled = ::getenv("LANGUAGETOOL_ENABLED");
7459 const char* pBaseUrlString = ::getenv("LANGUAGETOOL_BASEURL");
7461 if (pEnabled && pBaseUrlString)
7463 const char* pUsername = ::getenv("LANGUAGETOOL_USERNAME");
7464 const char* pApikey = ::getenv("LANGUAGETOOL_APIKEY");
7465 const char* pSSLVerification = ::getenv("LANGUAGETOOL_SSL_VERIFICATION");
7466 const char* pRestProtocol = ::getenv("LANGUAGETOOL_RESTPROTOCOL");
7468 OUString aEnabled = OStringToOUString(pEnabled, RTL_TEXTENCODING_UTF8);
7469 if (aEnabled != "true")
7470 return;
7471 OUString aBaseUrl = OStringToOUString(pBaseUrlString, RTL_TEXTENCODING_UTF8);
7474 using LanguageToolCfg = officecfg::Office::Linguistic::GrammarChecking::LanguageTool;
7475 auto batch(comphelper::ConfigurationChanges::create());
7477 LanguageToolCfg::BaseURL::set(aBaseUrl, batch);
7478 LanguageToolCfg::IsEnabled::set(true, batch);
7479 if (pSSLVerification)
7481 OUString aSSLVerification = OStringToOUString(pSSLVerification, RTL_TEXTENCODING_UTF8);
7482 LanguageToolCfg::SSLCertVerify::set(aSSLVerification == "true", batch);
7484 if (pRestProtocol)
7486 OUString aRestProtocol = OStringToOUString(pRestProtocol, RTL_TEXTENCODING_UTF8);
7487 LanguageToolCfg::RestProtocol::set(aRestProtocol, batch);
7489 if (pUsername && pApikey)
7491 OUString aUsername = OStringToOUString(pUsername, RTL_TEXTENCODING_UTF8);
7492 OUString aApiKey = OStringToOUString(pApikey, RTL_TEXTENCODING_UTF8);
7493 LanguageToolCfg::Username::set(aUsername, batch);
7494 LanguageToolCfg::ApiKey::set(aApiKey, batch);
7496 batch->commit();
7498 css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv =
7499 css::linguistic2::LinguServiceManager::create(xContext);
7500 if (xLangSrv.is())
7502 css::uno::Reference<css::linguistic2::XSpellChecker> xSpell = xLangSrv->getSpellChecker();
7503 if (xSpell.is())
7505 Sequence<OUString> aEmpty;
7506 Sequence<css::lang::Locale> aLocales = xSpell->getLocales();
7508 for (int itLocale = 0; itLocale < aLocales.getLength(); itLocale++)
7510 xLangSrv->setConfiguredServices(SN_SPELLCHECKER, aLocales[itLocale], aEmpty);
7515 catch(uno::Exception const& rException)
7517 SAL_WARN("lok", "Failed to set LanguageTool API settings: " << rException.Message);
7524 static int lo_initialize(LibreOfficeKit* pThis, const char* pAppPath, const char* pUserProfileUrl)
7526 enum {
7527 PRE_INIT, // setup shared data in master process
7528 SECOND_INIT, // complete init. after fork
7529 FULL_INIT // do a standard complete init.
7530 } eStage;
7532 // Did we do a pre-initialize
7533 static bool bPreInited = false;
7534 static bool bUnipoll = false;
7535 static bool bProfileZones = false;
7536 static bool bNotebookbar = false;
7538 { // cf. string lifetime for preinit
7539 std::vector<OUString> aOpts;
7541 // ':' delimited options - avoiding ABI change for new parameters
7542 const char *pOptions = getenv("SAL_LOK_OPTIONS");
7543 if (pOptions)
7544 aOpts = comphelper::string::split(OUString(pOptions, strlen(pOptions), RTL_TEXTENCODING_UTF8), ':');
7545 for (const auto &it : aOpts)
7547 if (it == "unipoll")
7548 bUnipoll = true;
7549 else if (it == "profile_events")
7550 bProfileZones = true;
7551 else if (it == "sc_no_grid_bg")
7552 comphelper::LibreOfficeKit::setCompatFlag(
7553 comphelper::LibreOfficeKit::Compat::scNoGridBackground);
7554 else if (it == "sc_print_twips_msgs")
7555 comphelper::LibreOfficeKit::setCompatFlag(
7556 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
7557 else if (it == "notebookbar")
7558 bNotebookbar = true;
7562 // What stage are we at ?
7563 if (pThis == nullptr)
7565 eStage = PRE_INIT;
7566 if (lok_preinit_2_called)
7568 SAL_INFO("lok", "Create libreoffice object");
7569 gImpl = new LibLibreOffice_Impl();
7572 else if (bPreInited)
7573 eStage = SECOND_INIT;
7574 else
7575 eStage = FULL_INIT;
7577 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
7579 if (bInitialized)
7580 return 1;
7582 // Turn profile zones on early
7583 if (bProfileZones && eStage == SECOND_INIT)
7585 comphelper::TraceEvent::startRecording();
7586 traceEventDumper = new TraceEventDumper();
7589 comphelper::ProfileZone aZone("lok-init");
7591 if (eStage == PRE_INIT)
7593 rtl_alloc_preInit(true);
7595 // Set the default timezone to the TZ envar, if set.
7596 const char* tz = ::getenv("TZ");
7597 SfxLokHelper::setDefaultTimezone(!!tz, tz ? OStringToOUString(tz, RTL_TEXTENCODING_UTF8)
7598 : OUString());
7601 if (eStage != SECOND_INIT)
7602 comphelper::LibreOfficeKit::setActive();
7604 if (eStage != PRE_INIT)
7605 comphelper::LibreOfficeKit::setStatusIndicatorCallback(lo_status_indicator_callback, pLib);
7607 if (pUserProfileUrl && eStage != PRE_INIT)
7609 OUString url(
7610 pUserProfileUrl, strlen(pUserProfileUrl), RTL_TEXTENCODING_UTF8);
7611 OUString path;
7612 if (url.startsWithIgnoreAsciiCase("vnd.sun.star.pathname:", &path))
7614 OUString url2;
7615 osl::FileBase::RC e = osl::FileBase::getFileURLFromSystemPath(
7616 path, url2);
7617 if (e == osl::FileBase::E_None)
7618 url = url2;
7619 else
7620 SAL_WARN("lok", "resolving <" << url << "> failed with " << +e);
7622 rtl::Bootstrap::set("UserInstallation", url);
7623 if (eStage == SECOND_INIT)
7624 utl::Bootstrap::reloadData();
7627 OUString aAppPath;
7628 if (pAppPath)
7630 aAppPath = OUString(pAppPath, strlen(pAppPath), RTL_TEXTENCODING_UTF8);
7632 else
7634 #if defined ANDROID || defined EMSCRIPTEN
7635 aAppPath = OUString::fromUtf8(lo_get_app_data_dir()) + "/program";
7636 #else
7637 // Fun conversion dance back and forth between URLs and system paths...
7638 OUString aAppURL;
7639 ::osl::Module::getUrlFromAddress( reinterpret_cast< oslGenericFunction >(lo_initialize),
7640 aAppURL);
7641 osl::FileBase::getSystemPathFromFileURL( aAppURL, aAppPath );
7642 #endif
7644 #ifdef IOS
7645 // The above gives something like
7646 // "/private/var/containers/Bundle/Application/953AA851-CC15-4C60-A2CB-C2C6F24E6F71/Foo.app/Foo",
7647 // and we want to drop the final component (the binary name).
7648 sal_Int32 lastSlash = aAppPath.lastIndexOf('/');
7649 assert(lastSlash > 0);
7650 aAppPath = aAppPath.copy(0, lastSlash);
7651 #endif
7654 OUString aAppURL;
7655 if (osl::FileBase::getFileURLFromSystemPath(aAppPath, aAppURL) != osl::FileBase::E_None)
7656 return 0;
7658 #ifdef IOS
7659 // A LibreOffice-using iOS app should have the ICU data file in the app bundle. Initialize ICU
7660 // to use that.
7661 NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
7663 int fd = open([[bundlePath stringByAppendingPathComponent:@"ICU.dat"] UTF8String], O_RDONLY);
7664 if (fd == -1)
7665 NSLog(@"Could not open ICU data file %s", [[bundlePath stringByAppendingPathComponent:@"ICU.dat"] UTF8String]);
7666 else
7668 struct stat st;
7669 if (fstat(fd, &st) == -1)
7670 NSLog(@"fstat on ICU data file failed: %s", strerror(errno));
7671 else
7673 void *icudata = mmap(0, (size_t) st.st_size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
7674 if (icudata == MAP_FAILED)
7675 NSLog(@"mmap failed: %s", strerror(errno));
7676 else
7678 UErrorCode icuStatus = U_ZERO_ERROR;
7679 udata_setCommonData(icudata, &icuStatus);
7680 if (U_FAILURE(icuStatus))
7681 NSLog(@"udata_setCommonData failed");
7682 else
7684 // Quick test that ICU works...
7685 UConverter *cnv = ucnv_open("iso-8859-3", &icuStatus);
7686 if (U_SUCCESS(icuStatus))
7687 ucnv_close(cnv);
7688 else
7689 NSLog(@"ucnv_open() failed: %s", u_errorName(icuStatus));
7693 close(fd);
7695 #endif
7699 if (eStage != SECOND_INIT)
7701 SAL_INFO("lok", "Attempting to initialize UNO");
7703 if (!initialize_uno(aAppURL))
7704 return false;
7706 // Force headless -- this is only for bitmap rendering.
7707 rtl::Bootstrap::set("SAL_USE_VCLPLUGIN", "svp");
7709 // We specifically need to make sure we have the "headless"
7710 // command arg set (various code specifically checks via
7711 // CommandLineArgs):
7712 desktop::Desktop::GetCommandLineArgs().setHeadless();
7714 #ifdef IOS
7715 if (InitVCL() && [NSThread isMainThread])
7717 static bool bFirstTime = true;
7718 if (bFirstTime)
7720 Application::GetSolarMutex().release();
7721 bFirstTime = false;
7724 SfxApplication::GetOrCreate();
7725 #endif
7727 #if HAVE_FEATURE_ANDROID_LOK
7728 // Register the bundled extensions - so that the dictionaries work
7729 desktop::Desktop::SynchronizeExtensionRepositories(false);
7730 bool bFailed = desktop::Desktop::CheckExtensionDependencies();
7731 if (bFailed)
7732 SAL_INFO("lok", "CheckExtensionDependencies failed");
7733 #endif
7735 if (eStage == PRE_INIT)
7738 comphelper::ProfileZone aInit("Init vcl");
7739 std::cerr << "Init vcl\n";
7740 InitVCL();
7743 // pre-load all component libraries.
7744 if (!xContext.is())
7745 throw css::uno::DeploymentException("preInit: XComponentContext is not created");
7747 css::uno::Reference< css::uno::XInterface > xService;
7748 xContext->getValueByName("/singletons/com.sun.star.lang.theServiceManager") >>= xService;
7749 if (!xService.is())
7750 throw css::uno::DeploymentException("preInit: XMultiComponentFactory is not created");
7752 css::uno::Reference<css::lang::XInitialization> aService(
7753 xService, css::uno::UNO_QUERY_THROW);
7755 // pre-requisites:
7756 // In order to load implementations and invoke
7757 // component factory it is required:
7758 // 1) defaultBootstrap_InitialComponentContext()
7759 // 2) comphelper::setProcessServiceFactory(xSFactory);
7760 // 3) InitVCL()
7762 comphelper::ProfileZone aInit("preload");
7763 aService->initialize({css::uno::Any(OUString("preload"))});
7765 { // Force load some modules
7766 comphelper::ProfileZone aInit("preload modules");
7767 VclBuilderPreload();
7768 VclAbstractDialogFactory::Create();
7771 preloadData();
7773 // Release Solar Mutex, lo_startmain thread should acquire it.
7774 Application::ReleaseSolarMutex();
7777 setLanguageAndLocale("en-US");
7780 if (eStage != PRE_INIT)
7782 SAL_INFO("lok", "Re-initialize temp paths");
7783 SvtPathOptions aOptions;
7784 OUString aNewTemp;
7785 osl::FileBase::getTempDirURL(aNewTemp);
7786 aOptions.SetTempPath(aNewTemp);
7787 desktop::Desktop::CreateTemporaryDirectory();
7789 // The RequestHandler is specifically set to be ready when all the other
7790 // init in Desktop::Main (run from soffice_main) is done. We can enable
7791 // the RequestHandler here (without starting any IPC thread;
7792 // shortcutting the invocation in Desktop::Main that would start the IPC
7793 // thread), and can then use it to wait until we're definitely ready to
7794 // continue.
7796 SAL_INFO("lok", "Enabling RequestHandler");
7797 RequestHandler::Enable(false);
7798 SAL_INFO("lok", "Starting soffice_main");
7799 RequestHandler::SetReady(false);
7800 if (!bUnipoll)
7802 // Start the main thread only in non-unipoll mode (i.e. multithreaded).
7803 pLib->maThread = osl_createThread(lo_startmain, nullptr);
7804 SAL_INFO("lok", "Waiting for RequestHandler");
7805 RequestHandler::WaitForReady();
7806 SAL_INFO("lok", "RequestHandler ready -- continuing");
7808 else
7809 InitVCL();
7812 if (eStage != SECOND_INIT)
7813 ErrorRegistry::RegisterDisplay(aBasicErrorFunc);
7815 SAL_INFO("lok", "LOK Initialized");
7816 if (eStage == PRE_INIT)
7817 bPreInited = true;
7818 else
7819 bInitialized = true;
7821 catch (css::uno::Exception& exception)
7823 fprintf(stderr, "Bootstrapping exception '%s'\n",
7824 OUStringToOString(exception.Message, RTL_TEXTENCODING_UTF8).getStr());
7827 if (eStage == PRE_INIT)
7829 comphelper::ThreadPool::getSharedOptimalPool().shutdown();
7832 // Turn off quick editing on iOS, Android and Emscripten
7833 #if defined IOS || defined ANDROID || defined __EMSCRIPTEN__
7834 if (officecfg::Office::Impress::Misc::TextObject::QuickEditing::get())
7836 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
7837 officecfg::Office::Impress::Misc::TextObject::QuickEditing::set(false, batch);
7838 batch->commit();
7840 #endif
7842 setCertificateDir();
7843 setDeeplConfig();
7844 setLanguageToolConfig();
7846 if (bNotebookbar)
7848 activateNotebookbar(u"Writer");
7849 activateNotebookbar(u"Calc");
7850 activateNotebookbar(u"Impress");
7851 activateNotebookbar(u"Draw");
7854 // staticize all strings.
7855 if (eStage == PRE_INIT)
7856 rtl_alloc_preInit(false);
7858 return bInitialized;
7861 SAL_JNI_EXPORT
7862 LibreOfficeKit *libreofficekit_hook_2(const char* install_path, const char* user_profile_url)
7864 static bool alreadyCalled = false;
7866 if ((!lok_preinit_2_called && !gImpl) || (lok_preinit_2_called && !alreadyCalled))
7868 alreadyCalled = true;
7870 if (!lok_preinit_2_called)
7872 SAL_INFO("lok", "Create libreoffice object");
7873 gImpl = new LibLibreOffice_Impl();
7876 if (!lo_initialize(gImpl, install_path, user_profile_url))
7878 lo_destroy(gImpl);
7881 return static_cast<LibreOfficeKit*>(gImpl);
7884 SAL_JNI_EXPORT
7885 LibreOfficeKit *libreofficekit_hook(const char* install_path)
7887 return libreofficekit_hook_2(install_path, nullptr);
7890 SAL_JNI_EXPORT
7891 int lok_preinit(const char* install_path, const char* user_profile_url)
7893 return lo_initialize(nullptr, install_path, user_profile_url);
7896 SAL_JNI_EXPORT
7897 int lok_preinit_2(const char* install_path, const char* user_profile_url, LibreOfficeKit** kit)
7899 lok_preinit_2_called = true;
7900 int result = lo_initialize(nullptr, install_path, user_profile_url);
7901 if (kit != nullptr)
7902 *kit = gImpl;
7903 return result;
7906 static void lo_destroy(LibreOfficeKit* pThis)
7908 SolarMutexClearableGuard aGuard;
7910 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
7911 gImpl = nullptr;
7913 SAL_INFO("lok", "LO Destroy");
7915 comphelper::LibreOfficeKit::setStatusIndicatorCallback(nullptr, nullptr);
7916 uno::Reference <frame::XDesktop2> xDesktop = frame::Desktop::create ( ::comphelper::getProcessComponentContext() );
7917 // FIXME: the terminate() call here is a no-op because it detects
7918 // that LibreOfficeKit::isActive() and then returns early!
7919 bool bSuccess = xDesktop.is() && xDesktop->terminate();
7921 if (!bSuccess)
7923 bSuccess = GetpApp() && GetpApp()->QueryExit();
7926 if (!bSuccess)
7928 Application::Quit();
7931 aGuard.clear();
7933 osl_joinWithThread(pLib->maThread);
7934 osl_destroyThread(pLib->maThread);
7936 delete pLib;
7937 bInitialized = false;
7938 SAL_INFO("lok", "LO Destroy Done");
7941 } // extern "C"
7943 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */