summaryrefslogtreecommitdiffstats
path: root/sfx2/source/control/dispatch.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sfx2/source/control/dispatch.cxx
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sfx2/source/control/dispatch.cxx')
-rw-r--r--sfx2/source/control/dispatch.cxx2105
1 files changed, 2105 insertions, 0 deletions
diff --git a/sfx2/source/control/dispatch.cxx b/sfx2/source/control/dispatch.cxx
new file mode 100644
index 0000000000..fe7271677f
--- /dev/null
+++ b/sfx2/source/control/dispatch.cxx
@@ -0,0 +1,2105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_feature_desktop.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <vector>
+
+#include <stdlib.h>
+
+#include <boost/property_tree/json_parser.hpp>
+
+#include <com/sun/star/awt/PopupMenuDirection.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XDispatchRecorderSupplier.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/XPopupMenuController.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ui/ContextMenuExecuteEvent.hpp>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <hintpost.hxx>
+#include <sfx2/ipclient.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svl/eitem.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/itempool.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/menu.hxx>
+
+#include <sfxtypes.hxx>
+#include <slotserv.hxx>
+#include <workwin.hxx>
+
+typedef std::vector<SfxShell*> SfxShellStack_Impl;
+
+namespace {
+
+struct SfxToDo_Impl
+{
+ SfxShell* pCluster;
+ bool bPush;
+ bool bDelete;
+ bool bDeleted;
+ bool bUntil;
+
+ SfxToDo_Impl( bool bOpPush, bool bOpDelete, bool bOpUntil, SfxShell& rCluster )
+ : pCluster(&rCluster)
+ , bPush(bOpPush)
+ , bDelete(bOpDelete)
+ , bDeleted(false)
+ , bUntil(bOpUntil)
+ {}
+};
+
+struct SfxObjectBars_Impl
+{
+ ToolbarId eId; // ConfigId of the Toolbox
+ sal_uInt16 nPos;
+ SfxVisibilityFlags nFlags; // special visibility flags
+
+ SfxObjectBars_Impl() : eId(ToolbarId::None), nPos(0), nFlags(SfxVisibilityFlags::Invisible) {}
+};
+
+}
+
+struct SfxDispatcher_Impl
+{
+ //When the dispatched is locked, SfxRequests accumulate in aReqArr for
+ //later dispatch when unlocked via Post
+
+ //The pointers are typically deleted in Post, only if we never get around
+ //to posting them do we delete the unposted requests.
+ std::vector<std::unique_ptr<SfxRequest>>
+ aReqArr;
+ SfxShellStack_Impl aStack; // active functionality
+ Idle aIdle { "sfx::SfxDispatcher_Impl aIdle" }; // for Flush
+ std::deque<SfxToDo_Impl> aToDoStack; // not processed Push/Pop
+ SfxViewFrame* pFrame; // NULL or associated Frame
+ tools::SvRef<SfxHintPoster>
+ xPoster; // Execute asynchronous
+ bool bFlushing; // sal_True during Flush //?
+ bool bUpdated; // Update_Impl has run
+ bool bLocked; // No Execute
+ bool bInvalidateOnUnlock; // because someone asked
+ bool bActive; // not to be confused with set!
+ bool* pInCallAliveFlag; // view the Destructor Stack
+ SfxObjectBars_Impl aObjBars[SFX_OBJECTBAR_MAX];
+ SfxObjectBars_Impl aFixedObjBars[SFX_OBJECTBAR_MAX];
+ std::vector<sal_uInt32> aChildWins;
+ bool bNoUI; // UI only from Parent Dispatcher
+ bool bReadOnly; // Document is ReadOnly
+ bool bQuiet; // Only use parent dispatcher
+
+ SfxSlotFilterState nFilterEnabling; // 1==filter enabled slots,
+ // 2==ReadOnlyDoc overturned
+ std::span<sal_uInt16 const>
+ pFilterSIDs; // sorted Array of SIDs
+ SfxDisableFlags nDisableFlags;
+ bool bFlushed;
+ std::deque< std::deque<SfxToDo_Impl> > aToDoCopyStack;
+};
+
+/** This method checks if the stack of the SfxDispatchers is flushed, or if
+ push- or pop- commands are pending.
+*/
+bool SfxDispatcher::IsFlushed() const
+{
+ return xImp->bFlushed;
+}
+
+/** This method performs outstanding push- and pop- commands. For <SfxShell>s,
+ which are new on the stack, the <SfxShell::Activate(bool)> is invoked
+ with bMDI == sal_True, for SfxShells that are removed from the stack, the
+ <SfxShell::Deactivate(bool)> is invoked with bMDI == sal_True
+*/
+void SfxDispatcher::Flush()
+{
+ if (!xImp->bFlushed) FlushImpl();
+}
+
+/** With this method, a <SfxShell> pushed on to the SfxDispatcher.
+ The SfxShell is first marked for push and a timer is set up.
+ First when the timer has counted down to zero the push
+ ( <SfxDispatcher::Flush()> ) is actually performed and the
+ <SfxBindings> is invalidated. While the timer is counting down
+ the opposing push and pop commands on the same SfxShell are
+ leveled out.
+*/
+void SfxDispatcher::Push(SfxShell& rShell)
+
+{
+ Pop( rShell, SfxDispatcherPopFlags::PUSH );
+}
+
+/** This method checks whether a particular <SfxShell> instance is
+ on the SfxDispatcher.
+
+ @returns true The SfxShell instance is on the SfxDispatcher.
+ false The SfxShell instance is not on the SfxDispatcher.
+*/
+bool SfxDispatcher::IsActive(const SfxShell& rShell)
+
+{
+ return CheckVirtualStack(rShell);
+}
+
+/** With this method it can be determined whether the SfxDispatcher is
+ locked or unlocked. A locked SfxDispatcher does not perform <SfxRequest>s
+ and no longer provides any status information. It behaves as if all the
+ slots are disabled.
+
+ The dispatcher is also marked as blocked, if all Dispatcher are locked
+ (<SfxApplication::LockDispatcher()>) or the associated top frame is in the
+ modal-mode and if the specified slot are handled as frame-specific
+ (ie, not served by the application).
+*/
+bool SfxDispatcher::IsLocked() const
+{
+ return xImp->bLocked;
+}
+
+/** With this method it can be determined if the SfxDispacher is the
+ applications dispatcher.
+
+ @return bool it is the application dispatcher.
+*/
+bool SfxDispatcher::IsAppDispatcher() const
+{
+ return !xImp->pFrame;
+}
+
+/** Helper function to check whether a slot can be executed and
+ check the execution itself
+*/
+void SfxDispatcher::Call_Impl(SfxShell& rShell, const SfxSlot &rSlot, SfxRequest &rReq, bool bRecord)
+{
+ SFX_STACK(SfxDispatcher::Call_Impl);
+
+ // The slot may be called (meaning enabled)
+ if ( !rSlot.IsMode(SfxSlotMode::FASTCALL) && !rShell.CanExecuteSlot_Impl(rSlot) && !rShell.IsConditionalFastCall(rReq) )
+ return;
+
+ if ( GetFrame() )
+ {
+ // Recording may start
+ css::uno::Reference< css::beans::XPropertySet > xSet(
+ GetFrame()->GetFrame().GetFrameInterface(),
+ css::uno::UNO_QUERY);
+
+ if ( xSet.is() )
+ {
+ css::uno::Any aProp = xSet->getPropertyValue("DispatchRecorderSupplier");
+ css::uno::Reference< css::frame::XDispatchRecorderSupplier > xSupplier;
+ css::uno::Reference< css::frame::XDispatchRecorder > xRecorder;
+ aProp >>= xSupplier;
+ if(xSupplier.is())
+ xRecorder = xSupplier->getDispatchRecorder();
+
+ if ( bRecord && xRecorder.is() && !rSlot.IsMode(SfxSlotMode::NORECORD) )
+ rReq.Record_Impl( rShell, rSlot, xRecorder, GetFrame() );
+ }
+ }
+ // Get all that is needed, because the slot may not have survived the
+ // Execute if it is a 'pseudo slot' for macros or verbs.
+ bool bAutoUpdate = rSlot.IsMode(SfxSlotMode::AUTOUPDATE);
+
+ // API-call parentheses and document-lock during the calls
+ {
+ // 'this' must respond in the Destructor
+ bool bThisDispatcherAlive = true;
+ bool *pOldInCallAliveFlag = xImp->pInCallAliveFlag;
+ xImp->pInCallAliveFlag = &bThisDispatcherAlive;
+
+ SfxExecFunc pFunc = rSlot.GetExecFnc();
+ (*pFunc)(&rShell, rReq);
+
+ // If 'this' is still alive
+ if ( bThisDispatcherAlive )
+ xImp->pInCallAliveFlag = pOldInCallAliveFlag;
+ else
+ {
+ if ( pOldInCallAliveFlag )
+ {
+ // also protect nested stack frames
+ *pOldInCallAliveFlag = false;
+ }
+
+ // do nothing after this object is dead
+ return;
+ }
+ }
+
+ if ( rReq.IsDone() )
+ {
+ SfxBindings *pBindings = GetBindings();
+
+ // When AutoUpdate update immediately
+ if ( bAutoUpdate && pBindings )
+ {
+ pBindings->Invalidate(rSlot.GetSlotId());
+ pBindings->Update(rSlot.GetSlotId());
+ }
+ }
+}
+
+void SfxDispatcher::Construct_Impl()
+{
+ xImp.reset(new SfxDispatcher_Impl);
+ xImp->bFlushed = true;
+
+ xImp->bFlushing = false;
+ xImp->bUpdated = false;
+ xImp->bLocked = false;
+ xImp->bActive = false;
+ xImp->bNoUI = false;
+ xImp->bReadOnly = false;
+ xImp->bQuiet = false;
+ xImp->pInCallAliveFlag = nullptr;
+ xImp->nFilterEnabling = SfxSlotFilterState::DISABLED;
+ xImp->nDisableFlags = SfxDisableFlags::NONE;
+
+ xImp->bInvalidateOnUnlock = false;
+
+ for (SfxObjectBars_Impl & rObjBar : xImp->aObjBars)
+ rObjBar.eId = ToolbarId::None;
+
+ xImp->xPoster = new SfxHintPoster(std::bind(&SfxDispatcher::PostMsgHandler, this, std::placeholders::_1));
+
+ xImp->aIdle.SetPriority(TaskPriority::HIGH_IDLE );
+ xImp->aIdle.SetInvokeHandler( LINK(this, SfxDispatcher, EventHdl_Impl ) );
+}
+
+SfxDispatcher::SfxDispatcher()
+{
+ Construct_Impl();
+ xImp->pFrame = nullptr;
+}
+
+/** The constructor of the SfxDispatcher class places a stack of empty
+ <SfxShell> pointers. It is not initially locked and is considered flushed.
+*/
+SfxDispatcher::SfxDispatcher(SfxViewFrame *pViewFrame)
+{
+ Construct_Impl();
+ xImp->pFrame = pViewFrame;
+}
+
+/** The destructor of the SfxDispatcher class should not be called when the
+ SfxDispatcher instance is active. It may, however, still be a <SfxShell>
+ pointer on the stack.
+*/
+SfxDispatcher::~SfxDispatcher()
+{
+ SAL_INFO("sfx.control", "Delete Dispatcher " << reinterpret_cast<sal_Int64>(this));
+ DBG_ASSERT( !xImp->bActive, "deleting active Dispatcher" );
+
+ // So that no timer by Reschedule in PlugComm strikes the LeaveRegistrations
+ xImp->aIdle.Stop();
+ xImp->xPoster->SetEventHdl( std::function<void (std::unique_ptr<SfxRequest>)>() );
+
+ // Notify the stack variables in Call_Impl
+ if ( xImp->pInCallAliveFlag )
+ *xImp->pInCallAliveFlag = false;
+
+ // Get bindings and application
+ SfxApplication *pSfxApp = SfxGetpApp();
+ SfxBindings* pBindings = GetBindings();
+
+ // When not flushed, revive the bindings
+ if (pBindings && !pSfxApp->IsDowning() && !xImp->bFlushed)
+ pBindings->DLEAVEREGISTRATIONS();
+
+ // may unregister the bindings
+ while ( pBindings )
+ {
+ if ( pBindings->GetDispatcher_Impl() == this)
+ pBindings->SetDispatcher(nullptr);
+ pBindings = pBindings->GetSubBindings_Impl();
+ }
+}
+
+/** With this method, one or more <SfxShell> are popped from the SfxDispatcher.
+ The SfxShell is marked for popping and a timer is set up. Only when the
+ timer has reached the end, the pop is actually performed
+ ( <SfxDispatcher::Flush()> ) and the <SfxBindings> is invalidated.
+ While the timer is running the opposing push and pop commands on one
+ SfxShell cancel each other out.
+
+ @param rShell the stack to take the SfxShell instance.
+ @param nMode SfxDispatcherPopFlags::POP_UNTIL
+ Also all 'rShell' of SfxShells are taken from the
+ stack.
+
+ SfxDispatcherPopFlags::POP_DELETE
+ All SfxShells actually taken from the stack
+ will be deleted.
+
+ SfxDispatcherPopFlags::PUSH (InPlace use only)
+ The Shell is pushed.
+*/
+void SfxDispatcher::Pop(SfxShell& rShell, SfxDispatcherPopFlags nMode)
+{
+ DBG_ASSERT( rShell.GetInterface(),
+ "pushing SfxShell without previous RegisterInterface()" );
+
+ bool bDelete = bool(nMode & SfxDispatcherPopFlags::POP_DELETE);
+ bool bUntil = bool(nMode & SfxDispatcherPopFlags::POP_UNTIL);
+ bool bPush = bool(nMode & SfxDispatcherPopFlags::PUSH);
+
+ SfxApplication *pSfxApp = SfxGetpApp();
+
+ SAL_INFO(
+ "sfx.control",
+ "-SfxDispatcher(" << this << (bPush ? ")::Push(" : ")::Pop(")
+ << (rShell.GetInterface()
+ ? rShell.GetInterface()->GetClassName() : SAL_STREAM(&rShell))
+ << (bDelete ? ") with delete" : ")")
+ << (bUntil ? " (up to)" : ""));
+
+ // same shell as on top of the to-do stack?
+ if(!xImp->aToDoStack.empty() && xImp->aToDoStack.front().pCluster == &rShell)
+ {
+ // cancel inverse actions
+ if ( xImp->aToDoStack.front().bPush != bPush )
+ xImp->aToDoStack.pop_front();
+ else
+ {
+ DBG_ASSERT( bPush, "SfxInterface pushed more than once" );
+ DBG_ASSERT( !bPush, "SfxInterface popped more than once" );
+ }
+ }
+ else
+ {
+ // Remember Action
+ xImp->aToDoStack.push_front( SfxToDo_Impl(bPush, bDelete, bUntil, rShell) );
+ if (xImp->bFlushed)
+ {
+ SAL_INFO("sfx.control", "Unflushed dispatcher!");
+ xImp->bFlushed = false;
+ xImp->bUpdated = false;
+
+ // Put bindings to sleep
+ SfxBindings* pBindings = GetBindings();
+ if ( pBindings )
+ pBindings->DENTERREGISTRATIONS();
+ }
+ }
+
+ if(!pSfxApp->IsDowning() && !xImp->aToDoStack.empty())
+ {
+ // No immediate update is requested
+ xImp->aIdle.Start();
+ }
+ else
+ {
+ // but to do nothing
+ xImp->aIdle.Stop();
+
+ // Bindings may wake up again
+ if(xImp->aToDoStack.empty())
+ {
+ SfxBindings* pBindings = GetBindings();
+ if ( pBindings )
+ pBindings->DLEAVEREGISTRATIONS();
+ }
+ }
+}
+
+
+/** This handler is called after <SfxDispatcher::Invalidate()> or after
+ changes on the stack (<SfxDispatcher::Push()> and <SfxDispatcher::Pop())
+
+ It flushes the Stack, if it is dirty, thus it actually executes the
+ pending Push and Pop commands.
+*/
+IMPL_LINK_NOARG( SfxDispatcher, EventHdl_Impl, Timer *, void )
+{
+ Flush();
+ Update_Impl();
+ SfxBindings* pBindings = GetBindings();
+ if ( pBindings )
+ pBindings->StartUpdate_Impl();
+}
+
+/** With this method it can be tested whether the <SfxShell> rShell is on the
+ stack, when it was flushed. This way the SfxDispatcher is not actually
+ flushed.
+
+ This method is intended among other things to make assertions possible
+ without the side effect of having to flush the SfxDispatcher.
+*/
+bool SfxDispatcher::CheckVirtualStack(const SfxShell& rShell)
+{
+ SFX_STACK(SfxDispatcher::CheckVirtualStack);
+
+ SfxShellStack_Impl aStack( xImp->aStack );
+ for(std::deque<SfxToDo_Impl>::reverse_iterator i = xImp->aToDoStack.rbegin(); i != xImp->aToDoStack.rend(); ++i)
+ {
+ if(i->bPush)
+ aStack.push_back(i->pCluster);
+ else
+ {
+ SfxShell* pPopped(nullptr);
+ do
+ {
+ DBG_ASSERT( !aStack.empty(), "popping from empty stack" );
+ pPopped = aStack.back();
+ aStack.pop_back();
+ }
+ while(i->bUntil && pPopped != i->pCluster);
+ DBG_ASSERT(pPopped == i->pCluster, "popping unpushed SfxInterface");
+ }
+ }
+
+ bool bReturn = std::find(aStack.begin(), aStack.end(), &rShell) != aStack.end();
+ return bReturn;
+}
+
+/** Determines the position of a given SfxShell in the stack of the dispatcher.
+ If possible this is flushed before.
+
+ [Return value]
+
+ sal_uInt16 == USRT_MAX
+ The SfxShell is not on this SfxDispatcher.
+
+ < USHRT_MAX
+ Position of the SfxShell on the Dispatcher
+ from the top count stating with 0.
+*/
+sal_uInt16 SfxDispatcher::GetShellLevel(const SfxShell& rShell)
+{
+ SFX_STACK(SfxDispatcher::GetShellLevel);
+ Flush();
+
+ for ( size_t n = 0; n < xImp->aStack.size(); ++n )
+ if ( *( xImp->aStack.rbegin() + n ) == &rShell )
+ return n;
+
+ return USHRT_MAX;
+}
+
+/** Returns a pointer to the <SfxShell> which is at the position nIdx
+ (from the top, last pushed is 0) on the stack.
+
+ Thus the SfxDispatcher is not flushed.
+
+ Is the stack not deep enough a NULL-Pointer is returned.
+*/
+SfxShell *SfxDispatcher::GetShell(sal_uInt16 nIdx) const
+{
+ sal_uInt16 nShellCount = xImp->aStack.size();
+ if ( nIdx < nShellCount )
+ return *(xImp->aStack.rbegin() + nIdx);
+ return nullptr;
+}
+
+/** This method returns a pointer to the <SfxBinding> Instance on which the
+ SfxDispatcher is currently bound. A SfxDispatcher is only bound to
+ the SfxBindings when it is <UI-aktiv>. If it is not UI-active,
+ a NULL-pointer is returned.
+
+ The returned pointer is only valid in the immediate context of the method
+ call.
+*/
+SfxBindings* SfxDispatcher::GetBindings() const
+{
+ if ( xImp->pFrame )
+ return &xImp->pFrame->GetBindings();
+ else
+ return nullptr;
+}
+
+/** Returns a pointer to the <SfxViewFrame> instance, which belongs to
+ this SfxDispatcher. If it is about the application dispatcher,
+ a NULL-pointer is returned.
+*/
+SfxViewFrame* SfxDispatcher::GetFrame() const
+{
+ return xImp->pFrame;
+}
+
+/** This method controls the activation of a dispatcher.
+
+ Since the application dispatcher is always active, either as a sub
+ dispatcher of the <SfxViewFrame> dispatcher or as itself, it is never
+ activated as a whole, instead only its individual <SfxShell>s at
+ <SfxDispatcher::Push(SfxShell&)>.
+
+ When activating a SfxDispatcher all of the SfxShells located on its stack
+ are called with the handler <SfxShell::Activate(bool)>, starting with
+ the lowest.
+*/
+void SfxDispatcher::DoActivate_Impl(bool bMDI)
+{
+ SFX_STACK(SfxDispatcher::DoActivate);
+ if ( bMDI )
+ {
+ SAL_INFO("sfx.control", "Activate Dispatcher " << reinterpret_cast<sal_Int64>(this));
+ DBG_ASSERT( !xImp->bActive, "Activation error" );
+
+ xImp->bActive = true;
+ xImp->bUpdated = false;
+ SfxBindings* pBindings = GetBindings();
+ if ( pBindings )
+ {
+ pBindings->SetDispatcher(this);
+ pBindings->SetActiveFrame( xImp->pFrame->GetFrame().GetFrameInterface() );
+ }
+ }
+ else
+ {
+ SAL_INFO("sfx.control", "Non-MDI-Activate Dispatcher " << reinterpret_cast<sal_Int64>(this));
+ }
+
+ if ( IsAppDispatcher() )
+ return;
+
+ for ( int i = int(xImp->aStack.size()) - 1; i >= 0; --i )
+ (*(xImp->aStack.rbegin() + i ))->DoActivate_Impl(xImp->pFrame, bMDI);
+
+ if ( bMDI && xImp->pFrame )
+ {
+ xImp->pFrame->GetFrame().GetWorkWindow_Impl()->HidePopups_Impl( false, 1 );
+ }
+
+ if(!xImp->aToDoStack.empty())
+ {
+ // No immediate update is requested
+ xImp->aIdle.Start();
+ }
+}
+
+/** This method controls the deactivation of a dispatcher.
+
+ Since the application dispatcher is always active, either as a sub
+ dispatcher of the <SfxViewFrame> dispatcher or as itself, it is never
+ deactivated as a whole, instead only its individual <SfxShell>s at
+ <SfxDispatcher::Pop(SfxShell&)>.
+
+ When deactivating a SfxDispatcher all of the SfxShells located on its stack
+ are called with the handler <SfxShell::Deactivate(bool)>, starting with
+ the lowest.
+*/
+void SfxDispatcher::DoDeactivate_Impl(bool bMDI, SfxViewFrame const * pNew)
+{
+ SFX_STACK(SfxDispatcher::DoDeactivate);
+
+ SfxApplication *pSfxApp = SfxGetpApp();
+
+ if ( bMDI )
+ {
+ SAL_INFO("sfx.control", "Deactivate Dispatcher " << this);
+ DBG_ASSERT( xImp->bActive, "Deactivate error" );
+ xImp->bActive = false;
+
+ if ( xImp->pFrame && !(xImp->pFrame->GetObjectShell()->IsInPlaceActive() ) )
+ {
+ SfxWorkWindow *pWorkWin = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
+ if ( pWorkWin )
+ {
+ for (size_t n=0; n<xImp->aChildWins.size();)
+ {
+ SfxChildWindow *pWin = pWorkWin->GetChildWindow_Impl( static_cast<sal_uInt16>( xImp->aChildWins[n] & 0xFFFF ) );
+ if (!pWin || pWin->GetAlignment() == SfxChildAlignment::NOALIGNMENT)
+ xImp->aChildWins.erase(xImp->aChildWins.begin()+n);
+ else
+ n++;
+ }
+ }
+ }
+ }
+ else {
+ SAL_INFO("sfx.control", "Non-MDI-DeActivate Dispatcher " << this);
+ }
+
+ if ( IsAppDispatcher() && !pSfxApp->IsDowning() )
+ return;
+
+ for ( size_t i = 0; i < xImp->aStack.size(); ++i )
+ (*(xImp->aStack.rbegin() + i))->DoDeactivate_Impl(xImp->pFrame, bMDI);
+
+ bool bHidePopups = bMDI && xImp->pFrame;
+ if ( pNew && xImp->pFrame )
+ {
+ css::uno::Reference< css::frame::XFrame > xOldFrame =
+ pNew->GetFrame().GetFrameInterface()->getCreator();
+
+ css::uno::Reference< css::frame::XFrame > xMyFrame =
+ GetFrame()->GetFrame().GetFrameInterface();
+
+ if ( xOldFrame == xMyFrame )
+ bHidePopups = false;
+ }
+
+ if ( bHidePopups )
+ {
+ xImp->pFrame->GetFrame().GetWorkWindow_Impl()->HidePopups_Impl( true, 1 );
+ }
+
+ Flush();
+}
+
+/** This method searches in SfxDispatcher after <SfxShell> , from the Slot Id
+ nSlot currently being handled. For this, the dispatcher is first flushed.
+
+ @param nSlot the searchable Slot-Id
+ @param ppShell the SfxShell, which are currently handled the nSlot
+ @param ppSlot the SfxSlot, which are currently handled the nSlot
+
+ @return int sal_True
+ The SfxShell was found, ppShell and ppSlot are valid.
+
+ sal_False
+ The SfxShell was not found, ppShell and ppSlot are invalid.
+*/
+bool SfxDispatcher::GetShellAndSlot_Impl(sal_uInt16 nSlot, SfxShell** ppShell,
+ const SfxSlot** ppSlot, bool bOwnShellsOnly, bool bRealSlot)
+{
+ SFX_STACK(SfxDispatcher::GetShellAndSlot_Impl);
+
+ Flush();
+ SfxSlotServer aSvr;
+ if ( FindServer_(nSlot, aSvr) )
+ {
+ if ( bOwnShellsOnly && aSvr.GetShellLevel() >= xImp->aStack.size() )
+ return false;
+
+ *ppShell = GetShell(aSvr.GetShellLevel());
+ *ppSlot = aSvr.GetSlot();
+ if ( nullptr == (*ppSlot)->GetExecFnc() && bRealSlot )
+ *ppSlot = (*ppShell)->GetInterface()->GetRealSlot(*ppSlot);
+ // Check only real slots as enum slots don't have an execute function!
+ return !bRealSlot || ((nullptr != *ppSlot) && (nullptr != (*ppSlot)->GetExecFnc()) );
+ }
+
+ return false;
+}
+
+/** This method performs a request for a cached <Slot-Server>.
+
+ @param rShell to the calling <SfxShell>
+ @param rSlot to the calling <SfxSlot>
+ @param rReq function to be performed (Id and optional parameters)
+ @param eCallMode Synchronously, asynchronously or as shown in the slot
+*/
+void SfxDispatcher::Execute_(SfxShell& rShell, const SfxSlot& rSlot,
+ SfxRequest& rReq, SfxCallMode eCallMode)
+{
+ SFX_STACK(SfxDispatcher::Execute_);
+ DBG_ASSERT( !xImp->bFlushing, "recursive call to dispatcher" );
+ DBG_ASSERT( xImp->aToDoStack.empty(), "unprepared InPlace _Execute" );
+
+ if ( IsLocked() )
+ return;
+
+ if ( bool(eCallMode & SfxCallMode::ASYNCHRON) ||
+ ( (eCallMode & SfxCallMode::SYNCHRON) == SfxCallMode::SLOT &&
+ rSlot.IsMode(SfxSlotMode::ASYNCHRON) ) )
+ {
+ sal_uInt16 nShellCount = xImp->aStack.size();
+ for ( sal_uInt16 n=0; n<nShellCount; n++ )
+ {
+ if ( &rShell == *(xImp->aStack.rbegin() + n) )
+ {
+ if ( bool(eCallMode & SfxCallMode::RECORD) )
+ rReq.AllowRecording( true );
+ xImp->xPoster->Post(std::make_unique<SfxRequest>(rReq));
+ return;
+ }
+ }
+ }
+ else
+ Call_Impl( rShell, rSlot, rReq, SfxCallMode::RECORD==(eCallMode&SfxCallMode::RECORD) );
+}
+
+/** Helper function to put from rItem below the Which-ID in the pool of the
+ Item Sets rSet.
+*/
+static void MappedPut_Impl(SfxAllItemSet &rSet, const SfxPoolItem &rItem)
+{
+ // Put with mapped Which-Id if possible
+ const SfxItemPool *pPool = rSet.GetPool();
+ sal_uInt16 nWhich = rItem.Which();
+ if ( SfxItemPool::IsSlot(nWhich) )
+ nWhich = pPool->GetWhich(nWhich);
+ rSet.Put( rItem, nWhich );
+}
+
+const SfxSlot* SfxDispatcher::GetSlot( const OUString& rCommand )
+{
+ // Count the number of Shells on the linked Dispatcher
+ Flush();
+ sal_uInt16 nTotCount = xImp->aStack.size();
+
+ for ( sal_uInt16 i = 0; i < nTotCount; ++i )
+ {
+ if (SfxShell *pObjShell = GetShell(i))
+ {
+ SfxInterface *pIFace = pObjShell->GetInterface();
+ const SfxSlot *pSlot = pIFace->GetSlot( rCommand );
+ if ( pSlot )
+ return pSlot;
+ }
+ }
+
+ return nullptr;
+}
+
+SfxPoolItemHolder SfxDispatcher::Execute(sal_uInt16 nSlot, SfxCallMode nCall,
+ SfxItemSet const * pArgs, SfxItemSet const * pInternalArgs, sal_uInt16 nModi)
+{
+ if ( IsLocked() )
+ return SfxPoolItemHolder();
+
+ SfxShell *pShell = nullptr;
+ const SfxSlot *pSlot = nullptr;
+ if ( GetShellAndSlot_Impl( nSlot, &pShell, &pSlot, false, true ) )
+ {
+ SfxAllItemSet aSet( pShell->GetPool() );
+ if ( pArgs )
+ {
+ SfxItemIter aIter(*pArgs);
+ for ( const SfxPoolItem *pArg = aIter.GetCurItem();
+ pArg;
+ pArg = aIter.NextItem() )
+ MappedPut_Impl( aSet, *pArg );
+ }
+ SfxRequest aReq(nSlot, nCall, aSet);
+ if (pInternalArgs)
+ aReq.SetInternalArgs_Impl( *pInternalArgs );
+ aReq.SetModifier( nModi );
+
+ Execute_( *pShell, *pSlot, aReq, nCall );
+ return aReq.GetReturnValue();
+ }
+
+ return SfxPoolItemHolder();
+}
+
+/** Method to execute a <SfxSlot>s over the Slot-Id.
+
+ @param nSlot the Id of the executing function
+ @param eCall SfxCallMode::SYNCRHON, ..._ASYNCHRON or ..._SLOT
+ @param pArgs Zero terminated C-Array of Parameters
+ @param pInternalArgs Zero terminated C-Array of Parameters
+
+ @return const SfxPoolItem* Pointer to the SfxPoolItem valid to the next run
+ though the Message-Loop, which contains the return
+ value.
+
+ Or a NULL-Pointer, when the function was not
+ executed (for example canceled by the user).
+*/
+SfxPoolItemHolder SfxDispatcher::Execute(sal_uInt16 nSlot, SfxCallMode eCall,
+ const SfxPoolItem **pArgs, sal_uInt16 nModi, const SfxPoolItem **pInternalArgs)
+{
+ if ( IsLocked() )
+ return SfxPoolItemHolder();
+
+ SfxShell *pShell = nullptr;
+ const SfxSlot *pSlot = nullptr;
+ if ( GetShellAndSlot_Impl( nSlot, &pShell, &pSlot, false, true ) )
+ {
+ std::unique_ptr<SfxRequest> pReq;
+ if ( pArgs && *pArgs )
+ {
+ SfxAllItemSet aSet( pShell->GetPool() );
+ for ( const SfxPoolItem **pArg = pArgs; *pArg; ++pArg )
+ MappedPut_Impl( aSet, **pArg );
+ pReq.reset(new SfxRequest( nSlot, eCall, aSet ));
+ }
+ else
+ pReq.reset(new SfxRequest( nSlot, eCall, pShell->GetPool() ));
+ pReq->SetModifier( nModi );
+ if( pInternalArgs && *pInternalArgs)
+ {
+ SfxAllItemSet aSet( SfxGetpApp()->GetPool() );
+ for ( const SfxPoolItem **pArg = pInternalArgs; *pArg; ++pArg )
+ aSet.Put( **pArg );
+ pReq->SetInternalArgs_Impl( aSet );
+ }
+ Execute_( *pShell, *pSlot, *pReq, eCall );
+ return pReq->GetReturnValue();
+ }
+
+ return SfxPoolItemHolder();
+}
+
+/** Method to execute a <SfxSlot>s over the Slot-Id.
+
+ @param nSlot the Id of the executing function
+ @param eCall SfxCallMode::SYNCRHON, ..._ASYNCHRON or ..._SLOT
+ @param rArgs <SfxItemSet> with the parameters
+
+ @return const SfxPoolItem* Pointer to the SfxPoolItem valid to the next run
+ though the Message-Loop, which contains the return
+ value.
+
+ Or a NULL-Pointer, when the function was not
+ executed (for example canceled by the user).
+*/
+SfxPoolItemHolder SfxDispatcher::Execute(sal_uInt16 nSlot, SfxCallMode eCall,
+ const SfxItemSet &rArgs)
+{
+ if ( IsLocked() )
+ return SfxPoolItemHolder();
+
+ SfxShell *pShell = nullptr;
+ const SfxSlot *pSlot = nullptr;
+ if ( GetShellAndSlot_Impl( nSlot, &pShell, &pSlot, false, true ) )
+ {
+ SfxAllItemSet aSet( pShell->GetPool() );
+ SfxItemIter aIter(rArgs);
+ for ( const SfxPoolItem *pArg = aIter.GetCurItem();
+ pArg;
+ pArg = aIter.NextItem() )
+ MappedPut_Impl( aSet, *pArg );
+ SfxRequest aReq( nSlot, eCall, aSet );
+ aReq.SetModifier( 0 );
+ Execute_( *pShell, *pSlot, aReq, eCall );
+ return aReq.GetReturnValue();
+ }
+
+ return SfxPoolItemHolder();
+}
+
+/** Method to execute a <SfxSlot>s over the Slot-Id.
+
+ [Note]
+
+ The parameters are copied, can therefore be passed on as the address
+ of stack objects.
+
+ @param nSlot the Id of the executing function
+ @param eCall SfxCallMode::SYNCRHON, ..._ASYNCHRON or ..._SLOT
+ @param args list of SfxPoolItem arguments
+
+ @return Pointer to the SfxPoolItem valid to the next run
+ though the Message-Loop, which contains the return
+ value.
+
+ Or a NULL-Pointer, when the function was not
+ executed (for example canceled by the user).
+
+ [Example]
+
+ pDispatcher->Execute( SID_OPENDOCUMENT, SfxCallMode::SYNCHRON,
+ { &SfxStringItem( SID_FILE_NAME, "\\tmp\\temp.sdd" ),
+ &SfxStringItem( SID_FILTER_NAME, "StarDraw Presentation" ),
+ &SfxBoolItem( SID_DOC_READONLY, sal_False ),
+ });
+*/
+SfxPoolItemHolder SfxDispatcher::ExecuteList(sal_uInt16 nSlot, SfxCallMode eCall,
+ std::initializer_list<SfxPoolItem const*> args,
+ std::initializer_list<SfxPoolItem const*> internalargs)
+{
+ if ( IsLocked() )
+ return SfxPoolItemHolder();
+
+ SfxShell *pShell = nullptr;
+ const SfxSlot *pSlot = nullptr;
+ if ( GetShellAndSlot_Impl( nSlot, &pShell, &pSlot, false, true ) )
+ {
+ SfxAllItemSet aSet( pShell->GetPool() );
+
+ for (const SfxPoolItem *pArg : args)
+ {
+ assert(pArg);
+ MappedPut_Impl( aSet, *pArg );
+ }
+
+ SfxRequest aReq(nSlot, eCall, aSet);
+
+ if (internalargs.begin() != internalargs.end())
+ {
+ SfxAllItemSet aInternalSet(SfxGetpApp()->GetPool());
+ for (const SfxPoolItem *pArg : internalargs)
+ {
+ assert(pArg);
+ aInternalSet.Put(*pArg);
+ }
+ aReq.SetInternalArgs_Impl(aInternalSet);
+ }
+
+ Execute_( *pShell, *pSlot, aReq, eCall );
+ return aReq.GetReturnValue();
+ }
+
+ return SfxPoolItemHolder();
+}
+
+/** Helper method to receive the asynchronously executed <SfxRequest>s.
+*/
+void SfxDispatcher::PostMsgHandler(std::unique_ptr<SfxRequest> pReq)
+{
+ DBG_ASSERT( !xImp->bFlushing, "recursive call to dispatcher" );
+ SFX_STACK(SfxDispatcher::PostMsgHandler);
+
+ // Has also the Pool not yet died?
+ if ( pReq->IsCancelled() )
+ return;
+
+ if ( !IsLocked() )
+ {
+ Flush();
+ SfxSlotServer aSvr;
+ if ( FindServer_(pReq->GetSlot(), aSvr ) ) // HACK(x), whatever that was supposed to mean
+ {
+ const SfxSlot *pSlot = aSvr.GetSlot();
+ SfxShell *pSh = GetShell(aSvr.GetShellLevel());
+
+ // When the pSlot is a "Pseudoslot" for macros or Verbs, it can
+ // be destroyed in the Call_Impl, thus do not use it anymore!
+ pReq->SetSynchronCall( false );
+ Call_Impl( *pSh, *pSlot, *pReq, pReq->AllowsRecording() ); //! why bRecord?
+ }
+ }
+ else
+ {
+ if ( xImp->bLocked )
+ xImp->aReqArr.emplace_back(std::move(pReq));
+ else
+ xImp->xPoster->Post(std::move(pReq));
+ }
+}
+
+void SfxDispatcher::SetMenu_Impl()
+{
+#if HAVE_FEATURE_DESKTOP
+ if ( !xImp->pFrame )
+ return;
+
+ SfxViewFrame* pTop = xImp->pFrame->GetTopViewFrame();
+ if ( !pTop || pTop->GetBindings().GetDispatcher() != this )
+ return;
+
+ SfxFrame& rFrame = pTop->GetFrame();
+ if ( !rFrame.IsMenuBarOn_Impl() )
+ return;
+
+ css::uno::Reference < css::beans::XPropertySet > xPropSet( rFrame.GetFrameInterface(), css::uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ if ( xLayoutManager.is() )
+ {
+ OUString aMenuBarURL( "private:resource/menubar/menubar" );
+ if ( !xLayoutManager->isElementVisible( aMenuBarURL ) )
+ xLayoutManager->createElement( aMenuBarURL );
+ }
+ }
+#endif
+}
+
+void SfxDispatcher::Update_Impl( bool bForce )
+{
+ SFX_STACK(SfxDispatcher::Update_Impl);
+
+ Flush();
+
+ if ( !xImp->pFrame )
+ return;
+
+ bool bUpdate = bForce;
+ if ( xImp->pFrame )
+ {
+ SfxWorkWindow *pWork = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
+ SfxDispatcher *pAct = pWork->GetBindings().GetDispatcher_Impl();
+ if (pAct == this)
+ {
+ if ( !bUpdate )
+ bUpdate = !xImp->bUpdated;
+ xImp->bUpdated = true;
+ }
+ }
+
+ if ( !bUpdate || xImp->pFrame->GetFrame().IsClosing_Impl() )
+ return;
+
+ SfxViewFrame* pTop = xImp->pFrame ? xImp->pFrame->GetTopViewFrame() : nullptr;
+ bool bUIActive = pTop && pTop->GetBindings().GetDispatcher() == this;
+
+ if ( !bUIActive && pTop && GetBindings() == &pTop->GetBindings() )
+ // keep own tools internally for collecting
+ GetBindings()->GetDispatcher()->xImp->bUpdated = false;
+
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ SfxBindings* pBindings = GetBindings();
+ if (pBindings)
+ {
+ pBindings->DENTERREGISTRATIONS();
+ xFrame = pBindings->GetActiveFrame();
+ }
+ css::uno::Reference< css::beans::XPropertySet > xPropSet( xFrame, css::uno::UNO_QUERY );
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+ }
+
+ if ( xLayoutManager.is() )
+ xLayoutManager->lock();
+
+ bool bIsIPActive = xImp->pFrame && xImp->pFrame->GetObjectShell()->IsInPlaceActive();
+ SfxInPlaceClient *pClient = xImp->pFrame ? xImp->pFrame->GetViewShell()->GetUIActiveClient() : nullptr;
+ if ( bUIActive && /* !bIsIPActive && */ ( !pClient || !pClient->IsObjectUIActive() ) )
+ SetMenu_Impl();
+
+ SfxWorkWindow *pWorkWin = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
+ pWorkWin->ResetStatusBar_Impl();
+
+ {
+ SfxWorkWindow *pWork = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
+ SfxDispatcher *pAct = pWork->GetBindings().GetDispatcher_Impl();
+ if (pAct == this)
+ {
+ pWork->ResetObjectBars_Impl();
+ pWork->ResetChildWindows_Impl();
+ }
+ }
+
+ bool bIsActive = false;
+ SfxDispatcher *pActDispat = pWorkWin->GetBindings().GetDispatcher_Impl();
+ if ( !bIsActive && this == pActDispat )
+ bIsActive = true;
+
+ Update_Impl_( bUIActive, !bIsIPActive, bIsIPActive, pWorkWin );
+ if (bUIActive || bIsActive)
+ pWorkWin->UpdateObjectBars_Impl();
+
+ if ( pBindings )
+ pBindings->DLEAVEREGISTRATIONS();
+
+ if ( xLayoutManager.is() )
+ xLayoutManager->unlock();
+
+ if ( SfxViewShell::Current() && SfxViewShell::Current()->GetDispatcher() )
+ {
+ SfxPoolItemHolder aItem;
+ SfxViewShell::Current()->GetDispatcher()->QueryState(SID_NOTEBOOKBAR, aItem);
+ }
+}
+
+void SfxDispatcher::Update_Impl_( bool bUIActive, bool bIsMDIApp, bool bIsIPOwner, SfxWorkWindow *pTaskWin )
+{
+ SfxWorkWindow *pWorkWin = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
+ bool bIsActive = false;
+ SfxDispatcher *pActDispat = pWorkWin->GetBindings().GetDispatcher_Impl();
+ if ( pActDispat && !bIsActive )
+ {
+ if ( this == pActDispat )
+ bIsActive = true;
+ }
+
+ for (SfxObjectBars_Impl & rObjBar : xImp->aObjBars)
+ rObjBar.eId = ToolbarId::None;
+ xImp->aChildWins.clear();
+
+ // bQuiet: own shells aren't considered for UI and SlotServer
+ // bNoUI: own Shells aren't considered forms UI
+ if ( xImp->bQuiet || xImp->bNoUI || (xImp->pFrame && xImp->pFrame->GetObjectShell()->IsPreview()) )
+ return;
+
+ StatusBarId eStatBarId = StatusBarId::None;
+
+ SfxSlotPool* pSlotPool = &SfxSlotPool::GetSlotPool( GetFrame() );
+ sal_uInt16 nTotCount = xImp->aStack.size();
+ for ( sal_uInt16 nShell = nTotCount; nShell > 0; --nShell )
+ {
+ SfxShell *pShell = GetShell( nShell-1 );
+ if (!pShell)
+ continue;
+
+ SfxInterface *pIFace = pShell->GetInterface();
+
+ // don't consider shells if "Hidden" or "Quiet"
+ bool bReadOnlyShell = IsReadOnlyShell_Impl( nShell-1 );
+ sal_uInt16 nNo;
+ for ( nNo = 0; pIFace && nNo<pIFace->GetObjectBarCount(); ++nNo )
+ {
+ sal_uInt16 nPos = pIFace->GetObjectBarPos(nNo);
+ SfxVisibilityFlags nFlags = pIFace->GetObjectBarFlags(nNo);
+ if ( bReadOnlyShell && !( nFlags & SfxVisibilityFlags::ReadonlyDoc ) )
+ continue;
+
+ // check whether toolbar needs activation of a special feature
+ SfxShellFeature nFeature = pIFace->GetObjectBarFeature(nNo);
+ if ((nFeature != SfxShellFeature::NONE) && !pShell->HasUIFeature(nFeature))
+ continue;
+
+ // check for toolboxes that are exclusively for a viewer
+ if ( xImp->pFrame)
+ {
+ bool bViewerTbx( nFlags & SfxVisibilityFlags::Viewer );
+ SfxObjectShell* pSh = xImp->pFrame->GetObjectShell();
+ const SfxBoolItem* pItem = pSh->GetMedium()->GetItemSet().GetItem(SID_VIEWONLY, false);
+ bool bIsViewer = pItem && pItem->GetValue();
+ if ( bIsViewer != bViewerTbx )
+ continue;
+ }
+
+ // always register toolbars, allows to switch them on
+ bool bVisible = pIFace->IsObjectBarVisible(nNo);
+ if ( !bVisible )
+ nFlags = SfxVisibilityFlags::Invisible;
+
+ SfxObjectBars_Impl& rBar = xImp->aObjBars[nPos];
+ rBar.nPos = nPos;
+ rBar.nFlags = nFlags;
+ rBar.eId = pIFace->GetObjectBarId(nNo);
+
+ if ( bUIActive || bIsActive )
+ {
+ pWorkWin->SetObjectBar_Impl(nPos, nFlags, rBar.eId);
+ }
+
+ if ( !bVisible )
+ rBar.eId = ToolbarId::None;
+ }
+
+ for ( nNo=0; pIFace && nNo<pIFace->GetChildWindowCount(); nNo++ )
+ {
+ sal_uInt32 nId = pIFace->GetChildWindowId(nNo);
+ const SfxSlot *pSlot = pSlotPool->GetSlot( static_cast<sal_uInt16>(nId) );
+ SAL_INFO_IF( !pSlot, "sfx.control", "Childwindow slot missing: " << nId );
+ if ( bReadOnlyShell )
+ {
+ // only show ChildWindows if their slot is allowed for readonly documents
+ if ( pSlot && !pSlot->IsMode( SfxSlotMode::READONLYDOC ) )
+ continue;
+ }
+
+ SfxShellFeature nFeature = pIFace->GetChildWindowFeature(nNo);
+ if ((nFeature != SfxShellFeature::NONE) && !pShell->HasUIFeature(nFeature))
+ continue;
+
+ // slot decides whether a ChildWindow is shown when document is OLE server or OLE client
+ SfxVisibilityFlags nMode = SfxVisibilityFlags::Standard;
+ if( pSlot )
+ {
+ if ( pSlot->IsMode(SfxSlotMode::CONTAINER) )
+ {
+ if ( pWorkWin->IsVisible_Impl( SfxVisibilityFlags::Client ) )
+ nMode |= SfxVisibilityFlags::Client;
+ }
+ else
+ {
+ if ( pWorkWin->IsVisible_Impl( SfxVisibilityFlags::Server ) )
+ nMode |= SfxVisibilityFlags::Server;
+ }
+ }
+
+ if ( bUIActive || bIsActive )
+ pWorkWin->SetChildWindowVisible_Impl( nId, true, nMode );
+ if ( bUIActive || bIsActive || !pWorkWin->IsFloating( static_cast<sal_uInt16>( nId & 0xFFFF ) ) )
+ xImp->aChildWins.push_back( nId );
+ }
+
+ if ( bIsMDIApp || bIsIPOwner )
+ {
+ StatusBarId eId = pIFace ? pIFace->GetStatusBarId() : StatusBarId::None;
+ if (eId != StatusBarId::None)
+ eStatBarId = eId;
+ }
+ }
+
+ for ( sal_uInt16 nPos=0; nPos<SFX_OBJECTBAR_MAX; nPos++ )
+ {
+ SfxObjectBars_Impl& rFixed = xImp->aFixedObjBars[nPos];
+ if (rFixed.eId != ToolbarId::None)
+ {
+ SfxObjectBars_Impl& rBar = xImp->aObjBars[nPos];
+ rBar = rFixed;
+ pWorkWin->SetObjectBar_Impl(rFixed.nPos, rFixed.nFlags,
+ rFixed.eId);
+ }
+ }
+
+ if ( !pTaskWin || ( !bIsMDIApp && !bIsIPOwner ) )
+ return;
+
+ bool bIsTaskActive = false;
+
+ SfxDispatcher *pActDispatcher = pTaskWin->GetBindings().GetDispatcher_Impl();
+ if ( pActDispatcher && !bIsTaskActive )
+ {
+ if ( this == pActDispatcher )
+ bIsTaskActive = true;
+ }
+
+ if (bIsTaskActive && eStatBarId != StatusBarId::None && xImp->pFrame)
+ {
+ // internal frames also may control statusbar
+ xImp->pFrame->GetFrame().GetWorkWindow_Impl()->SetStatusBar_Impl(eStatBarId);
+ }
+}
+
+/** Helper method to execute the outstanding push and pop commands.
+*/
+void SfxDispatcher::FlushImpl()
+{
+ SFX_STACK(SfxDispatcher::FlushImpl);
+
+ SAL_INFO("sfx.control", "Flushing dispatcher!");
+
+ xImp->aIdle.Stop();
+
+ xImp->bFlushing = !xImp->bFlushing;
+ if ( !xImp->bFlushing )
+ {
+ xImp->bFlushing = true;
+ return;
+ }
+
+ SfxApplication *pSfxApp = SfxGetpApp();
+
+ // Re-build the true stack in the first round
+ std::deque<SfxToDo_Impl> aToDoCopy;
+ bool bModify = false;
+ for(std::deque<SfxToDo_Impl>::reverse_iterator i = xImp->aToDoStack.rbegin(); i != xImp->aToDoStack.rend(); ++i)
+ {
+ bModify = true;
+
+ if(i->bPush)
+ {
+ // Actually push
+ DBG_ASSERT( std::find(xImp->aStack.begin(), xImp->aStack.end(), i->pCluster) == xImp->aStack.end(),
+ "pushed SfxShell already on stack" );
+ xImp->aStack.push_back(i->pCluster);
+ i->pCluster->SetDisableFlags(xImp->nDisableFlags);
+
+ // Mark the moved shell
+ aToDoCopy.push_front(*i);
+ }
+ else
+ {
+ // Actually pop
+ bool bFound = false;
+ if (!i->bUntil)
+ {
+ // pop exactly the requested shell
+ if (auto it = std::find(xImp->aStack.begin(), xImp->aStack.end(), i->pCluster);
+ it != xImp->aStack.end())
+ {
+ xImp->aStack.erase(it);
+ i->pCluster->SetDisableFlags(SfxDisableFlags::NONE);
+ bFound = true;
+
+ // Mark the moved Shell
+ aToDoCopy.push_front(SfxToDo_Impl(false, i->bDelete, false, *i->pCluster));
+ }
+ }
+ while (!bFound)
+ {
+ DBG_ASSERT( !xImp->aStack.empty(), "popping from empty stack" );
+ SfxShell* pPopped = xImp->aStack.back();
+ xImp->aStack.pop_back();
+ pPopped->SetDisableFlags( SfxDisableFlags::NONE );
+ bFound = (pPopped == i->pCluster);
+
+ // Mark the moved Shell
+ aToDoCopy.push_front(SfxToDo_Impl(false, i->bDelete, false, *pPopped));
+ if (!i->bUntil)
+ {
+ // We get here only when the requested shell was not on the stack.
+ // I don't know how correct to pop a single random other shell and exit
+ // in this case, but I just make sure that the previous logic is kept.
+ break;
+ }
+ }
+ DBG_ASSERT( bFound, "wrong SfxShell popped" );
+ }
+ }
+ xImp->aToDoStack.clear();
+
+ // Invalidate bindings, if possible
+ if ( !pSfxApp->IsDowning() )
+ {
+ InvalidateBindings_Impl( bModify );
+ }
+
+ xImp->bFlushing = false;
+ xImp->bUpdated = false; // not only when bModify, if Doc/Template-Config
+ xImp->bFlushed = true;
+ SAL_INFO("sfx.control", "Successfully flushed dispatcher!");
+
+ //fdo#70703 FlushImpl may call back into itself so use aToDoCopyStack to talk
+ //to outer levels of ourself. If DoActivate_Impl/DoDeactivate_Impl deletes
+ //an entry, then they will walk back up aToDoCopyStack and set outer
+ //levels's entries to bDeleted
+ xImp->aToDoCopyStack.push_back(aToDoCopy);
+ std::deque<SfxToDo_Impl>& rToDoCopy = xImp->aToDoCopyStack.back();
+ // Activate the Shells and possible delete them in the 2nd round
+ for(std::deque<SfxToDo_Impl>::reverse_iterator i = rToDoCopy.rbegin(); i != rToDoCopy.rend(); ++i)
+ {
+ if (i->bDeleted)
+ continue;
+ if (!xImp->bActive)
+ continue;
+ if (i->bPush)
+ i->pCluster->DoActivate_Impl(xImp->pFrame, true);
+ else
+ i->pCluster->DoDeactivate_Impl(xImp->pFrame, true);
+ }
+
+ aToDoCopy = xImp->aToDoCopyStack.back();
+ xImp->aToDoCopyStack.pop_back();
+
+ for(std::deque<SfxToDo_Impl>::reverse_iterator i = aToDoCopy.rbegin(); i != aToDoCopy.rend(); ++i)
+ {
+ if (i->bDelete && !i->bDeleted)
+ {
+ if (!xImp->aToDoCopyStack.empty())
+ {
+ //fdo#70703 if there is an outer FlushImpl then inform it that
+ //we have deleted this cluster
+ for (auto & elem : xImp->aToDoCopyStack)
+ {
+ for (auto & subelem : elem)
+ {
+ if (subelem.pCluster == i->pCluster)
+ subelem.bDeleted = true;
+ }
+ }
+ }
+ delete i->pCluster;
+ }
+ }
+ bool bAwakeBindings = !aToDoCopy.empty();
+ if( bAwakeBindings )
+ aToDoCopy.clear();
+
+ // If more changes have occurred on the stack when
+ // Activate/Deactivate/Delete:
+ if (!xImp->bFlushed)
+ // If Push/Pop has been called by someone, then also EnterReg was called!
+ FlushImpl();
+
+ if( bAwakeBindings && GetBindings() )
+ GetBindings()->DLEAVEREGISTRATIONS();
+
+ for (SfxObjectBars_Impl & rFixedObjBar : xImp->aFixedObjBars)
+ rFixedObjBar.eId = ToolbarId::None;
+
+ SAL_INFO("sfx.control", "SfxDispatcher(" << this << ")::Flush() done");
+}
+
+/** With this method a filter set, the target slots can be enabled or disabled.
+ The passed array must be retained until the destructor or the next
+ <SetSlotFilter()>, it is not deleted from the dispatcher, so it can thus be
+ static.
+
+ In read-only documents the quasi ReadOnlyDoc Flag of slots can be
+ overturned by the use of 'bEnable == 2', so this will be displayed again.
+ On the other slots it has no effect.
+
+ // HACK(here should be used an enum) ???
+ @param nEnable 1==true: only enable specified slots, disable all other
+ 0==false: disable specified slots, first enable all other
+ @param nCount Number of SIDs in the following Array
+ @param pSIDs sorted Array of 'nCount' SIDs
+
+ [Example]
+
+ Targeted disabling of Slots 1, 2 and 3:
+
+ static sal_uInt16 const pSIDs[] = { 1, 2, 3 };
+ pDisp->SetSlotFilter( sal_False, sizeof(pSIDs)/sizeof(sal_uInt16), pSIDs );
+
+ only permit Slots 5, 6 and 7:
+
+ static sal_uInt16 const pSIDs[] = { 5, 6, 7 };
+ pDisp->SetSlotFilter( sal_True, sizeof(pSIDs)/sizeof(sal_uInt16), pSIDs );
+
+ Turn-off Filter:
+
+ pDisp->SetSlotFilter();
+*/
+void SfxDispatcher::SetSlotFilter(SfxSlotFilterState nEnable,
+ std::span<sal_uInt16 const> pSIDs)
+{
+#ifdef DBG_UTIL
+ // Check Array
+ for ( std::size_t n = 1; n < pSIDs.size(); ++n )
+ DBG_ASSERT( pSIDs[n] > pSIDs[n-1], "SetSlotFilter: SIDs not sorted" );
+#endif
+
+ xImp->nFilterEnabling = nEnable;
+ xImp->pFilterSIDs = pSIDs;
+
+ GetBindings()->InvalidateAll(true);
+}
+
+extern "C" {
+
+static int SfxCompareSIDs_Impl(const void* pSmaller, const void* pBigger)
+{
+ return static_cast<tools::Long>(*static_cast<sal_uInt16 const *>(pSmaller)) - static_cast<tools::Long>(*static_cast<sal_uInt16 const *>(pBigger));
+}
+
+}
+
+/** Searches for 'nSID' in the Filter set by <SetSlotFilter()> and
+ returns sal_True, if the SIDis allowed, or sal_False, if it is
+ disabled by the Filter.
+
+ @return 0 => disabled
+ 1 => enabled
+ 2 => enabled even if ReadOnlyDoc
+*/
+SfxSlotFilterState SfxDispatcher::IsSlotEnabledByFilter_Impl( sal_uInt16 nSID ) const
+{
+ // no filter?
+ if ( xImp->pFilterSIDs.empty() )
+ // => all SIDs allowed
+ return SfxSlotFilterState::ENABLED;
+
+ // search
+ bool bFound = nullptr != bsearch( &nSID, xImp->pFilterSIDs.data(), xImp->pFilterSIDs.size(),
+ sizeof(sal_uInt16), SfxCompareSIDs_Impl );
+
+ // even if ReadOnlyDoc
+ if ( SfxSlotFilterState::ENABLED_READONLY == xImp->nFilterEnabling )
+ return bFound ? SfxSlotFilterState::ENABLED_READONLY : SfxSlotFilterState::ENABLED;
+ // Otherwise after Negative/Positive Filter
+ else if ( SfxSlotFilterState::ENABLED == xImp->nFilterEnabling )
+ return bFound ? SfxSlotFilterState::ENABLED : SfxSlotFilterState::DISABLED;
+ else
+ return bFound ? SfxSlotFilterState::DISABLED : SfxSlotFilterState::ENABLED;
+}
+
+/** This helper method searches for the <Slot-Server> which currently serves
+ the nSlot. As the result, rServe is filled accordingly.
+
+ If known the SfxInterface which is currently served by nSlot can be
+ passed along.
+
+ The SfxDispatcher is flushed while searching for nSlot.
+
+ @param nSlot Slot-Id to search for
+ @param rServer <SfxSlotServer>-Instance to fill
+
+ @return true
+ The Slot was found, rServer is valid.
+
+ false
+ The Slot is currently not served, rServer is invalid.
+*/
+bool SfxDispatcher::FindServer_(sal_uInt16 nSlot, SfxSlotServer& rServer)
+{
+ SFX_STACK(SfxDispatcher::FindServer_);
+
+ // Dispatcher locked? (nevertheless let SID_HELP_PI through)
+ if ( IsLocked() )
+ {
+ xImp->bInvalidateOnUnlock = true;
+ return false;
+ }
+
+ // Count the number of Shells in the linked dispatchers.
+ Flush();
+ sal_uInt16 nTotCount = xImp->aStack.size();
+
+ // Verb-Slot?
+ if (nSlot >= SID_VERB_START && nSlot <= SID_VERB_END)
+ {
+ for ( sal_uInt16 nShell = 0;; ++nShell )
+ {
+ SfxShell *pSh = GetShell(nShell);
+ if ( pSh == nullptr )
+ return false;
+ if ( dynamic_cast< const SfxViewShell *>( pSh ) != nullptr )
+ {
+ const SfxSlot* pSlot = pSh->GetVerbSlot_Impl(nSlot);
+ if ( pSlot )
+ {
+ rServer.SetShellLevel(nShell);
+ rServer.SetSlot( pSlot );
+ return true;
+ }
+ }
+ }
+ }
+
+ // SID check against set filter
+ SfxSlotFilterState nSlotEnableMode = SfxSlotFilterState::DISABLED;
+ if ( xImp->pFrame )
+ {
+ nSlotEnableMode = IsSlotEnabledByFilter_Impl( nSlot );
+ if ( SfxSlotFilterState::DISABLED == nSlotEnableMode )
+ return false;
+ }
+
+ // In Quiet-Mode only Parent-Dispatcher
+ if ( xImp->bQuiet )
+ {
+ return false;
+ }
+
+ bool bReadOnly = ( SfxSlotFilterState::ENABLED_READONLY != nSlotEnableMode && xImp->bReadOnly );
+
+ // search through all the shells of the chained dispatchers
+ // from top to bottom
+ sal_uInt16 nFirstShell = 0;
+ for ( sal_uInt16 i = nFirstShell; i < nTotCount; ++i )
+ {
+ SfxShell *pObjShell = GetShell(i);
+ if (!pObjShell)
+ continue;
+
+ SfxInterface *pIFace = pObjShell->GetInterface();
+ const SfxSlot *pSlot = pIFace->GetSlot(nSlot);
+
+ if ( pSlot && pSlot->nDisableFlags != SfxDisableFlags::NONE &&
+ ( static_cast<int>(pSlot->nDisableFlags) & static_cast<int>(pObjShell->GetDisableFlags()) ) != 0 )
+ return false;
+
+ if ( pSlot && !( pSlot->nFlags & SfxSlotMode::READONLYDOC ) && bReadOnly )
+ return false;
+
+ if ( pSlot )
+ {
+ // Slot belongs to Container?
+ bool bIsContainerSlot = pSlot->IsMode(SfxSlotMode::CONTAINER);
+ bool bIsInPlace = xImp->pFrame && xImp->pFrame->GetObjectShell()->IsInPlaceActive();
+
+ // Shell belongs to Server?
+ // AppDispatcher or IPFrame-Dispatcher
+ bool bIsServerShell = !xImp->pFrame || bIsInPlace;
+
+ // Of course ShellServer-Slots are also executable even when it is
+ // executed on a container dispatcher without an IPClient.
+ if ( !bIsServerShell )
+ {
+ SfxViewShell *pViewSh = xImp->pFrame->GetViewShell();
+ bIsServerShell = !pViewSh || !pViewSh->GetUIActiveClient();
+ }
+
+ // Shell belongs to Container?
+ // AppDispatcher or no IPFrameDispatcher
+ bool bIsContainerShell = !xImp->pFrame || !bIsInPlace;
+ // Shell and Slot match
+ if ( !( ( bIsContainerSlot && bIsContainerShell ) ||
+ ( !bIsContainerSlot && bIsServerShell ) ) )
+ pSlot = nullptr;
+ }
+
+ if ( pSlot )
+ {
+ rServer.SetSlot(pSlot);
+ rServer.SetShellLevel(i);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/** Helper method to obtain the status of the <Slot-Server>s rSvr.
+ The required slots IDs (partly converted to Which-IDs of the pool)
+ must be present in rstate.
+
+ The SfxDispatcher is flushed before the query.
+
+ @param rSvr Slot-Server to query
+ @param rState SfxItemSet to be filled
+ @param pRealSlot The actual Slot if possible
+*/
+bool SfxDispatcher::FillState_(const SfxSlotServer& rSvr, SfxItemSet& rState,
+ const SfxSlot* pRealSlot)
+{
+ SFX_STACK(SfxDispatcher::FillState_);
+
+ const SfxSlot *pSlot = rSvr.GetSlot();
+ if ( pSlot && IsLocked() )
+ {
+ xImp->bInvalidateOnUnlock = true;
+ return false;
+ }
+
+ if ( pSlot )
+ {
+ DBG_ASSERT(xImp->bFlushed,
+ "Dispatcher not flushed after retrieving slot servers!");
+ if (!xImp->bFlushed)
+ return false;
+
+ // Determine the object and call the Message of this object
+ SfxShell *pSh = GetShell(rSvr.GetShellLevel());
+ if (!pSh)
+ return false;
+
+ SfxStateFunc pFunc;
+
+ if (pRealSlot)
+ pFunc = pRealSlot->GetStateFnc();
+ else
+ pFunc = pSlot->GetStateFnc();
+
+ (*pFunc)(pSh, rState);
+#ifdef DBG_UTIL
+ // To examine the conformity of IDL (SlotMap) and current Items
+ if ( rState.Count() )
+ {
+ SfxInterface *pIF = pSh->GetInterface();
+ SfxItemIter aIter( rState );
+ for ( const SfxPoolItem *pItem = aIter.GetCurItem();
+ pItem;
+ pItem = aIter.NextItem() )
+ {
+ if ( !IsInvalidItem(pItem) && !pItem->isVoidItem() )
+ {
+ sal_uInt16 nSlotId = rState.GetPool()->GetSlotId(pItem->Which());
+ SAL_INFO_IF(
+ typeid(pItem) != *pIF->GetSlot(nSlotId)->GetType()->Type(),
+ "sfx.control",
+ "item-type unequal to IDL (=> no BASIC) with SID: "
+ << nSlotId << " in " << pIF->GetClassName());
+ }
+ }
+ }
+#endif
+
+ return true;
+ }
+
+ return false;
+}
+
+void SfxDispatcher::ExecutePopup( vcl::Window *pWin, const Point *pPos )
+{
+ SfxDispatcher &rDisp = *SfxGetpApp()->GetDispatcher_Impl();
+ sal_uInt16 nShLevel = 0;
+ SfxShell *pSh;
+
+ if ( rDisp.xImp->bQuiet )
+ nShLevel = rDisp.xImp->aStack.size();
+
+ for ( pSh = rDisp.GetShell(nShLevel); pSh; ++nShLevel, pSh = rDisp.GetShell(nShLevel) )
+ {
+ const OUString& rResName = pSh->GetInterface()->GetPopupMenuName();
+ if ( !rResName.isEmpty() )
+ {
+ rDisp.ExecutePopup( rResName, pWin, pPos );
+ return;
+ }
+ }
+}
+
+namespace {
+
+boost::property_tree::ptree fillPopupMenu(Menu* pMenu)
+{
+ // Activate this menu first
+ pMenu->HandleMenuActivateEvent(pMenu);
+ pMenu->HandleMenuDeActivateEvent(pMenu);
+
+ boost::property_tree::ptree aTree;
+ // If last item inserted is some valid text
+ bool bIsLastItemText = false;
+ sal_uInt16 nCount = pMenu->GetItemCount();
+ for (sal_uInt16 nPos = 0; nPos < nCount; nPos++)
+ {
+ boost::property_tree::ptree aItemTree;
+ const MenuItemType aItemType = pMenu->GetItemType(nPos);
+
+ if (aItemType == MenuItemType::DONTKNOW)
+ continue;
+
+ if (aItemType == MenuItemType::SEPARATOR)
+ {
+ if (bIsLastItemText)
+ aItemTree.put("type", "separator");
+ bIsLastItemText = false;
+ }
+ else
+ {
+ const sal_uInt16 nItemId = pMenu->GetItemId(nPos);
+ OUString aCommandURL = pMenu->GetItemCommand(nItemId);
+
+ if (aCommandURL.isEmpty())
+ {
+ const SfxSlot *pSlot = SFX_SLOTPOOL().GetSlot(nItemId);
+ if (pSlot)
+ aCommandURL = pSlot->GetCommand();
+ }
+
+ const OUString aItemText = pMenu->GetItemText(nItemId);
+ Menu* pPopupSubmenu = pMenu->GetPopupMenu(nItemId);
+
+ if (!aItemText.isEmpty())
+ aItemTree.put("text", aItemText.toUtf8().getStr());
+
+ if (pPopupSubmenu)
+ {
+ boost::property_tree::ptree aSubmenu = ::fillPopupMenu(pPopupSubmenu);
+ if (aSubmenu.empty())
+ continue;
+
+ aItemTree.put("type", "menu");
+ if (!aCommandURL.isEmpty())
+ aItemTree.put("command", aCommandURL.toUtf8().getStr());
+ aItemTree.push_back(std::make_pair("menu", aSubmenu));
+ }
+ else
+ {
+ // no point in exposing choices that don't have the .uno:
+ // command
+ if (aCommandURL.isEmpty())
+ continue;
+
+ aItemTree.put("type", "command");
+ aItemTree.put("command", aCommandURL.toUtf8().getStr());
+ }
+
+ aItemTree.put("enabled", pMenu->IsItemEnabled(nItemId));
+
+ MenuItemBits aItemBits = pMenu->GetItemBits(nItemId);
+ bool bHasChecks = true;
+ if (aItemBits & MenuItemBits::CHECKABLE)
+ aItemTree.put("checktype", "checkmark");
+ else if (aItemBits & MenuItemBits::RADIOCHECK)
+ aItemTree.put("checktype", "radio");
+ else if (aItemBits & MenuItemBits::AUTOCHECK)
+ aItemTree.put("checktype", "auto");
+ else
+ bHasChecks = false;
+
+ if (bHasChecks)
+ aItemTree.put("checked", pMenu->IsItemChecked(nItemId));
+ }
+
+ if (!aItemTree.empty())
+ {
+ aTree.push_back(std::make_pair("", aItemTree));
+ if (aItemType != MenuItemType::SEPARATOR)
+ bIsLastItemText = true;
+ }
+ }
+
+ return aTree;
+}
+
+}
+
+boost::property_tree::ptree SfxDispatcher::fillPopupMenu(const rtl::Reference<VCLXPopupMenu>& rPopupMenu)
+{
+ PopupMenu* pVCLMenu = static_cast<PopupMenu*>(rPopupMenu->GetMenu());
+ return ::fillPopupMenu(pVCLMenu);
+}
+
+void SfxDispatcher::ExecutePopup( const OUString& rResName, vcl::Window* pWin, const Point* pPos )
+{
+ css::uno::Sequence< css::uno::Any > aArgs{
+ css::uno::Any(comphelper::makePropertyValue( "Value", rResName )),
+ css::uno::Any(comphelper::makePropertyValue( "Frame", GetFrame()->GetFrame().GetFrameInterface() )),
+ css::uno::Any(comphelper::makePropertyValue( "IsContextMenu", true ))
+ };
+
+ css::uno::Reference< css::uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+ css::uno::Reference< css::frame::XPopupMenuController > xPopupController(
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.framework.ResourceMenuController", aArgs, xContext ), css::uno::UNO_QUERY );
+
+ rtl::Reference< VCLXPopupMenu > xPopupMenu = new VCLXPopupMenu();
+
+ if ( !xPopupController.is() || !xPopupMenu.is() )
+ return;
+
+ vcl::Window* pWindow = pWin ? pWin : xImp->pFrame->GetFrame().GetWorkWindow_Impl()->GetWindow();
+ Point aPos = pPos ? *pPos : pWindow->GetPointerPosPixel();
+
+ css::ui::ContextMenuExecuteEvent aEvent;
+ aEvent.SourceWindow = VCLUnoHelper::GetInterface( pWindow );
+ aEvent.ExecutePosition.X = aPos.X();
+ aEvent.ExecutePosition.Y = aPos.Y();
+
+ xPopupController->setPopupMenu( xPopupMenu );
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ boost::property_tree::ptree aMenu = fillPopupMenu(xPopupMenu);
+ boost::property_tree::ptree aRoot;
+ aRoot.add_child("menu", aMenu);
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aRoot, true);
+ if (SfxViewShell* pViewShell = xImp->pFrame->GetViewShell())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_MENU, OString(aStream.str()));
+ }
+ else
+ {
+ OUString aMenuURL = "private:resource/popupmenu/" + rResName;
+ if (GetFrame()->GetViewShell()->TryContextMenuInterception(xPopupMenu, aMenuURL, aEvent))
+ {
+ css::uno::Reference<css::awt::XWindowPeer> xParent(aEvent.SourceWindow, css::uno::UNO_QUERY);
+ xPopupMenu->execute(xParent, css::awt::Rectangle(aPos.X(), aPos.Y(), 1, 1), css::awt::PopupMenuDirection::EXECUTE_DOWN);
+ }
+ }
+
+ css::uno::Reference< css::lang::XComponent > xComponent( xPopupController, css::uno::UNO_QUERY );
+ if ( xComponent.is() )
+ xComponent->dispose();
+}
+
+/** With this method the SfxDispatcher can be locked and released. A locked
+ SfxDispatcher does not perform <SfxRequest>s and does no longer provide
+ status information. It behaves as if all the slots were disabled.
+*/
+void SfxDispatcher::Lock( bool bLock )
+{
+ SfxBindings* pBindings = GetBindings();
+ if ( !bLock && xImp->bLocked && xImp->bInvalidateOnUnlock )
+ {
+ if ( pBindings )
+ pBindings->InvalidateAll(true);
+ xImp->bInvalidateOnUnlock = false;
+ }
+ else if ( pBindings )
+ pBindings->InvalidateAll(false);
+ xImp->bLocked = bLock;
+ if ( !bLock )
+ {
+ for(size_t i = 0; i < xImp->aReqArr.size(); ++i)
+ xImp->xPoster->Post(std::move(xImp->aReqArr[i]));
+ xImp->aReqArr.clear();
+ }
+}
+
+ToolbarId SfxDispatcher::GetObjectBarId( sal_uInt16 nPos ) const
+{
+ return xImp->aObjBars[nPos].eId;
+}
+
+void SfxDispatcher::HideUI( bool bHide )
+{
+ bool bWasHidden = xImp->bNoUI;
+ xImp->bNoUI = bHide;
+ if ( xImp->pFrame )
+ {
+ SfxViewFrame* pTop = xImp->pFrame->GetTopViewFrame();
+ if ( pTop && pTop->GetBindings().GetDispatcher() == this )
+ {
+ SfxFrame& rFrame = pTop->GetFrame();
+ if ( rFrame.IsMenuBarOn_Impl() )
+ {
+ css::uno::Reference < css::beans::XPropertySet > xPropSet( rFrame.GetFrameInterface(), css::uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ if ( xLayoutManager.is() )
+ xLayoutManager->setVisible( !bHide );
+ }
+ }
+ }
+ }
+
+ if ( bHide != bWasHidden )
+ Update_Impl( true );
+}
+
+void SfxDispatcher::SetReadOnly_Impl( bool bOn )
+{
+ xImp->bReadOnly = bOn;
+}
+
+bool SfxDispatcher::GetReadOnly_Impl() const
+{
+ return xImp->bReadOnly;
+}
+
+/** With 'bOn' the Dispatcher is quasi dead and transfers everything to the
+ Parent-Dispatcher.
+*/
+void SfxDispatcher::SetQuietMode_Impl( bool bOn )
+{
+ xImp->bQuiet = bOn;
+ SfxBindings* pBindings = GetBindings();
+ if ( pBindings )
+ pBindings->InvalidateAll(true);
+}
+
+SfxItemState SfxDispatcher::QueryState( sal_uInt16 nSlot, SfxPoolItemHolder& rState )
+{
+ SfxShell *pShell = nullptr;
+ const SfxSlot *pSlot = nullptr;
+ if ( GetShellAndSlot_Impl( nSlot, &pShell, &pSlot, false, true ) )
+ {
+ rState = pShell->GetSlotState(nSlot);
+ if ( nullptr == rState.getItem() )
+ return SfxItemState::DISABLED;
+ else
+ return SfxItemState::DEFAULT;
+ }
+
+ return SfxItemState::DISABLED;
+}
+
+SfxItemState SfxDispatcher::QueryState( sal_uInt16 nSID, css::uno::Any& rAny )
+{
+ SfxShell *pShell = nullptr;
+ const SfxSlot *pSlot = nullptr;
+ if ( GetShellAndSlot_Impl( nSID, &pShell, &pSlot, false, true ) )
+ {
+ SfxPoolItemHolder aItem(pShell->GetSlotState(nSID));
+ if (nullptr == aItem.getItem())
+ return SfxItemState::DISABLED;
+ else
+ {
+ css::uno::Any aState;
+ if ( !aItem.getItem()->isVoidItem() )
+ {
+ sal_uInt16 nSubId( 0 );
+ SfxItemPool& rPool = pShell->GetPool();
+ sal_uInt16 nWhich = rPool.GetWhich( nSID );
+ if ( rPool.GetMetric( nWhich ) == MapUnit::MapTwip )
+ nSubId |= CONVERT_TWIPS;
+ aItem.getItem()->QueryValue( aState, static_cast<sal_uInt8>(nSubId) );
+ }
+ rAny = aState;
+
+ return SfxItemState::DEFAULT;
+ }
+ }
+
+ return SfxItemState::DISABLED;
+}
+
+bool SfxDispatcher::IsReadOnlyShell_Impl( sal_uInt16 nShell ) const
+{
+ sal_uInt16 nShellCount = xImp->aStack.size();
+ if ( nShell < nShellCount )
+ {
+ SfxShell* pShell = *( xImp->aStack.rbegin() + nShell );
+ if( dynamic_cast< const SfxModule *>( pShell ) != nullptr || dynamic_cast< const SfxApplication *>( pShell ) != nullptr || dynamic_cast< const SfxViewFrame *>( pShell ) != nullptr )
+ return false;
+ else
+ return xImp->bReadOnly;
+ }
+ return true;
+}
+
+void SfxDispatcher::RemoveShell_Impl( SfxShell& rShell )
+{
+ Flush();
+
+ sal_uInt16 nCount = xImp->aStack.size();
+ for ( sal_uInt16 n=0; n<nCount; ++n )
+ {
+ if ( xImp->aStack[n] == &rShell )
+ {
+ xImp->aStack.erase( xImp->aStack.begin() + n );
+ rShell.SetDisableFlags( SfxDisableFlags::NONE );
+ rShell.DoDeactivate_Impl(xImp->pFrame, true);
+ break;
+ }
+ }
+
+ if ( !SfxGetpApp()->IsDowning() )
+ {
+ xImp->bUpdated = false;
+ InvalidateBindings_Impl(true);
+ }
+}
+
+void SfxDispatcher::InvalidateBindings_Impl( bool bModify )
+{
+ // App-Dispatcher?
+ if ( IsAppDispatcher() )
+ {
+ for ( SfxViewFrame *pFrame = SfxViewFrame::GetFirst();
+ pFrame;
+ pFrame = SfxViewFrame::GetNext( *pFrame ) )
+ pFrame->GetBindings().InvalidateAll(bModify);
+ }
+ else
+ {
+ SfxDispatcher *pDisp = GetBindings()->GetDispatcher_Impl();
+ if ( pDisp == this )
+ {
+ GetBindings()->InvalidateAll( bModify );
+ }
+ }
+}
+
+bool SfxDispatcher::IsUpdated_Impl() const
+{
+ return xImp->bUpdated;
+}
+
+void SfxDispatcher::SetDisableFlags( SfxDisableFlags nFlags )
+{
+ xImp->nDisableFlags = nFlags;
+ for ( SfxShellStack_Impl::reverse_iterator it = xImp->aStack.rbegin(); it != xImp->aStack.rend(); ++it )
+ (*it)->SetDisableFlags( nFlags );
+}
+
+SfxDisableFlags SfxDispatcher::GetDisableFlags() const
+{
+ return xImp->nDisableFlags;
+}
+
+SfxModule* SfxDispatcher::GetModule() const
+{
+ for ( sal_uInt16 nShell = 0;; ++nShell )
+ {
+ SfxShell *pSh = GetShell(nShell);
+ if ( pSh == nullptr )
+ return nullptr;
+ if ( auto pModule = dynamic_cast<SfxModule *>( pSh ) )
+ return pModule;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */