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