1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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
;
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
52 assert(m_nDisabled
>= 0 && "Expected non-negative DisabledCallbacks state when disabling.");
58 assert(m_nDisabled
> 0 && "Expected positive DisabledCallbacks state when re-enabling.");
62 static inline bool disabled()
64 return !comphelper::LibreOfficeKit::isActive() || m_nDisabled
!= 0;
68 static int m_nDisabled
;
71 int DisableCallbacks::m_nDisabled
= 0;
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)
88 SfxViewShell::SetCurrentDocId(docId
);
89 SfxRequest
aRequest(pViewFrame
, SID_NEWWINDOW
);
90 pViewFrame
->ExecView_Impl(aRequest
);
91 SfxViewShell
* pViewShell
= SfxViewShell::Current();
92 if (pViewShell
== nullptr)
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)
108 return createView(pViewShell
->GetViewFrame(), pViewShell
->GetDocId());
111 int SfxLokHelper::createView(int nDocId
)
113 const SfxApplication
* pApp
= SfxApplication::Get();
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.
129 void SfxLokHelper::setEditMode(int nMode
, vcl::ITiledRenderable
* pDoc
)
132 pDoc
->setEditMode(nMode
);
135 void SfxLokHelper::destroyView(int nId
)
137 const SfxApplication
* pApp
= SfxApplication::Get();
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
);
156 void SfxLokHelper::setView(int nId
)
158 SfxApplication
* pApp
= SfxApplication::Get();
162 const ViewShellId
nViewShellId(nId
);
163 std::vector
<SfxViewShell
*>& rViewArr
= pApp
->GetViewShells_Impl();
165 for (const SfxViewShell
* pViewShell
: rViewArr
)
167 if (pViewShell
->GetViewShellId() == nViewShellId
)
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())
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
);
191 SfxViewShell
* SfxLokHelper::getViewOfId(int nId
)
193 SfxApplication
* pApp
= SfxApplication::Get();
197 const ViewShellId
nViewShellId(nId
);
198 std::vector
<SfxViewShell
*>& rViewArr
= pApp
->GetViewShells_Impl();
199 for (SfxViewShell
* pViewShell
: rViewArr
)
201 if (pViewShell
->GetViewShellId() == nViewShellId
)
208 int SfxLokHelper::getView(const SfxViewShell
* pViewShell
)
211 pViewShell
= SfxViewShell::Current();
212 // Still no valid view shell? Then no idea.
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();
227 const ViewShellDocId
nCurrentDocId(nDocId
);
229 SfxViewShell
* pViewShell
= SfxViewShell::GetFirst();
232 if (pViewShell
->GetDocId() == nCurrentDocId
)
234 pViewShell
= SfxViewShell::GetNext(*pViewShell
);
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();
248 const ViewShellDocId
nCurrentDocId(nDocId
);
250 SfxViewShell
* pViewShell
= SfxViewShell::GetFirst();
253 if (pViewShell
->GetDocId() == nCurrentDocId
)
258 pArray
[n
] = static_cast<sal_Int32
>(pViewShell
->GetViewShellId());
262 pViewShell
= SfxViewShell::GetNext(*pViewShell
);
268 int SfxLokHelper::getDocumentIdOfView(int nViewId
)
270 SfxViewShell
* pViewShell
= SfxViewShell::GetFirst();
273 if (pViewShell
->GetViewShellId() == ViewShellId(nViewId
))
274 return static_cast<int>(pViewShell
->GetDocId());
275 pViewShell
= SfxViewShell::GetNext(*pViewShell
);
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
);
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
);
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
;
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)
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
] == '\\')
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())
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())
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())
422 // Cache the payload so we only have to generate it once, at most.
426 const ViewShellDocId nCurrentDocId
= pThisView
->GetDocId();
427 SfxViewShell
* pViewShell
= SfxViewShell::GetFirst();
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())
453 // Cache the payload so we only have to generate it once, at most.
457 const ViewShellDocId nCurrentDocId
= pThisView
->GetDocId();
458 SfxViewShell
* pViewShell
= SfxViewShell::GetFirst();
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
);
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
);
490 const char* pName
= pSlot
->GetUnoName();
493 return ".uno:" + OStringToOUString(pName
, RTL_TEXTENCODING_ASCII_US
);
502 void SfxLokHelper::sendUnoStatus(const SfxViewShell
* pShell
, const SfxPoolItem
* pItem
)
504 if (!pShell
|| !pItem
|| pItem
== INVALID_POOL_ITEM
|| DisableCallbacks::disabled())
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())
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())
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())
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())
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();
587 // FIXME: What if SfxViewShell::Current() returned null?
588 // Should we then do this for all views of all open documents
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())
604 SfxViewShell
* pViewShell
= SfxViewShell::GetFirst();
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
+ " }";
630 void SfxLokHelper::notifyAllViews(int nType
, const OString
& rPayload
)
632 if (DisableCallbacks::disabled())
635 const auto payload
= rPayload
.getStr();
636 const SfxViewShell
* const pCurrentViewShell
= SfxViewShell::Current();
637 SfxViewShell
* pViewShell
= SfxViewShell::GetFirst();
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())
652 OUStringToOString(aApplication
.replace(' ', '_'), RTL_TEXTENCODING_UTF8
) +
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())
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())
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())
688 int viewId
= SfxLokHelper::getView(pThisView
);
689 const ViewShellDocId nCurrentDocId
= pThisView
->GetDocId();
690 SfxViewShell
* pViewShell
= SfxViewShell::GetFirst();
693 if (pViewShell
!= pThisView
&& nCurrentDocId
== pViewShell
->GetDocId())
694 pViewShell
->libreOfficeKitViewUpdatedCallbackPerViewId(nType
, viewId
, viewId
);
696 pViewShell
= SfxViewShell::GetNext(*pViewShell
);
702 struct LOKAsyncEventData
704 int mnView
; // Window is not enough.
705 VclPtr
<vcl::Window
> mpWindow
;
707 MouseEvent maMouseEvent
;
712 void LOKPostAsyncEvent(void* pEv
, void*)
714 std::unique_ptr
<LOKAsyncEventData
> pLOKEv(static_cast<LOKAsyncEventData
*>(pEv
));
715 if (pLOKEv
->mpWindow
->isDisposed())
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();
733 pFocusWindow
= pLOKEv
->mpWindow
;
735 if (pLOKEv
->mpWindow
->isDisposed())
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
);
750 case VclEventId::WindowKeyUp
:
751 if (!pFocusWindow
->isDisposed())
752 pFocusWindow
->KeyUp(pLOKEv
->maKeyEvent
);
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
);
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();
774 case VclEventId::WindowMouseMove
:
775 pLOKEv
->mpWindow
->SetLastMousePos(pLOKEv
->maMouseEvent
.GetPosPixel());
776 pLOKEv
->mpWindow
->MouseMove(pLOKEv
->maMouseEvent
);
778 case VclEventId::ExtTextInput
:
779 case VclEventId::EndExtTextInput
:
780 pLOKEv
->mpWindow
->PostExtTextInputEvent(pLOKEv
->mnEvent
, pLOKEv
->maText
);
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());
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);
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
;
815 case LOK_KEYEVENT_KEYINPUT
:
816 pLOKEv
->mnEvent
= VclEventId::WindowKeyInput
;
818 case LOK_KEYEVENT_KEYUP
:
819 pLOKEv
->mnEvent
= VclEventId::WindowKeyUp
;
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
);
835 pViewShell
->setBlockedCommandList(blockedCommandList
);
839 void SfxLokHelper::postExtTextEventAsync(const VclPtr
<vcl::Window
> &xWindow
,
840 int nType
, const OUString
&rText
)
842 LOKAsyncEventData
* pLOKEv
= new LOKAsyncEventData
;
845 case LOK_EXT_TEXTINPUT
:
846 pLOKEv
->mnEvent
= VclEventId::ExtTextInput
;
847 pLOKEv
->maText
= rText
;
849 case LOK_EXT_TEXTINPUT_END
:
850 pLOKEv
->mnEvent
= VclEventId::EndExtTextInput
;
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
;
868 case LOK_MOUSEEVENT_MOUSEBUTTONUP
:
869 pLOKEv
->mnEvent
= VclEventId::WindowMouseButtonUp
;
871 case LOK_MOUSEEVENT_MOUSEMOVE
:
872 pLOKEv
->mnEvent
= VclEventId::WindowMouseMove
;
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
);
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();
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
,
931 // In LOK RTL mode draw/svx operates in negative X coordinates
932 // But the coordinates from client is always positive, so negate 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
))
941 if (LokStarMathHelper
aMathHelper(pViewShell
);
942 aMathHelper
.postMouseEvent(nType
, nX
, nY
, nCount
, nButtons
, nModifier
, fScaleX
, fScaleY
))
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
))
956 VclPtr
<vcl::Window
> SfxLokHelper::getInPlaceDocWindow(SfxViewShell
* pViewShell
)
958 if (VclPtr
<vcl::Window
> pWindow
= LokChartHelper(pViewShell
).GetWindow())
960 if (VclPtr
<vcl::Window
> pWindow
= LokStarMathHelper(pViewShell
).GetWidgetWindow())
965 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */