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