LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sfx2 / source / view / lokhelper.cxx
blob20be47fc473dc1d4f090379c8146639e421e50cb
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_view>
14 #include <sfx2/lokcomponenthelpers.hxx>
15 #include <sfx2/lokhelper.hxx>
17 #include <com/sun/star/frame/Desktop.hpp>
19 #include <comphelper/processfactory.hxx>
20 #include <rtl/strbuf.hxx>
21 #include <vcl/lok.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/commandevent.hxx>
24 #include <vcl/window.hxx>
25 #include <sal/log.hxx>
26 #include <sfx2/app.hxx>
27 #include <sfx2/msg.hxx>
28 #include <sfx2/viewsh.hxx>
29 #include <sfx2/request.hxx>
30 #include <sfx2/sfxsids.hrc>
31 #include <sfx2/viewfrm.hxx>
32 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
33 #include <comphelper/lok.hxx>
34 #include <editeng/outliner.hxx>
35 #include <sfx2/msgpool.hxx>
37 #include <boost/property_tree/json_parser.hpp>
39 using namespace com::sun::star;
41 namespace {
42 /// Used to disable callbacks.
43 /// Needed to avoid recursion when switching views,
44 /// which can cause clients to invoke LOKit API and
45 /// implicitly set the view, which might cause an
46 /// infinite recursion if not detected and prevented.
47 class DisableCallbacks
49 public:
50 DisableCallbacks()
52 assert(m_nDisabled >= 0 && "Expected non-negative DisabledCallbacks state when disabling.");
53 ++m_nDisabled;
56 ~DisableCallbacks()
58 assert(m_nDisabled > 0 && "Expected positive DisabledCallbacks state when re-enabling.");
59 --m_nDisabled;
62 static inline bool disabled()
64 return !comphelper::LibreOfficeKit::isActive() || m_nDisabled != 0;
67 private:
68 static int m_nDisabled;
71 int DisableCallbacks::m_nDisabled = 0;
74 namespace
76 LanguageTag g_defaultLanguageTag("en-US", true);
77 LanguageTag g_loadLanguageTag("en-US", true); //< The language used to load.
78 LOKDeviceFormFactor g_deviceFormFactor = LOKDeviceFormFactor::UNKNOWN;
81 int SfxLokHelper::createView(SfxViewFrame* pViewFrame, ViewShellDocId docId)
83 assert(docId >= ViewShellDocId(0) && "Cannot createView for invalid (negative) DocId.");
85 if (pViewFrame == nullptr)
86 return -1;
88 SfxViewShell::SetCurrentDocId(docId);
89 SfxRequest aRequest(pViewFrame, SID_NEWWINDOW);
90 pViewFrame->ExecView_Impl(aRequest);
91 SfxViewShell* pViewShell = SfxViewShell::Current();
92 if (pViewShell == nullptr)
93 return -1;
95 assert(pViewShell->GetDocId() == docId && "DocId must be already set!");
96 return static_cast<sal_Int32>(pViewShell->GetViewShellId());
99 int SfxLokHelper::createView()
101 // Assumes a single document, or at least that the
102 // current view belongs to the document on which the
103 // view will be created.
104 SfxViewShell* pViewShell = SfxViewShell::Current();
105 if (pViewShell == nullptr)
106 return -1;
108 return createView(pViewShell->GetViewFrame(), pViewShell->GetDocId());
111 int SfxLokHelper::createView(int nDocId)
113 const SfxApplication* pApp = SfxApplication::Get();
114 if (pApp == nullptr)
115 return -1;
117 // Find a shell with the given DocId.
118 const ViewShellDocId docId(nDocId);
119 for (const SfxViewShell* pViewShell : pApp->GetViewShells_Impl())
121 if (pViewShell->GetDocId() == docId)
122 return createView(pViewShell->GetViewFrame(), docId);
125 // No frame with nDocId found.
126 return -1;
129 void SfxLokHelper::setEditMode(int nMode, vcl::ITiledRenderable* pDoc)
131 DisableCallbacks dc;
132 pDoc->setEditMode(nMode);
135 void SfxLokHelper::destroyView(int nId)
137 const SfxApplication* pApp = SfxApplication::Get();
138 if (pApp == nullptr)
139 return;
141 const ViewShellId nViewShellId(nId);
142 std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
144 for (const SfxViewShell* pViewShell : rViewArr)
146 if (pViewShell->GetViewShellId() == nViewShellId)
148 SfxViewFrame* pViewFrame = pViewShell->GetViewFrame();
149 SfxRequest aRequest(pViewFrame, SID_CLOSEWIN);
150 pViewFrame->Exec_Impl(aRequest);
151 break;
156 void SfxLokHelper::setView(int nId)
158 SfxApplication* pApp = SfxApplication::Get();
159 if (pApp == nullptr)
160 return;
162 const ViewShellId nViewShellId(nId);
163 std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
165 for (const SfxViewShell* pViewShell : rViewArr)
167 if (pViewShell->GetViewShellId() == nViewShellId)
169 DisableCallbacks dc;
171 // update the current LOK language and locale for the dialog tunneling
172 comphelper::LibreOfficeKit::setLanguageTag(pViewShell->GetLOKLanguageTag());
173 comphelper::LibreOfficeKit::setLocale(pViewShell->GetLOKLocale());
175 if (pViewShell == SfxViewShell::Current())
176 return;
178 SfxViewFrame* pViewFrame = pViewShell->GetViewFrame();
179 pViewFrame->MakeActive_Impl(false);
181 // Make comphelper::dispatchCommand() find the correct frame.
182 uno::Reference<frame::XFrame> xFrame = pViewFrame->GetFrame().GetFrameInterface();
183 uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext());
184 xDesktop->setActiveFrame(xFrame);
185 return;
191 SfxViewShell* SfxLokHelper::getViewOfId(int nId)
193 SfxApplication* pApp = SfxApplication::Get();
194 if (pApp == nullptr)
195 return nullptr;
197 const ViewShellId nViewShellId(nId);
198 std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
199 for (SfxViewShell* pViewShell : rViewArr)
201 if (pViewShell->GetViewShellId() == nViewShellId)
202 return pViewShell;
205 return nullptr;
208 int SfxLokHelper::getView(const SfxViewShell* pViewShell)
210 if (!pViewShell)
211 pViewShell = SfxViewShell::Current();
212 // Still no valid view shell? Then no idea.
213 if (!pViewShell)
214 return -1;
216 return static_cast<sal_Int32>(pViewShell->GetViewShellId());
219 std::size_t SfxLokHelper::getViewsCount(int nDocId)
221 assert(nDocId != -1 && "Cannot getViewsCount for invalid DocId -1");
223 SfxApplication* pApp = SfxApplication::Get();
224 if (!pApp)
225 return 0;
227 const ViewShellDocId nCurrentDocId(nDocId);
228 std::size_t n = 0;
229 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
230 while (pViewShell)
232 if (pViewShell->GetDocId() == nCurrentDocId)
233 n++;
234 pViewShell = SfxViewShell::GetNext(*pViewShell);
237 return n;
240 bool SfxLokHelper::getViewIds(int nDocId, int* pArray, size_t nSize)
242 assert(nDocId != -1 && "Cannot getViewsIds for invalid DocId -1");
244 SfxApplication* pApp = SfxApplication::Get();
245 if (!pApp)
246 return false;
248 const ViewShellDocId nCurrentDocId(nDocId);
249 std::size_t n = 0;
250 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
251 while (pViewShell)
253 if (pViewShell->GetDocId() == nCurrentDocId)
255 if (n == nSize)
256 return false;
258 pArray[n] = static_cast<sal_Int32>(pViewShell->GetViewShellId());
259 n++;
262 pViewShell = SfxViewShell::GetNext(*pViewShell);
265 return true;
268 int SfxLokHelper::getDocumentIdOfView(int nViewId)
270 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
271 while (pViewShell)
273 if (pViewShell->GetViewShellId() == ViewShellId(nViewId))
274 return static_cast<int>(pViewShell->GetDocId());
275 pViewShell = SfxViewShell::GetNext(*pViewShell);
277 return -1;
280 const LanguageTag & SfxLokHelper::getDefaultLanguage()
282 return g_defaultLanguageTag;
285 void SfxLokHelper::setDefaultLanguage(const OUString& rBcp47LanguageTag)
287 g_defaultLanguageTag = LanguageTag(rBcp47LanguageTag, true);
290 const LanguageTag& SfxLokHelper::getLoadLanguage() { return g_loadLanguageTag; }
292 void SfxLokHelper::setLoadLanguage(const OUString& rBcp47LanguageTag)
294 g_loadLanguageTag = LanguageTag(rBcp47LanguageTag, true);
297 void SfxLokHelper::setViewLanguage(int nId, const OUString& rBcp47LanguageTag)
299 std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
301 for (SfxViewShell* pViewShell : rViewArr)
303 if (pViewShell->GetViewShellId() == ViewShellId(nId))
305 pViewShell->SetLOKLanguageTag(rBcp47LanguageTag);
306 return;
311 void SfxLokHelper::setViewLocale(int nId, const OUString& rBcp47LanguageTag)
313 std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
315 for (SfxViewShell* pViewShell : rViewArr)
317 if (pViewShell->GetViewShellId() == ViewShellId(nId))
319 pViewShell->SetLOKLocale(rBcp47LanguageTag);
320 return;
325 LOKDeviceFormFactor SfxLokHelper::getDeviceFormFactor()
327 return g_deviceFormFactor;
330 void SfxLokHelper::setDeviceFormFactor(std::u16string_view rDeviceFormFactor)
332 if (rDeviceFormFactor == u"desktop")
333 g_deviceFormFactor = LOKDeviceFormFactor::DESKTOP;
334 else if (rDeviceFormFactor == u"tablet")
335 g_deviceFormFactor = LOKDeviceFormFactor::TABLET;
336 else if (rDeviceFormFactor == u"mobile")
337 g_deviceFormFactor = LOKDeviceFormFactor::MOBILE;
338 else
339 g_deviceFormFactor = LOKDeviceFormFactor::UNKNOWN;
343 * Used for putting a whole JSON string into a string value
344 * e.g { key: "{JSON}" }
346 static OString lcl_sanitizeJSONAsValue(const OString &rStr)
348 if (rStr.getLength() < 1)
349 return rStr;
350 // FIXME: need an optimized 'escape' method for O[U]String.
351 OStringBuffer aBuf(rStr.getLength() + 8);
352 for (sal_Int32 i = 0; i < rStr.getLength(); ++i)
354 if (rStr[i] == '"' || rStr[i] == '\\')
355 aBuf.append('\\');
357 if (rStr[i] != '\n')
358 aBuf.append(rStr[i]);
360 return aBuf.makeStringAndClear();
363 static OString lcl_generateJSON(const SfxViewShell* pView, const boost::property_tree::ptree& rTree)
365 assert(pView != nullptr && "pView must be valid");
366 boost::property_tree::ptree aMessageProps = rTree;
367 aMessageProps.put("viewId", SfxLokHelper::getView(pView));
368 aMessageProps.put("part", pView->getPart());
369 aMessageProps.put("mode", pView->getEditMode());
370 std::stringstream aStream;
371 boost::property_tree::write_json(aStream, aMessageProps, false /* pretty */);
372 const std::string aString = aStream.str();
373 return OString(aString.c_str(), aString.size()).trim();
376 static inline OString lcl_generateJSON(const SfxViewShell* pView, int nViewId, std::string_view rKey,
377 const OString& rPayload)
379 assert(pView != nullptr && "pView must be valid");
380 return OString::Concat("{ \"viewId\": \"") + OString::number(nViewId)
381 + "\", \"part\": \"" + OString::number(pView->getPart()) + "\", \"mode\": \""
382 + OString::number(pView->getEditMode()) + "\", \"" + rKey + "\": \""
383 + lcl_sanitizeJSONAsValue(rPayload) + "\" }";
386 static inline OString lcl_generateJSON(const SfxViewShell* pView, std::string_view rKey,
387 const OString& rPayload)
389 return lcl_generateJSON(pView, SfxLokHelper::getView(pView), rKey, rPayload);
392 void SfxLokHelper::notifyOtherView(const SfxViewShell* pThisView, SfxViewShell const* pOtherView,
393 int nType, std::string_view rKey, const OString& rPayload)
395 assert(pThisView != nullptr && "pThisView must be valid");
396 if (DisableCallbacks::disabled())
397 return;
399 const OString aPayload = lcl_generateJSON(pThisView, rKey, rPayload);
400 const int viewId = SfxLokHelper::getView(pThisView);
401 pOtherView->libreOfficeKitViewCallbackWithViewId(nType, aPayload.getStr(), viewId);
404 void SfxLokHelper::notifyOtherView(const SfxViewShell* pThisView, SfxViewShell const* pOtherView,
405 int nType, const boost::property_tree::ptree& rTree)
407 assert(pThisView != nullptr && "pThisView must be valid");
408 if (DisableCallbacks::disabled())
409 return;
411 const int viewId = SfxLokHelper::getView(pThisView);
412 pOtherView->libreOfficeKitViewCallbackWithViewId(nType, lcl_generateJSON(pThisView, rTree).getStr(), viewId);
415 void SfxLokHelper::notifyOtherViews(const SfxViewShell* pThisView, int nType, std::string_view rKey,
416 const OString& rPayload)
418 assert(pThisView != nullptr && "pThisView must be valid");
419 if (DisableCallbacks::disabled())
420 return;
422 // Cache the payload so we only have to generate it once, at most.
423 OString aPayload;
424 int viewId = -1;
426 const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
427 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
428 while (pViewShell)
430 if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
432 // Payload is only dependent on pThisView.
433 if (aPayload.isEmpty())
435 aPayload = lcl_generateJSON(pThisView, rKey, rPayload);
436 viewId = SfxLokHelper::getView(pThisView);
439 pViewShell->libreOfficeKitViewCallbackWithViewId(nType, aPayload.getStr(), viewId);
442 pViewShell = SfxViewShell::GetNext(*pViewShell);
446 void SfxLokHelper::notifyOtherViews(const SfxViewShell* pThisView, int nType,
447 const boost::property_tree::ptree& rTree)
449 assert(pThisView != nullptr && "pThisView must be valid");
450 if (DisableCallbacks::disabled())
451 return;
453 // Cache the payload so we only have to generate it once, at most.
454 OString aPayload;
455 int viewId = -1;
457 const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
458 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
459 while (pViewShell)
461 if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
463 // Payload is only dependent on pThisView.
464 if (aPayload.isEmpty())
466 aPayload = lcl_generateJSON(pThisView, rTree);
467 viewId = SfxLokHelper::getView(pThisView);
470 pViewShell->libreOfficeKitViewCallbackWithViewId(nType, aPayload.getStr(), viewId);
473 pViewShell = SfxViewShell::GetNext(*pViewShell);
477 OString SfxLokHelper::makePayloadJSON(const SfxViewShell* pThisView, int nViewId, std::string_view rKey, const OString& rPayload)
479 return lcl_generateJSON(pThisView, nViewId, rKey, rPayload);
482 namespace {
483 OUString lcl_getNameForSlot(const SfxViewShell* pShell, sal_uInt16 nWhich)
485 if (pShell && pShell->GetFrame())
487 const SfxSlot* pSlot = SfxSlotPool::GetSlotPool(pShell->GetFrame()).GetSlot(nWhich);
488 if (pSlot)
490 const char* pName = pSlot->GetUnoName();
491 if (pName)
493 return ".uno:" + OStringToOUString(pName, RTL_TEXTENCODING_ASCII_US);
498 return "";
502 void SfxLokHelper::sendUnoStatus(const SfxViewShell* pShell, const SfxPoolItem* pItem)
504 if (!pShell || !pItem || pItem == INVALID_POOL_ITEM || DisableCallbacks::disabled())
505 return;
507 boost::property_tree::ptree aItem = pItem->dumpAsJSON();
509 if (aItem.count("state"))
511 OUString sCommand = lcl_getNameForSlot(pShell, pItem->Which());
512 if (!sCommand.isEmpty())
513 aItem.put("commandName", sCommand);
515 std::stringstream aStream;
516 boost::property_tree::write_json(aStream, aItem);
517 pShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, aStream.str().c_str());
521 void SfxLokHelper::notifyWindow(const SfxViewShell* pThisView,
522 vcl::LOKWindowId nLOKWindowId,
523 std::u16string_view rAction,
524 const std::vector<vcl::LOKPayloadItem>& rPayload)
526 assert(pThisView != nullptr && "pThisView must be valid");
528 if (nLOKWindowId == 0 || DisableCallbacks::disabled())
529 return;
531 OStringBuffer aPayload =
532 "{ \"id\": \"" + OString::number(nLOKWindowId) + "\""
533 ", \"action\": \"" + OUStringToOString(rAction, RTL_TEXTENCODING_UTF8) + "\"";
535 for (const auto& rItem: rPayload)
537 if (!rItem.first.isEmpty() && !rItem.second.isEmpty())
539 aPayload.append(", \"" + rItem.first + "\": \"" +
540 rItem.second).append('"');
543 aPayload.append('}');
545 const OString s = aPayload.makeStringAndClear();
546 pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_WINDOW, s.getStr());
549 void SfxLokHelper::notifyInvalidation(SfxViewShell const* pThisView, tools::Rectangle const* pRect)
551 if (DisableCallbacks::disabled())
552 return;
554 // -1 means all parts
555 const int nPart = comphelper::LibreOfficeKit::isPartInInvalidation() ? pThisView->getPart() : INT_MIN;
556 const int nMode = pThisView->getEditMode();
557 pThisView->libreOfficeKitViewInvalidateTilesCallback(pRect, nPart, nMode);
560 void SfxLokHelper::notifyDocumentSizeChanged(SfxViewShell const* pThisView, const OString& rPayload, vcl::ITiledRenderable* pDoc, bool bInvalidateAll)
562 if (!pDoc || pDoc->isDisposed() || DisableCallbacks::disabled())
563 return;
565 if (bInvalidateAll)
567 for (int i = 0; i < pDoc->getParts(); ++i)
569 tools::Rectangle aRectangle(0, 0, 1000000000, 1000000000);
570 const int nMode = pThisView->getEditMode();
571 pThisView->libreOfficeKitViewInvalidateTilesCallback(&aRectangle, i, nMode);
574 pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_SIZE_CHANGED, rPayload.getStr());
577 void SfxLokHelper::notifyDocumentSizeChangedAllViews(vcl::ITiledRenderable* pDoc, bool bInvalidateAll)
579 if (DisableCallbacks::disabled())
580 return;
582 // FIXME: Do we know whether it is the views for the document that is in the "current" view that has changed?
583 const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
584 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
585 while (pViewShell)
587 // FIXME: What if SfxViewShell::Current() returned null?
588 // Should we then do this for all views of all open documents
589 // or not?
590 if (pCurrentViewShell == nullptr || pViewShell->GetDocId() == pCurrentViewShell-> GetDocId())
592 SfxLokHelper::notifyDocumentSizeChanged(pViewShell, "", pDoc, bInvalidateAll);
593 bInvalidateAll = false; // we direct invalidations to all views anyway.
595 pViewShell = SfxViewShell::GetNext(*pViewShell);
599 void SfxLokHelper::notifyPartSizeChangedAllViews(vcl::ITiledRenderable* pDoc, int nPart)
601 if (DisableCallbacks::disabled())
602 return;
604 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
605 while (pViewShell)
607 if (pViewShell->getPart() == nPart)
608 SfxLokHelper::notifyDocumentSizeChanged(pViewShell, "", pDoc, false);
609 pViewShell = SfxViewShell::GetNext(*pViewShell);
613 OString SfxLokHelper::makeVisCursorInvalidation(int nViewId, const OString& rRectangle,
614 bool bMispelledWord, const OString& rHyperlink)
616 if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
618 OString sHyperlink = rHyperlink.isEmpty() ? "{}" : rHyperlink;
619 return OString::Concat("{ \"viewId\": \"") + OString::number(nViewId) +
620 "\", \"rectangle\": \"" + rRectangle +
621 "\", \"mispelledWord\": \"" + OString::number(bMispelledWord ? 1 : 0) +
622 "\", \"hyperlink\": " + sHyperlink + " }";
624 else
626 return rRectangle;
630 void SfxLokHelper::notifyAllViews(int nType, const OString& rPayload)
632 if (DisableCallbacks::disabled())
633 return;
635 const auto payload = rPayload.getStr();
636 const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
637 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
638 while (pViewShell)
640 if (pViewShell->GetDocId() == pCurrentViewShell->GetDocId())
641 pViewShell->libreOfficeKitViewCallback(nType, payload);
642 pViewShell = SfxViewShell::GetNext(*pViewShell);
646 void SfxLokHelper::notifyContextChange(SfxViewShell const* pViewShell, const OUString& aApplication, const OUString& aContext)
648 if (DisableCallbacks::disabled())
649 return;
651 OString aBuffer =
652 OUStringToOString(aApplication.replace(' ', '_'), RTL_TEXTENCODING_UTF8) +
653 " " +
654 OUStringToOString(aContext.replace(' ', '_'), RTL_TEXTENCODING_UTF8);
655 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_CHANGED, aBuffer.getStr());
658 void SfxLokHelper::notifyUpdate(SfxViewShell const* pThisView, int nType)
660 if (DisableCallbacks::disabled())
661 return;
663 pThisView->libreOfficeKitViewUpdatedCallback(nType);
666 void SfxLokHelper::notifyUpdatePerViewId(SfxViewShell const* pThisView, int nType)
668 notifyUpdatePerViewId(pThisView, pThisView, pThisView, nType);
671 void SfxLokHelper::notifyUpdatePerViewId(SfxViewShell const* pTargetShell, SfxViewShell const* pViewShell,
672 SfxViewShell const* pSourceShell, int nType)
674 if (DisableCallbacks::disabled())
675 return;
677 int viewId = SfxLokHelper::getView(pViewShell);
678 int sourceViewId = SfxLokHelper::getView(pSourceShell);
679 pTargetShell->libreOfficeKitViewUpdatedCallbackPerViewId(nType, viewId, sourceViewId);
682 void SfxLokHelper::notifyOtherViewsUpdatePerViewId(SfxViewShell const* pThisView, int nType)
684 assert(pThisView != nullptr && "pThisView must be valid");
685 if (DisableCallbacks::disabled())
686 return;
688 int viewId = SfxLokHelper::getView(pThisView);
689 const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
690 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
691 while (pViewShell)
693 if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
694 pViewShell->libreOfficeKitViewUpdatedCallbackPerViewId(nType, viewId, viewId);
696 pViewShell = SfxViewShell::GetNext(*pViewShell);
700 namespace
702 struct LOKAsyncEventData
704 int mnView; // Window is not enough.
705 VclPtr<vcl::Window> mpWindow;
706 VclEventId mnEvent;
707 MouseEvent maMouseEvent;
708 KeyEvent maKeyEvent;
709 OUString maText;
712 void LOKPostAsyncEvent(void* pEv, void*)
714 std::unique_ptr<LOKAsyncEventData> pLOKEv(static_cast<LOKAsyncEventData*>(pEv));
715 if (pLOKEv->mpWindow->isDisposed())
716 return;
718 int nView = SfxLokHelper::getView(nullptr);
719 if (nView != pLOKEv->mnView)
721 SAL_INFO("sfx.view", "LOK - view mismatch " << nView << " vs. " << pLOKEv->mnView);
722 SfxLokHelper::setView(pLOKEv->mnView);
725 if (!pLOKEv->mpWindow->HasChildPathFocus(true))
727 SAL_INFO("sfx.view", "LOK - focus mismatch, switching focus");
728 pLOKEv->mpWindow->GrabFocus();
731 VclPtr<vcl::Window> pFocusWindow = pLOKEv->mpWindow->GetFocusedWindow();
732 if (!pFocusWindow)
733 pFocusWindow = pLOKEv->mpWindow;
735 if (pLOKEv->mpWindow->isDisposed())
736 return;
738 switch (pLOKEv->mnEvent)
740 case VclEventId::WindowKeyInput:
742 sal_uInt16 nRepeat = pLOKEv->maKeyEvent.GetRepeat();
743 KeyEvent singlePress(pLOKEv->maKeyEvent.GetCharCode(),
744 pLOKEv->maKeyEvent.GetKeyCode());
745 for (sal_uInt16 i = 0; i <= nRepeat; ++i)
746 if (!pFocusWindow->isDisposed())
747 pFocusWindow->KeyInput(singlePress);
748 break;
750 case VclEventId::WindowKeyUp:
751 if (!pFocusWindow->isDisposed())
752 pFocusWindow->KeyUp(pLOKEv->maKeyEvent);
753 break;
754 case VclEventId::WindowMouseButtonDown:
755 pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
756 pLOKEv->mpWindow->MouseButtonDown(pLOKEv->maMouseEvent);
757 // Invoke the context menu
758 if (pLOKEv->maMouseEvent.GetButtons() & MOUSE_RIGHT)
760 const CommandEvent aCEvt(pLOKEv->maMouseEvent.GetPosPixel(), CommandEventId::ContextMenu, true, nullptr);
761 pLOKEv->mpWindow->Command(aCEvt);
763 break;
764 case VclEventId::WindowMouseButtonUp:
765 pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
766 pLOKEv->mpWindow->MouseButtonUp(pLOKEv->maMouseEvent);
768 // sometimes MouseButtonDown captures mouse and starts tracking, and VCL
769 // will not take care of releasing that with tiled rendering
770 if (pLOKEv->mpWindow->IsTracking())
771 pLOKEv->mpWindow->EndTracking();
773 break;
774 case VclEventId::WindowMouseMove:
775 pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel());
776 pLOKEv->mpWindow->MouseMove(pLOKEv->maMouseEvent);
777 break;
778 case VclEventId::ExtTextInput:
779 case VclEventId::EndExtTextInput:
780 pLOKEv->mpWindow->PostExtTextInputEvent(pLOKEv->mnEvent, pLOKEv->maText);
781 break;
782 default:
783 assert(false);
784 break;
788 void postEventAsync(LOKAsyncEventData *pEvent)
790 if (!pEvent->mpWindow || pEvent->mpWindow->isDisposed())
792 SAL_WARN("vcl", "Async event post - but no valid window as destination " << pEvent->mpWindow.get());
793 delete pEvent;
794 return;
797 pEvent->mnView = SfxLokHelper::getView(nullptr);
798 if (vcl::lok::isUnipoll())
800 if (!Application::IsMainThread())
801 SAL_WARN("lok", "Posting event directly but not called from main thread!");
802 LOKPostAsyncEvent(pEvent, nullptr);
804 else
805 Application::PostUserEvent(Link<void*, void>(pEvent, LOKPostAsyncEvent));
809 void SfxLokHelper::postKeyEventAsync(const VclPtr<vcl::Window> &xWindow,
810 int nType, int nCharCode, int nKeyCode, int nRepeat)
812 LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
813 switch (nType)
815 case LOK_KEYEVENT_KEYINPUT:
816 pLOKEv->mnEvent = VclEventId::WindowKeyInput;
817 break;
818 case LOK_KEYEVENT_KEYUP:
819 pLOKEv->mnEvent = VclEventId::WindowKeyUp;
820 break;
821 default:
822 assert(false);
824 pLOKEv->maKeyEvent = KeyEvent(nCharCode, nKeyCode, nRepeat);
825 pLOKEv->mpWindow = xWindow;
826 postEventAsync(pLOKEv);
829 void SfxLokHelper::setBlockedCommandList(int nViewId, const char* blockedCommandList)
831 SfxViewShell* pViewShell = SfxLokHelper::getViewOfId(nViewId);
833 if(pViewShell)
835 pViewShell->setBlockedCommandList(blockedCommandList);
839 void SfxLokHelper::postExtTextEventAsync(const VclPtr<vcl::Window> &xWindow,
840 int nType, const OUString &rText)
842 LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
843 switch (nType)
845 case LOK_EXT_TEXTINPUT:
846 pLOKEv->mnEvent = VclEventId::ExtTextInput;
847 pLOKEv->maText = rText;
848 break;
849 case LOK_EXT_TEXTINPUT_END:
850 pLOKEv->mnEvent = VclEventId::EndExtTextInput;
851 pLOKEv->maText = "";
852 break;
853 default:
854 assert(false);
856 pLOKEv->mpWindow = xWindow;
857 postEventAsync(pLOKEv);
860 void SfxLokHelper::postMouseEventAsync(const VclPtr<vcl::Window> &xWindow, LokMouseEventData const & rLokMouseEventData)
862 LOKAsyncEventData* pLOKEv = new LOKAsyncEventData;
863 switch (rLokMouseEventData.mnType)
865 case LOK_MOUSEEVENT_MOUSEBUTTONDOWN:
866 pLOKEv->mnEvent = VclEventId::WindowMouseButtonDown;
867 break;
868 case LOK_MOUSEEVENT_MOUSEBUTTONUP:
869 pLOKEv->mnEvent = VclEventId::WindowMouseButtonUp;
870 break;
871 case LOK_MOUSEEVENT_MOUSEMOVE:
872 pLOKEv->mnEvent = VclEventId::WindowMouseMove;
873 break;
874 default:
875 assert(false);
878 // no reason - just always true so far.
879 assert (rLokMouseEventData.meModifiers == MouseEventModifiers::SIMPLECLICK);
881 pLOKEv->maMouseEvent = MouseEvent(rLokMouseEventData.maPosition, rLokMouseEventData.mnCount,
882 rLokMouseEventData.meModifiers, rLokMouseEventData.mnButtons,
883 rLokMouseEventData.mnModifier);
884 if (rLokMouseEventData.maLogicPosition)
886 pLOKEv->maMouseEvent.setLogicPosition(*rLokMouseEventData.maLogicPosition);
888 pLOKEv->mpWindow = xWindow;
889 postEventAsync(pLOKEv);
892 void SfxLokHelper::dumpState(rtl::OStringBuffer &rState)
894 SfxViewShell* pShell = SfxViewShell::Current();
895 sal_Int32 nDocId = pShell ? static_cast<sal_Int32>(pShell->GetDocId().get()) : -1;
897 rState.append("\n\tDocId:\t");
898 rState.append(nDocId);
900 if (nDocId < 0)
901 return;
903 rState.append("\n\tViewCount:\t");
904 rState.append(static_cast<sal_Int32>(getViewsCount(nDocId)));
906 const SfxViewShell* const pCurrentViewShell = SfxViewShell::Current();
907 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
908 while (pViewShell)
910 if (pCurrentViewShell == nullptr || pViewShell->GetDocId() == pCurrentViewShell-> GetDocId())
911 pViewShell->dumpLibreOfficeKitViewState(rState);
913 pViewShell = SfxViewShell::GetNext(*pViewShell);
917 void SfxLokHelper::notifyMediaUpdate(boost::property_tree::ptree& json)
919 std::stringstream aStream;
920 boost::property_tree::write_json(aStream, json, /*pretty=*/ false);
921 const std::string str = aStream.str();
923 SfxLokHelper::notifyAllViews(LOK_CALLBACK_MEDIA_SHAPE, str.c_str());
926 bool SfxLokHelper::testInPlaceComponentMouseEventHit(SfxViewShell* pViewShell, int nType, int nX,
927 int nY, int nCount, int nButtons,
928 int nModifier, double fScaleX, double fScaleY,
929 bool bNegativeX)
931 // In LOK RTL mode draw/svx operates in negative X coordinates
932 // But the coordinates from client is always positive, so negate nX.
933 if (bNegativeX)
934 nX = -nX;
936 // check if the user hit a chart/math object which is being edited by this view
937 if (LokChartHelper aChartHelper(pViewShell, bNegativeX);
938 aChartHelper.postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier, fScaleX, fScaleY))
939 return true;
941 if (LokStarMathHelper aMathHelper(pViewShell);
942 aMathHelper.postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier, fScaleX, fScaleY))
943 return true;
945 // check if the user hit a chart which is being edited by someone else
946 // and, if so, skip current mouse event
947 if (nType != LOK_MOUSEEVENT_MOUSEMOVE)
949 if (LokChartHelper::HitAny({nX, nY}, bNegativeX))
950 return true;
953 return false;
956 VclPtr<vcl::Window> SfxLokHelper::getInPlaceDocWindow(SfxViewShell* pViewShell)
958 if (VclPtr<vcl::Window> pWindow = LokChartHelper(pViewShell).GetWindow())
959 return pWindow;
960 if (VclPtr<vcl::Window> pWindow = LokStarMathHelper(pViewShell).GetWidgetWindow())
961 return pWindow;
962 return {};
965 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */