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