tdf#150789 - FILEOPEN PPTX: fix text in SmartArt vertically off
[LibreOffice.git] / sfx2 / source / view / lokhelper.cxx
blob5eec80fba5763992dbb3e9da67b3c1fed5c197d5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <sal/config.h>
12 #include <string>
13 #include <string_view>
14 #include <list>
15 #include <mutex>
17 #include <sfx2/lokcomponenthelpers.hxx>
18 #include <sfx2/lokhelper.hxx>
20 #include <com/sun/star/frame/Desktop.hpp>
21 #include <com/sun/star/ui/ContextChangeEventObject.hpp>
22 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
23 #include <com/sun/star/xml/crypto/XCertificateCreator.hpp>
25 #include <comphelper/processfactory.hxx>
26 #include <o3tl/string_view.hxx>
27 #include <rtl/strbuf.hxx>
28 #include <vcl/lok.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/commandevent.hxx>
31 #include <vcl/window.hxx>
32 #include <sal/log.hxx>
33 #include <sfx2/app.hxx>
34 #include <sfx2/msg.hxx>
35 #include <sfx2/viewsh.hxx>
36 #include <sfx2/request.hxx>
37 #include <sfx2/sfxsids.hrc>
38 #include <sfx2/viewfrm.hxx>
39 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
40 #include <comphelper/lok.hxx>
41 #include <sfx2/msgpool.hxx>
42 #include <comphelper/scopeguard.hxx>
43 #include <comphelper/base64.hxx>
44 #include <tools/json_writer.hxx>
45 #include <svl/cryptosign.hxx>
46 #include <tools/urlobj.hxx>
48 #include <boost/property_tree/json_parser.hpp>
50 using namespace com::sun::star;
52 namespace {
53 bool g_bSettingView(false);
55 /// Used to disable callbacks.
56 /// Needed to avoid recursion when switching views,
57 /// which can cause clients to invoke LOKit API and
58 /// implicitly set the view, which might cause an
59 /// infinite recursion if not detected and prevented.
60 class DisableCallbacks
62 public:
63 DisableCallbacks()
65 assert(m_nDisabled >= 0 && "Expected non-negative DisabledCallbacks state when disabling.");
66 ++m_nDisabled;
69 ~DisableCallbacks()
71 assert(m_nDisabled > 0 && "Expected positive DisabledCallbacks state when re-enabling.");
72 --m_nDisabled;
75 static inline bool disabled()
77 return !comphelper::LibreOfficeKit::isActive() || m_nDisabled != 0;
80 private:
81 static int m_nDisabled;
84 int DisableCallbacks::m_nDisabled = 0;
87 namespace
89 LanguageTag g_defaultLanguageTag(u"en-US"_ustr, true);
90 LanguageTag g_loadLanguageTag(u"en-US"_ustr, true); //< The language used to load.
91 LOKDeviceFormFactor g_deviceFormFactor = LOKDeviceFormFactor::UNKNOWN;
92 bool g_isDefaultTimezoneSet = false;
93 OUString g_DefaultTimezone;
94 const std::size_t g_logNotifierCacheMaxSize = 50;
95 ::std::list<::std::string> g_logNotifierCache;
98 int SfxLokHelper::createView(SfxViewFrame& rViewFrame, ViewShellDocId docId)
100 assert(docId >= ViewShellDocId(0) && "Cannot createView for invalid (negative) DocId.");
102 SfxViewShell::SetCurrentDocId(docId);
103 SfxRequest aRequest(rViewFrame, SID_NEWWINDOW);
104 rViewFrame.ExecView_Impl(aRequest);
105 SfxViewShell* pViewShell = SfxViewShell::Current();
106 if (pViewShell == nullptr)
107 return -1;
109 assert(pViewShell->GetDocId() == docId && "DocId must be already set!");
110 return static_cast<sal_Int32>(pViewShell->GetViewShellId());
113 int SfxLokHelper::createView()
115 // Assumes a single document, or at least that the
116 // current view belongs to the document on which the
117 // view will be created.
118 SfxViewShell* pViewShell = SfxViewShell::Current();
119 if (pViewShell == nullptr)
120 return -1;
122 return createView(pViewShell->GetViewFrame(), pViewShell->GetDocId());
125 std::unordered_map<OUString, css::uno::Reference<css::ui::XAcceleratorConfiguration>>& SfxLokHelper::getAcceleratorConfs()
127 return SfxApplication::GetOrCreate()->GetAcceleratorConfs_Impl();
130 int SfxLokHelper::createView(int nDocId)
132 const SfxApplication* pApp = SfxApplication::Get();
133 if (pApp == nullptr)
134 return -1;
136 // Find a shell with the given DocId.
137 const ViewShellDocId docId(nDocId);
138 for (const SfxViewShell* pViewShell : pApp->GetViewShells_Impl())
140 if (pViewShell->GetDocId() == docId)
141 return createView(pViewShell->GetViewFrame(), docId);
144 // No frame with nDocId found.
145 return -1;
148 void SfxLokHelper::setEditMode(int nMode, vcl::ITiledRenderable* pDoc)
150 DisableCallbacks dc;
151 pDoc->setEditMode(nMode);
154 void SfxLokHelper::destroyView(int nId)
156 const SfxApplication* pApp = SfxApplication::Get();
157 if (pApp == nullptr)
158 return;
160 const ViewShellId nViewShellId(nId);
161 std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
163 for (SfxViewShell* pViewShell : rViewArr)
165 if (pViewShell->GetViewShellId() == nViewShellId)
167 pViewShell->SetLOKAccessibilityState(false);
168 SfxViewFrame& rViewFrame = pViewShell->GetViewFrame();
169 SfxRequest aRequest(rViewFrame, SID_CLOSEWIN);
170 rViewFrame.Exec_Impl(aRequest);
171 break;
176 bool SfxLokHelper::isSettingView()
178 return g_bSettingView;
181 void SfxLokHelper::setView(int nId)
183 g_bSettingView = true;
184 comphelper::ScopeGuard g([] { g_bSettingView = false; });
186 SfxApplication* pApp = SfxApplication::Get();
187 if (pApp == nullptr)
188 return;
190 const ViewShellId nViewShellId(nId);
191 std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
193 const auto itViewShell = std::find_if(rViewArr.begin(), rViewArr.end(), [nViewShellId](SfxViewShell* pViewShell){ return pViewShell->GetViewShellId() == nViewShellId; });
194 if (itViewShell == rViewArr.end())
195 return;
197 const SfxViewShell* pViewShell = *itViewShell;
198 assert(pViewShell);
199 DisableCallbacks dc;
201 bool bIsCurrShell = (pViewShell == SfxViewShell::Current());
202 if (bIsCurrShell && comphelper::LibreOfficeKit::getLanguageTag().getBcp47() == pViewShell->GetLOKLanguageTag().getBcp47())
203 return;
205 if (bIsCurrShell)
207 // If we wanted to set the SfxViewShell that is actually set, we could skip it.
208 // But it looks like that the language can go wrong, so we have to fix that.
209 // This can happen, when someone sets the language or SfxViewShell::Current() separately.
210 SAL_WARN("lok", "LANGUAGE mismatch at setView! ... old (wrong) lang:"
211 << comphelper::LibreOfficeKit::getLanguageTag().getBcp47()
212 << " new lang:" << pViewShell->GetLOKLanguageTag().getBcp47());
215 // update the current LOK language and locale for the dialog tunneling
216 comphelper::LibreOfficeKit::setLanguageTag(pViewShell->GetLOKLanguageTag());
217 comphelper::LibreOfficeKit::setLocale(pViewShell->GetLOKLocale());
219 if (bIsCurrShell)
220 return;
222 SfxViewFrame& rViewFrame = pViewShell->GetViewFrame();
223 rViewFrame.MakeActive_Impl(false);
225 // Make comphelper::dispatchCommand() find the correct frame.
226 uno::Reference<frame::XFrame> xFrame = rViewFrame.GetFrame().GetFrameInterface();
227 uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext());
228 xDesktop->setActiveFrame(xFrame);
231 SfxViewShell* SfxLokHelper::getViewOfId(int nId)
233 SfxApplication* pApp = SfxApplication::Get();
234 if (pApp == nullptr)
235 return nullptr;
237 const ViewShellId nViewShellId(nId);
238 std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
239 for (SfxViewShell* pViewShell : rViewArr)
241 if (pViewShell->GetViewShellId() == nViewShellId)
242 return pViewShell;
245 return nullptr;
248 int SfxLokHelper::getView(const SfxViewShell* pViewShell)
250 if (!pViewShell)
251 pViewShell = SfxViewShell::Current();
252 // Still no valid view shell? Then no idea.
253 if (!pViewShell)
254 return -1;
256 return static_cast<sal_Int32>(pViewShell->GetViewShellId());
259 std::size_t SfxLokHelper::getViewsCount(int nDocId)
261 assert(nDocId != -1 && "Cannot getViewsCount for invalid DocId -1");
263 SfxApplication* pApp = SfxApplication::Get();
264 if (!pApp)
265 return 0;
267 const ViewShellDocId nCurrentDocId(nDocId);
268 std::size_t n = 0;
269 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
270 while (pViewShell)
272 if (pViewShell->GetDocId() == nCurrentDocId)
273 n++;
274 pViewShell = SfxViewShell::GetNext(*pViewShell);
277 return n;
280 bool SfxLokHelper::getViewIds(int nDocId, int* pArray, size_t nSize)
282 assert(nDocId != -1 && "Cannot getViewsIds for invalid DocId -1");
284 SfxApplication* pApp = SfxApplication::Get();
285 if (!pApp)
286 return false;
288 const ViewShellDocId nCurrentDocId(nDocId);
289 std::size_t n = 0;
290 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
291 while (pViewShell)
293 if (pViewShell->GetDocId() == nCurrentDocId)
295 if (n == nSize)
296 return false;
298 pArray[n] = static_cast<sal_Int32>(pViewShell->GetViewShellId());
299 n++;
302 pViewShell = SfxViewShell::GetNext(*pViewShell);
305 return true;
308 int SfxLokHelper::getDocumentIdOfView(int nViewId)
310 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
311 while (pViewShell)
313 if (pViewShell->GetViewShellId() == ViewShellId(nViewId))
314 return static_cast<int>(pViewShell->GetDocId());
315 pViewShell = SfxViewShell::GetNext(*pViewShell);
317 return -1;
320 const LanguageTag & SfxLokHelper::getDefaultLanguage()
322 return g_defaultLanguageTag;
325 void SfxLokHelper::setDefaultLanguage(const OUString& rBcp47LanguageTag)
327 g_defaultLanguageTag = LanguageTag(rBcp47LanguageTag, true);
330 const LanguageTag& SfxLokHelper::getLoadLanguage() { return g_loadLanguageTag; }
332 void SfxLokHelper::setLoadLanguage(const OUString& rBcp47LanguageTag)
334 g_loadLanguageTag = LanguageTag(rBcp47LanguageTag, true);
337 void SfxLokHelper::setViewLanguage(int nId, const OUString& rBcp47LanguageTag)
339 std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
341 for (SfxViewShell* pViewShell : rViewArr)
343 if (pViewShell->GetViewShellId() == ViewShellId(nId))
345 pViewShell->SetLOKLanguageTag(rBcp47LanguageTag);
346 // sync also global getter if we are the current view
347 bool bIsCurrShell = (pViewShell == SfxViewShell::Current());
348 if (bIsCurrShell)
349 comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(rBcp47LanguageTag));
350 return;
355 void SfxLokHelper::setViewReadOnly(int nId, bool readOnly)
357 std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
359 for (SfxViewShell* pViewShell : rViewArr)
361 if (pViewShell && pViewShell->GetViewShellId() == ViewShellId(nId))
363 LOK_INFO("lok.readonlyview", "SfxLokHelper::setViewReadOnly: view id: " << nId << ", readOnly: " << readOnly);
364 pViewShell->SetLokReadOnlyView(readOnly);
365 return;
370 void SfxLokHelper::setAllowChangeComments(int nId, bool allow)
372 std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
374 for (SfxViewShell* pViewShell : rViewArr)
376 if (pViewShell && pViewShell->GetViewShellId() == ViewShellId(nId))
378 LOK_INFO("lok.readonlyview", "SfxLokHelper::setAllowChangeComments: view id: " << nId << ", allow: " << allow);
379 pViewShell->SetAllowChangeComments(allow);
380 return;
385 void SfxLokHelper::setAccessibilityState(int nId, bool nEnabled)
387 std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
389 for (SfxViewShell* pViewShell : rViewArr)
391 if (pViewShell && pViewShell->GetViewShellId() == ViewShellId(nId))
393 LOK_INFO("lok.a11y", "SfxLokHelper::setAccessibilityState: view id: " << nId << ", nEnabled: " << nEnabled);
394 pViewShell->SetLOKAccessibilityState(nEnabled);
395 return;
400 void SfxLokHelper::setViewLocale(int nId, const OUString& rBcp47LanguageTag)
402 std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
404 for (SfxViewShell* pViewShell : rViewArr)
406 if (pViewShell->GetViewShellId() == ViewShellId(nId))
408 pViewShell->SetLOKLocale(rBcp47LanguageTag);
409 return;
414 LOKDeviceFormFactor SfxLokHelper::getDeviceFormFactor()
416 return g_deviceFormFactor;
419 void SfxLokHelper::setDeviceFormFactor(std::u16string_view rDeviceFormFactor)
421 if (rDeviceFormFactor == u"desktop")
422 g_deviceFormFactor = LOKDeviceFormFactor::DESKTOP;
423 else if (rDeviceFormFactor == u"tablet")
424 g_deviceFormFactor = LOKDeviceFormFactor::TABLET;
425 else if (rDeviceFormFactor == u"mobile")
426 g_deviceFormFactor = LOKDeviceFormFactor::MOBILE;
427 else
428 g_deviceFormFactor = LOKDeviceFormFactor::UNKNOWN;
431 void SfxLokHelper::setDefaultTimezone(bool isSet, const OUString& rTimezone)
433 g_isDefaultTimezoneSet = isSet;
434 g_DefaultTimezone = rTimezone;
437 std::pair<bool, OUString> SfxLokHelper::getDefaultTimezone()
439 return { g_isDefaultTimezoneSet, g_DefaultTimezone };
442 void SfxLokHelper::setViewTimezone(int nId, bool isSet, const OUString& rTimezone)
444 std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
446 for (SfxViewShell* pViewShell : rViewArr)
448 if (pViewShell->GetViewShellId() == ViewShellId(nId))
450 pViewShell->SetLOKTimezone(isSet, rTimezone);
451 return;
456 std::pair<bool, OUString> SfxLokHelper::getViewTimezone(int nId)
458 std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
460 for (SfxViewShell* pViewShell : rViewArr)
462 if (pViewShell->GetViewShellId() == ViewShellId(nId))
464 return pViewShell->GetLOKTimezone();
468 return {};
472 * Used for putting a whole JSON string into a string value
473 * e.g { key: "{JSON}" }
475 static OString lcl_sanitizeJSONAsValue(const OString &rStr)
477 if (rStr.getLength() < 1)
478 return rStr;
479 // FIXME: need an optimized 'escape' method for O[U]String.
480 OStringBuffer aBuf(rStr.getLength() + 8);
481 for (sal_Int32 i = 0; i < rStr.getLength(); ++i)
483 if (rStr[i] == '"' || rStr[i] == '\\')
484 aBuf.append('\\');
486 if (rStr[i] != '\n')
487 aBuf.append(rStr[i]);
489 return aBuf.makeStringAndClear();
492 static OString lcl_generateJSON(const SfxViewShell* pView, const boost::property_tree::ptree& rTree)
494 assert(pView != nullptr && "pView must be valid");
495 boost::property_tree::ptree aMessageProps = rTree;
496 aMessageProps.put("viewId", SfxLokHelper::getView(pView));
497 aMessageProps.put("part", pView->getPart());
498 aMessageProps.put("mode", pView->getEditMode());
499 std::stringstream aStream;
500 boost::property_tree::write_json(aStream, aMessageProps, false /* pretty */);
501 return OString(o3tl::trim(aStream.str()));
504 static inline OString lcl_generateJSON(const SfxViewShell* pView, int nViewId, std::string_view rKey,
505 const OString& rPayload)
507 assert(pView != nullptr && "pView must be valid");
508 return OString::Concat("{ \"viewId\": \"") + OString::number(nViewId)
509 + "\", \"part\": \"" + OString::number(pView->getPart()) + "\", \"mode\": \""
510 + OString::number(pView->getEditMode()) + "\", \"" + rKey + "\": \""
511 + lcl_sanitizeJSONAsValue(rPayload) + "\" }";
514 static inline OString lcl_generateJSON(const SfxViewShell* pView, std::string_view rKey,
515 const OString& rPayload)
517 return lcl_generateJSON(pView, SfxLokHelper::getView(pView), rKey, rPayload);
520 void SfxLokHelper::notifyOtherView(const SfxViewShell* pThisView, SfxViewShell const* pOtherView,
521 int nType, std::string_view rKey, const OString& rPayload)
523 assert(pThisView != nullptr && "pThisView must be valid");
524 if (DisableCallbacks::disabled())
525 return;
527 const OString aPayload = lcl_generateJSON(pThisView, rKey, rPayload);
528 const int viewId = SfxLokHelper::getView(pThisView);
529 pOtherView->libreOfficeKitViewCallbackWithViewId(nType, aPayload, viewId);
532 void SfxLokHelper::notifyOtherView(const SfxViewShell* pThisView, SfxViewShell const* pOtherView,
533 int nType, const boost::property_tree::ptree& rTree)
535 assert(pThisView != nullptr && "pThisView must be valid");
536 if (DisableCallbacks::disabled() || !pOtherView)
537 return;
539 const int viewId = SfxLokHelper::getView(pThisView);
540 pOtherView->libreOfficeKitViewCallbackWithViewId(nType, lcl_generateJSON(pThisView, rTree), viewId);
543 void SfxLokHelper::notifyOtherViews(const SfxViewShell* pThisView, int nType, std::string_view rKey,
544 const OString& rPayload)
546 assert(pThisView != nullptr && "pThisView must be valid");
547 if (DisableCallbacks::disabled())
548 return;
550 // Cache the payload so we only have to generate it once, at most.
551 OString aPayload;
552 int viewId = -1;
554 const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
555 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
556 while (pViewShell)
558 if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
560 // Payload is only dependent on pThisView.
561 if (aPayload.isEmpty())
563 aPayload = lcl_generateJSON(pThisView, rKey, rPayload);
564 viewId = SfxLokHelper::getView(pThisView);
567 pViewShell->libreOfficeKitViewCallbackWithViewId(nType, aPayload, viewId);
570 pViewShell = SfxViewShell::GetNext(*pViewShell);
574 void SfxLokHelper::notifyOtherViews(const SfxViewShell* pThisView, int nType,
575 const boost::property_tree::ptree& rTree)
577 assert(pThisView != nullptr && "pThisView must be valid");
578 if (!pThisView || DisableCallbacks::disabled())
579 return;
581 // Cache the payload so we only have to generate it once, at most.
582 OString aPayload;
583 int viewId = -1;
585 const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
586 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
587 while (pViewShell)
589 if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
591 // Payload is only dependent on pThisView.
592 if (aPayload.isEmpty())
594 aPayload = lcl_generateJSON(pThisView, rTree);
595 viewId = SfxLokHelper::getView(pThisView);
598 pViewShell->libreOfficeKitViewCallbackWithViewId(nType, aPayload, viewId);
601 pViewShell = SfxViewShell::GetNext(*pViewShell);
605 OString SfxLokHelper::makePayloadJSON(const SfxViewShell* pThisView, int nViewId, std::string_view rKey, const OString& rPayload)
607 return lcl_generateJSON(pThisView, nViewId, rKey, rPayload);
610 namespace {
611 OUString lcl_getNameForSlot(const SfxViewShell* pShell, sal_uInt16 nWhich)
613 if (pShell && pShell->GetFrame())
615 const SfxSlot* pSlot = SfxSlotPool::GetSlotPool(pShell->GetFrame()).GetSlot(nWhich);
616 if (pSlot)
618 if (!pSlot->GetUnoName().isEmpty())
620 return pSlot->GetCommand();
625 return u""_ustr;
629 void SfxLokHelper::sendUnoStatus(const SfxViewShell* pShell, const SfxPoolItem* pItem)
631 if (!pShell || !pItem || IsInvalidItem(pItem) || DisableCallbacks::disabled())
632 return;
634 boost::property_tree::ptree aItem = pItem->dumpAsJSON();
636 if (aItem.count("state"))
638 OUString sCommand = lcl_getNameForSlot(pShell, pItem->Which());
639 if (!sCommand.isEmpty())
640 aItem.put("commandName", sCommand);
642 std::stringstream aStream;
643 boost::property_tree::write_json(aStream, aItem);
644 pShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, OString(aStream.str()));
648 void SfxLokHelper::notifyViewRenderState(const SfxViewShell* pShell, vcl::ITiledRenderable* pDoc)
650 pShell->libreOfficeKitViewCallback(LOK_CALLBACK_VIEW_RENDER_STATE, pDoc->getViewRenderState());
653 void SfxLokHelper::notifyWindow(const SfxViewShell* pThisView,
654 vcl::LOKWindowId nLOKWindowId,
655 std::u16string_view rAction,
656 const std::vector<vcl::LOKPayloadItem>& rPayload)
658 assert(pThisView != nullptr && "pThisView must be valid");
660 if (nLOKWindowId == 0 || DisableCallbacks::disabled())
661 return;
663 OStringBuffer aPayload =
664 "{ \"id\": \"" + OString::number(nLOKWindowId) + "\""
665 ", \"action\": \"" + OUStringToOString(rAction, RTL_TEXTENCODING_UTF8) + "\"";
667 for (const auto& rItem: rPayload)
669 if (!rItem.first.isEmpty() && !rItem.second.isEmpty())
671 auto aFirst = rItem.first.replaceAll("\""_ostr, "\\\""_ostr);
672 auto aSecond = rItem.second.replaceAll("\""_ostr, "\\\""_ostr);
673 aPayload.append(", \"" + aFirst + "\": \"" + aSecond + "\"");
676 aPayload.append('}');
678 const OString s = aPayload.makeStringAndClear();
679 pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_WINDOW, s);
682 void SfxLokHelper::notifyInvalidation(SfxViewShell const* pThisView, tools::Rectangle const* pRect)
684 // -1 means all parts
685 const int nPart = comphelper::LibreOfficeKit::isPartInInvalidation() ? pThisView->getPart() : INT_MIN;
686 SfxLokHelper::notifyInvalidation(pThisView, nPart, pRect);
689 void SfxLokHelper::notifyInvalidation(SfxViewShell const* pThisView, const int nInPart, tools::Rectangle const* pRect)
691 if (DisableCallbacks::disabled())
692 return;
694 // -1 means all parts
695 const int nPart = comphelper::LibreOfficeKit::isPartInInvalidation() ? nInPart : INT_MIN;
696 const int nMode = pThisView->getEditMode();
697 pThisView->libreOfficeKitViewInvalidateTilesCallback(pRect, nPart, nMode);
700 void SfxLokHelper::notifyDocumentSizeChanged(SfxViewShell const* pThisView, const OString& rPayload, vcl::ITiledRenderable* pDoc, bool bInvalidateAll)
702 if (!pDoc || pDoc->isDisposed() || DisableCallbacks::disabled())
703 return;
705 if (bInvalidateAll)
707 for (int i = 0; i < pDoc->getParts(); ++i)
709 tools::Rectangle aRectangle(0, 0, 1000000000, 1000000000);
710 const int nMode = pThisView->getEditMode();
711 pThisView->libreOfficeKitViewInvalidateTilesCallback(&aRectangle, i, nMode);
714 pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_SIZE_CHANGED, rPayload);
717 void SfxLokHelper::notifyDocumentSizeChangedAllViews(vcl::ITiledRenderable* pDoc, bool bInvalidateAll)
719 if (DisableCallbacks::disabled())
720 return;
722 // FIXME: Do we know whether it is the views for the document that is in the "current" view that has changed?
723 const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
724 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
725 while (pViewShell)
727 // FIXME: What if SfxViewShell::Current() returned null?
728 // Should we then do this for all views of all open documents
729 // or not?
730 if (pCurrentViewShell == nullptr || pViewShell->GetDocId() == pCurrentViewShell-> GetDocId())
732 SfxLokHelper::notifyDocumentSizeChanged(pViewShell, ""_ostr, pDoc, bInvalidateAll);
733 bInvalidateAll = false; // we direct invalidations to all views anyway.
735 pViewShell = SfxViewShell::GetNext(*pViewShell);
739 void SfxLokHelper::notifyPartSizeChangedAllViews(vcl::ITiledRenderable* pDoc, int nPart)
741 if (DisableCallbacks::disabled())
742 return;
744 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
745 while (pViewShell)
747 if (// FIXME should really filter on pViewShell->GetDocId() too
748 pViewShell->getPart() == nPart)
749 SfxLokHelper::notifyDocumentSizeChanged(pViewShell, ""_ostr, pDoc, false);
750 pViewShell = SfxViewShell::GetNext(*pViewShell);
754 OString SfxLokHelper::makeVisCursorInvalidation(int nViewId, const OString& rRectangle,
755 bool bMispelledWord, const OString& rHyperlink)
757 if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
759 OString sHyperlink = rHyperlink.isEmpty() ? "{}"_ostr : rHyperlink;
760 return OString::Concat("{ \"viewId\": \"") + OString::number(nViewId) +
761 "\", \"rectangle\": \"" + rRectangle +
762 "\", \"mispelledWord\": \"" + OString::number(bMispelledWord ? 1 : 0) +
763 "\", \"hyperlink\": " + sHyperlink + " }";
765 else
767 return rRectangle;
771 void SfxLokHelper::notifyAllViews(int nType, const OString& rPayload)
773 if (DisableCallbacks::disabled())
774 return;
776 const auto payload = rPayload.getStr();
777 const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
778 if (!pCurrentViewShell)
779 return;
780 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
781 while (pViewShell)
783 if (pViewShell->GetDocId() == pCurrentViewShell->GetDocId())
784 pViewShell->libreOfficeKitViewCallback(nType, payload);
785 pViewShell = SfxViewShell::GetNext(*pViewShell);
789 void SfxLokHelper::notifyContextChange(const css::ui::ContextChangeEventObject& rEvent)
791 if (DisableCallbacks::disabled())
792 return;
794 SfxViewShell* pViewShell = SfxViewShell::Get({ rEvent.Source, css::uno::UNO_QUERY });
795 if (!pViewShell)
796 return;
798 OUString aBuffer =
799 rEvent.ApplicationName.replace(' ', '_') +
800 " " +
801 rEvent.ContextName.replace(' ', '_');
802 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_CHANGED, aBuffer.toUtf8());
805 void SfxLokHelper::notifyLog(const std::ostringstream& stream)
807 if (DisableCallbacks::disabled())
808 return;
810 SfxViewShell* pViewShell = SfxViewShell::Current();
811 if (!pViewShell)
812 return;
813 if (pViewShell->getLibreOfficeKitViewCallback())
815 if (!g_logNotifierCache.empty())
817 for (const auto& msg : g_logNotifierCache)
819 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CORE_LOG, msg.c_str());
821 g_logNotifierCache.clear();
823 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CORE_LOG, stream.str().c_str());
825 else
827 while (g_logNotifierCache.size() >= g_logNotifierCacheMaxSize)
828 g_logNotifierCache.pop_front();
829 g_logNotifierCache.push_back(stream.str());
833 namespace
835 std::string extractCertificateWithOffset(const std::string& certificate, size_t& rOffset)
837 static constexpr std::string_view header("-----BEGIN CERTIFICATE-----");
838 static constexpr std::string_view footer("-----END CERTIFICATE-----");
840 std::string result;
842 size_t pos1 = certificate.find(header, rOffset);
843 if (pos1 == std::string::npos)
844 return result;
846 size_t pos2 = certificate.find(footer, pos1 + 1);
847 if (pos2 == std::string::npos)
848 return result;
850 pos1 = pos1 + header.length();
851 size_t len = pos2 - pos1;
853 rOffset = pos2;
854 return certificate.substr(pos1, len);
858 std::string SfxLokHelper::extractCertificate(const std::string & certificate)
860 size_t nOffset = 0;
861 return extractCertificateWithOffset(certificate, nOffset);
864 std::vector<std::string> SfxLokHelper::extractCertificates(const std::string& rCerts)
866 std::vector<std::string> aRet;
867 size_t nOffset = 0;
868 while (true)
870 std::string aNext = extractCertificateWithOffset(rCerts, nOffset);
871 if (aNext.empty())
873 break;
876 aRet.push_back(aNext);
878 return aRet;
881 namespace
883 std::string extractKey(const std::string & privateKey)
885 static constexpr std::string_view header("-----BEGIN PRIVATE KEY-----");
886 static constexpr std::string_view footer("-----END PRIVATE KEY-----");
888 std::string result;
890 size_t pos1 = privateKey.find(header);
891 if (pos1 == std::string::npos)
892 return result;
894 size_t pos2 = privateKey.find(footer, pos1 + 1);
895 if (pos2 == std::string::npos)
896 return result;
898 pos1 = pos1 + header.length();
899 pos2 = pos2 - pos1;
901 return privateKey.substr(pos1, pos2);
905 css::uno::Reference<css::security::XCertificate> SfxLokHelper::getSigningCertificate(const std::string& rCert, const std::string& rKey)
907 const uno::Reference<uno::XComponentContext>& xContext = comphelper::getProcessComponentContext();
908 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
909 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
910 if (!xSecurityContext.is())
912 return {};
915 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
916 uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
918 if (!xCertificateCreator.is())
920 return {};
923 uno::Sequence<sal_Int8> aCertificateSequence;
925 std::string aCertificateBase64String = extractCertificate(rCert);
926 if (!aCertificateBase64String.empty())
928 OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String);
929 comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
931 else
933 aCertificateSequence.realloc(rCert.size());
934 std::copy(rCert.c_str(), rCert.c_str() + rCert.size(), aCertificateSequence.getArray());
937 uno::Sequence<sal_Int8> aPrivateKeySequence;
938 std::string aPrivateKeyBase64String = extractKey(rKey);
939 if (!aPrivateKeyBase64String.empty())
941 OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String);
942 comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString);
944 else
946 aPrivateKeySequence.realloc(rKey.size());
947 std::copy(rKey.c_str(), rKey.c_str() + rKey.size(), aPrivateKeySequence.getArray());
950 uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence);
951 return xCertificate;
954 uno::Reference<security::XCertificate> SfxLokHelper::addCertificate(
955 const css::uno::Reference<css::xml::crypto::XCertificateCreator>& xCertificateCreator,
956 const css::uno::Sequence<sal_Int8>& rCert)
958 // Trust arg is handled by CERT_DecodeTrustString(), see 'man certutil'.
959 return xCertificateCreator->addDERCertificateToTheDatabase(rCert, u"TCu,Cu,Tu"_ustr);
962 void SfxLokHelper::addCertificates(const std::vector<std::string>& rCerts)
964 const uno::Reference<uno::XComponentContext>& xContext = comphelper::getProcessComponentContext();
965 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
966 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
967 if (!xSecurityContext.is())
969 return;
972 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
973 uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
974 if (!xCertificateCreator.is())
976 return;
979 for (const auto& rCert : rCerts)
981 uno::Sequence<sal_Int8> aCertificateSequence;
982 OUString aBase64OUString = OUString::fromUtf8(rCert);
983 comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
984 addCertificate(xCertificateCreator, aCertificateSequence);
987 // Update the signature state, perhaps the signing certificate is now trusted.
988 SfxObjectShell* pObjectShell = SfxObjectShell::Current();
989 if (!pObjectShell)
991 return;
994 pObjectShell->RecheckSignature(false);
997 bool SfxLokHelper::supportsCommand(std::u16string_view rCommand)
999 static const std::initializer_list<std::u16string_view> vSupport = { u"Signature" };
1001 return std::find(vSupport.begin(), vSupport.end(), rCommand) != vSupport.end();
1004 std::map<OUString, OUString> SfxLokHelper::parseCommandParameters(std::u16string_view rCommand)
1006 std::map<OUString, OUString> aMap;
1008 INetURLObject aParser(rCommand);
1009 OUString aArguments = aParser.GetParam();
1010 sal_Int32 nParamIndex = 0;
1013 std::u16string_view aParam = o3tl::getToken(aArguments, 0, '&', nParamIndex);
1014 sal_Int32 nIndex = 0;
1015 OUString aKey;
1016 OUString aValue;
1019 std::u16string_view aToken = o3tl::getToken(aParam, 0, '=', nIndex);
1020 if (aKey.isEmpty())
1021 aKey = aToken;
1022 else
1023 aValue = aToken;
1024 } while (nIndex >= 0);
1025 aMap[aKey] = INetURLObject::decode(aValue, INetURLObject::DecodeMechanism::WithCharset);
1026 } while (nParamIndex >= 0);
1028 return aMap;
1031 void SfxLokHelper::getCommandValues(tools::JsonWriter& rJsonWriter, std::string_view rCommand)
1033 static constexpr OString aSignature(".uno:Signature"_ostr);
1034 if (!o3tl::starts_with(rCommand, aSignature))
1036 return;
1039 SfxObjectShell* pObjectShell = SfxObjectShell::Current();
1040 if (!pObjectShell)
1042 return;
1045 svl::crypto::SigningContext aSigningContext;
1046 std::map<OUString, OUString> aMap
1047 = SfxLokHelper::parseCommandParameters(OUString::fromUtf8(rCommand));
1048 auto it = aMap.find("signatureTime");
1049 if (it != aMap.end())
1051 // Signature time is provided: prefer it over the system time.
1052 aSigningContext.m_nSignatureTime = it->second.toInt64();
1054 pObjectShell->SignDocumentContentUsingCertificate(aSigningContext);
1055 // Set commandName, this is a reply to a request.
1056 rJsonWriter.put("commandName", aSignature);
1057 auto aCommandValues = rJsonWriter.startNode("commandValues");
1058 rJsonWriter.put("signatureTime", aSigningContext.m_nSignatureTime);
1060 uno::Sequence<sal_Int8> aDigest(reinterpret_cast<sal_Int8*>(aSigningContext.m_aDigest.data()),
1061 aSigningContext.m_aDigest.size());
1062 OUStringBuffer aBuffer;
1063 comphelper::Base64::encode(aBuffer, aDigest);
1064 rJsonWriter.put("digest", aBuffer.makeStringAndClear());
1067 void SfxLokHelper::notifyUpdate(SfxViewShell const* pThisView, int nType)
1069 if (DisableCallbacks::disabled() || !pThisView)
1070 return;
1072 pThisView->libreOfficeKitViewUpdatedCallback(nType);
1075 void SfxLokHelper::notifyUpdatePerViewId(SfxViewShell const* pThisView, int nType)
1077 notifyUpdatePerViewId(pThisView, pThisView, pThisView, nType);
1080 void SfxLokHelper::notifyUpdatePerViewId(SfxViewShell const* pTargetShell, SfxViewShell const* pViewShell,
1081 SfxViewShell const* pSourceShell, int nType)
1083 if (DisableCallbacks::disabled())
1084 return;
1086 int viewId = SfxLokHelper::getView(pViewShell);
1087 int sourceViewId = SfxLokHelper::getView(pSourceShell);
1088 pTargetShell->libreOfficeKitViewUpdatedCallbackPerViewId(nType, viewId, sourceViewId);
1091 void SfxLokHelper::notifyOtherViewsUpdatePerViewId(SfxViewShell const* pThisView, int nType)
1093 if (DisableCallbacks::disabled() || !pThisView)
1094 return;
1096 int viewId = SfxLokHelper::getView(pThisView);
1097 const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
1098 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1099 while (pViewShell)
1101 if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
1102 pViewShell->libreOfficeKitViewUpdatedCallbackPerViewId(nType, viewId, viewId);
1104 pViewShell = SfxViewShell::GetNext(*pViewShell);
1108 namespace
1110 struct LOKAsyncEventData
1112 int mnView; // Window is not enough.
1113 VclPtr<vcl::Window> mpWindow;
1114 VclEventId mnEvent;
1115 MouseEvent maMouseEvent;
1116 KeyEvent maKeyEvent;
1117 OUString maText;
1120 void LOKPostAsyncEvent(void* pEv, void*)
1122 std::unique_ptr<LOKAsyncEventData> pLOKEv(static_cast<LOKAsyncEventData*>(pEv));
1123 if (pLOKEv->mpWindow->isDisposed())
1124 return;
1126 int nView = SfxLokHelper::getView(nullptr);
1127 if (nView != pLOKEv->mnView)
1129 SAL_INFO("sfx.view", "LOK - view mismatch " << nView << " vs. " << pLOKEv->mnView);
1130 SfxLokHelper::setView(pLOKEv->mnView);
1133 if (!pLOKEv->mpWindow->HasChildPathFocus(true))
1135 SAL_INFO("sfx.view", "LOK - focus mismatch, switching focus");
1136 pLOKEv->mpWindow->GrabFocus();
1139 VclPtr<vcl::Window> pFocusWindow = pLOKEv->mpWindow->GetFocusedWindow();
1140 if (!pFocusWindow)
1141 pFocusWindow = pLOKEv->mpWindow;
1143 if (pLOKEv->mpWindow->isDisposed())
1144 return;
1146 switch (pLOKEv->mnEvent)
1148 case VclEventId::WindowKeyInput:
1150 sal_uInt16 nRepeat = pLOKEv->maKeyEvent.GetRepeat();
1151 KeyEvent singlePress(pLOKEv->maKeyEvent.GetCharCode(),
1152 pLOKEv->maKeyEvent.GetKeyCode());
1153 for (sal_uInt16 i = 0; i <= nRepeat; ++i)
1154 if (!pFocusWindow->isDisposed())
1155 pFocusWindow->KeyInput(singlePress);
1157 if (pLOKEv->maKeyEvent.GetKeyCode().GetCode() == KEY_CONTEXTMENU)
1159 // later do use getCaretPosition probably, or get focused obj position, smt like that
1160 Point aPos = pFocusWindow->GetPointerPosPixel();
1161 CommandEvent aCEvt( aPos, CommandEventId::ContextMenu);
1162 pFocusWindow->Command(aCEvt);
1164 break;
1166 case VclEventId::WindowKeyUp:
1167 if (!pFocusWindow->isDisposed())
1168 pFocusWindow->KeyUp(pLOKEv->maKeyEvent);
1169 break;
1170 case VclEventId::WindowMouseButtonDown:
1171 pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
1172 pLOKEv->mpWindow->MouseButtonDown(pLOKEv->maMouseEvent);
1173 // Invoke the context menu
1174 if (pLOKEv->maMouseEvent.GetButtons() & MOUSE_RIGHT)
1176 const CommandEvent aCEvt(pLOKEv->maMouseEvent.GetPosPixel(), CommandEventId::ContextMenu, true, nullptr);
1177 pLOKEv->mpWindow->Command(aCEvt);
1179 break;
1180 case VclEventId::WindowMouseButtonUp:
1181 pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
1182 pLOKEv->mpWindow->MouseButtonUp(pLOKEv->maMouseEvent);
1184 // sometimes MouseButtonDown captures mouse and starts tracking, and VCL
1185 // will not take care of releasing that with tiled rendering
1186 if (pLOKEv->mpWindow->IsTracking())
1187 pLOKEv->mpWindow->EndTracking();
1189 break;
1190 case VclEventId::WindowMouseMove:
1191 pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
1192 pLOKEv->mpWindow->MouseMove(pLOKEv->maMouseEvent);
1193 pLOKEv->mpWindow->RequestHelp(HelpEvent{
1194 pLOKEv->mpWindow->OutputToScreenPixel(pLOKEv->maMouseEvent.GetPosPixel()),
1195 HelpEventMode::QUICK }); // If needed, HelpEventMode should be taken from a config
1196 break;
1197 case VclEventId::ExtTextInput:
1198 case VclEventId::EndExtTextInput:
1199 pLOKEv->mpWindow->PostExtTextInputEvent(pLOKEv->mnEvent, pLOKEv->maText);
1200 break;
1201 default:
1202 assert(false);
1203 break;
1207 void postEventAsync(LOKAsyncEventData *pEvent)
1209 if (!pEvent->mpWindow || pEvent->mpWindow->isDisposed())
1211 SAL_WARN("vcl", "Async event post - but no valid window as destination " << pEvent->mpWindow.get());
1212 delete pEvent;
1213 return;
1216 pEvent->mnView = SfxLokHelper::getView(nullptr);
1217 if (vcl::lok::isUnipoll())
1219 if (!Application::IsMainThread())
1220 SAL_WARN("lok", "Posting event directly but not called from main thread!");
1221 LOKPostAsyncEvent(pEvent, nullptr);
1223 else
1224 Application::PostUserEvent(Link<void*, void>(pEvent, LOKPostAsyncEvent));
1228 void SfxLokHelper::postKeyEventAsync(const VclPtr<vcl::Window> &xWindow,
1229 int nType, int nCharCode, int nKeyCode, int nRepeat)
1231 LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
1232 switch (nType)
1234 case LOK_KEYEVENT_KEYINPUT:
1235 pLOKEv->mnEvent = VclEventId::WindowKeyInput;
1236 break;
1237 case LOK_KEYEVENT_KEYUP:
1238 pLOKEv->mnEvent = VclEventId::WindowKeyUp;
1239 break;
1240 default:
1241 assert(false);
1243 pLOKEv->maKeyEvent = KeyEvent(nCharCode, nKeyCode, nRepeat);
1244 pLOKEv->mpWindow = xWindow;
1245 postEventAsync(pLOKEv);
1248 void SfxLokHelper::setBlockedCommandList(int nViewId, const char* blockedCommandList)
1250 SfxViewShell* pViewShell = SfxLokHelper::getViewOfId(nViewId);
1252 if(pViewShell)
1254 pViewShell->setBlockedCommandList(blockedCommandList);
1258 void SfxLokHelper::postExtTextEventAsync(const VclPtr<vcl::Window> &xWindow,
1259 int nType, const OUString &rText)
1261 LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
1262 switch (nType)
1264 case LOK_EXT_TEXTINPUT:
1265 pLOKEv->mnEvent = VclEventId::ExtTextInput;
1266 pLOKEv->maText = rText;
1267 break;
1268 case LOK_EXT_TEXTINPUT_END:
1269 pLOKEv->mnEvent = VclEventId::EndExtTextInput;
1270 pLOKEv->maText = "";
1271 break;
1272 default:
1273 assert(false);
1275 pLOKEv->mpWindow = xWindow;
1276 postEventAsync(pLOKEv);
1279 void SfxLokHelper::postMouseEventAsync(const VclPtr<vcl::Window> &xWindow, LokMouseEventData const & rLokMouseEventData)
1281 LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
1282 switch (rLokMouseEventData.mnType)
1284 case LOK_MOUSEEVENT_MOUSEBUTTONDOWN:
1285 pLOKEv->mnEvent = VclEventId::WindowMouseButtonDown;
1286 break;
1287 case LOK_MOUSEEVENT_MOUSEBUTTONUP:
1288 pLOKEv->mnEvent = VclEventId::WindowMouseButtonUp;
1289 break;
1290 case LOK_MOUSEEVENT_MOUSEMOVE:
1291 pLOKEv->mnEvent = VclEventId::WindowMouseMove;
1292 break;
1293 default:
1294 assert(false);
1297 // no reason - just always true so far.
1298 assert (rLokMouseEventData.meModifiers == MouseEventModifiers::SIMPLECLICK);
1300 pLOKEv->maMouseEvent = MouseEvent(rLokMouseEventData.maPosition, rLokMouseEventData.mnCount,
1301 rLokMouseEventData.meModifiers, rLokMouseEventData.mnButtons,
1302 rLokMouseEventData.mnModifier);
1303 if (rLokMouseEventData.maLogicPosition)
1305 pLOKEv->maMouseEvent.setLogicPosition(*rLokMouseEventData.maLogicPosition);
1307 pLOKEv->mpWindow = xWindow;
1308 postEventAsync(pLOKEv);
1311 void SfxLokHelper::dumpState(rtl::OStringBuffer &rState)
1313 SfxViewShell* pShell = SfxViewShell::Current();
1314 sal_Int32 nDocId = pShell ? static_cast<sal_Int32>(pShell->GetDocId().get()) : -1;
1316 rState.append("\n\tDocId:\t");
1317 rState.append(nDocId);
1319 if (nDocId < 0)
1320 return;
1322 rState.append("\n\tViewCount:\t");
1323 rState.append(static_cast<sal_Int32>(getViewsCount(nDocId)));
1325 const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
1326 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1327 while (pViewShell)
1329 if (pCurrentViewShell == nullptr || pViewShell->GetDocId() == pCurrentViewShell-> GetDocId())
1330 pViewShell->dumpLibreOfficeKitViewState(rState);
1332 pViewShell = SfxViewShell::GetNext(*pViewShell);
1336 bool SfxLokHelper::testInPlaceComponentMouseEventHit(SfxViewShell* pViewShell, int nType, int nX,
1337 int nY, int nCount, int nButtons,
1338 int nModifier, double fScaleX, double fScaleY,
1339 bool bNegativeX)
1341 // In LOK RTL mode draw/svx operates in negative X coordinates
1342 // But the coordinates from client is always positive, so negate nX.
1343 if (bNegativeX)
1344 nX = -nX;
1346 // check if the user hit a chart/math object which is being edited by this view
1347 if (LokChartHelper aChartHelper(pViewShell, bNegativeX);
1348 aChartHelper.postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier, fScaleX, fScaleY))
1349 return true;
1351 if (LokStarMathHelper aMathHelper(pViewShell);
1352 aMathHelper.postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier, fScaleX, fScaleY))
1353 return true;
1355 // check if the user hit a chart which is being edited by someone else
1356 // and, if so, skip current mouse event
1357 if (nType != LOK_MOUSEEVENT_MOUSEMOVE)
1359 if (LokChartHelper::HitAny({nX, nY}, bNegativeX))
1360 return true;
1363 return false;
1366 VclPtr<vcl::Window> SfxLokHelper::getInPlaceDocWindow(SfxViewShell* pViewShell)
1368 if (VclPtr<vcl::Window> pWindow = LokChartHelper(pViewShell).GetWindow())
1369 return pWindow;
1370 if (VclPtr<vcl::Window> pWindow = LokStarMathHelper(pViewShell).GetWidgetWindow())
1371 return pWindow;
1372 return {};
1375 void SfxLokHelper::sendNetworkAccessError(std::string_view rAction)
1377 tools::JsonWriter aWriter;
1378 aWriter.put("code", static_cast<sal_uInt32>(
1379 ErrCode(ErrCodeArea::Inet, sal_uInt16(ErrCodeClass::Access))));
1380 aWriter.put("kind", "network");
1381 aWriter.put("cmd", rAction);
1383 SfxViewShell* pViewShell = SfxViewShell::Current();
1384 if (pViewShell)
1386 pViewShell->libreOfficeKitViewCallback(
1387 LOK_CALLBACK_ERROR, aWriter.finishAndGetAsOString());
1391 SfxLokLanguageGuard::SfxLokLanguageGuard(SfxViewShell* pNewShell)
1392 : m_bSetLanguage(false)
1393 , m_pOldShell(nullptr)
1395 m_pOldShell = SfxViewShell::Current();
1396 if (!comphelper::LibreOfficeKit::isActive() || !pNewShell || pNewShell == m_pOldShell)
1398 return;
1401 // The current view ID is not the one that belongs to this frame, update
1402 // language/locale.
1403 comphelper::LibreOfficeKit::setLanguageTag(pNewShell->GetLOKLanguageTag());
1404 comphelper::LibreOfficeKit::setLocale(pNewShell->GetLOKLocale());
1405 m_bSetLanguage = true;
1408 SfxLokLanguageGuard::~SfxLokLanguageGuard()
1410 if (!m_bSetLanguage || !m_pOldShell)
1412 return;
1415 comphelper::LibreOfficeKit::setLanguageTag(m_pOldShell->GetLOKLanguageTag());
1416 comphelper::LibreOfficeKit::setLocale(m_pOldShell->GetLOKLocale());
1419 LOKEditViewHistory::EditViewHistoryMap LOKEditViewHistory::maEditViewHistory;
1422 void LOKEditViewHistory::Update(bool bRemove)
1424 if (!comphelper::LibreOfficeKit::isActive())
1425 return;
1427 SfxViewShell* pViewShell = SfxViewShell::Current();
1428 if (pViewShell)
1430 int nDocId = pViewShell->GetDocId().get();
1431 if (maEditViewHistory.find(nDocId) != maEditViewHistory.end())
1432 maEditViewHistory[nDocId].remove(pViewShell);
1433 if (!bRemove)
1435 maEditViewHistory[nDocId].push_back(pViewShell);
1436 if (maEditViewHistory[nDocId].size() > 10)
1437 maEditViewHistory[nDocId].pop_front();
1442 ViewShellList LOKEditViewHistory::GetHistoryForDoc(ViewShellDocId aDocId)
1444 int nDocId = aDocId.get();
1445 ViewShellList aResult;
1446 if (maEditViewHistory.find(nDocId) != maEditViewHistory.end())
1447 aResult = maEditViewHistory.at(nDocId);
1448 return aResult;
1451 ViewShellList LOKEditViewHistory::GetSortedViewsForDoc(ViewShellDocId aDocId)
1453 ViewShellList aEditViewHistoryForDoc = LOKEditViewHistory::GetHistoryForDoc(aDocId);
1454 // all views where document is loaded
1455 ViewShellList aCurrentDocViewList;
1456 // active views that are listed in the edit history
1457 ViewShellList aEditedViewList;
1459 // Populate aCurrentDocViewList and aEditedViewList
1460 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1461 while (pViewShell)
1463 if (pViewShell->GetDocId() == aDocId)
1465 if (aEditViewHistoryForDoc.empty() ||
1466 std::find(aEditViewHistoryForDoc.begin(), aEditViewHistoryForDoc.end(),
1467 pViewShell) == aEditViewHistoryForDoc.end())
1469 // append views not listed in the edit history;
1470 // the edit history is limited to 10 views,
1471 // so it could miss some view where in place editing is occurring
1472 aCurrentDocViewList.push_back(pViewShell);
1474 else
1476 // view is listed in the edit history
1477 aEditedViewList.push_back(pViewShell);
1480 pViewShell = SfxViewShell::GetNext(*pViewShell);
1483 // in case some no more active view needs to be removed from the history
1484 aEditViewHistoryForDoc.remove_if(
1485 [&aEditedViewList](SfxViewShell* pHistoryItem) {
1486 return std::find(aEditedViewList.begin(), aEditedViewList.end(), pHistoryItem) == aEditedViewList.end();
1489 // place views belonging to the edit history at the end
1490 aCurrentDocViewList.splice(aCurrentDocViewList.end(), aEditViewHistoryForDoc);
1492 return aCurrentDocViewList;
1495 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */