/* -*- 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 .
*/
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
/** 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 ( 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;
// 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());
}
}
}
/** 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->ClearLink();
// 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()" );
// 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);
/** 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" );
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);
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() ) returnfalse;
*ppShell = GetShell(aSvr.GetShellLevel());
*ppSlot = aSvr.GetSlot(); if ( nullptr == (*ppSlot)->GetExecFnc() && bRealSlot && *ppShell )
*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()) );
}
returnfalse;
}
/** 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" );
/** Helper function to put from rItem below the Which-ID in the pool of the Item Sets rSet.
*/ staticvoid MappedPut_Impl(SfxAllItemSet &rSet, const SfxPoolItem &rItem)
{ // Put with mapped Which-Id if needed if (SfxItemPool::IsSlot(rItem.Which()))
rSet.PutAsTargetWhich(rItem, rSet.GetPool()->GetWhichIDFromSlotID(rItem.Which())); else
rSet.Put(rItem);
}
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;
}
}
/** 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();
/** 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();
/** 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
{ if (SfxShell *pSh = GetShell(aSvr.GetShellLevel()))
{ const SfxSlot *pSlot = aSvr.GetSlot();
// 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;
if (comphelper::LibreOfficeKit::isActive()) return;
SfxViewFrame* pTop = xImp->pFrame->GetTopViewFrame(); if ( !pTop || pTop->GetBindings().GetDispatcher() != this ) return;
SfxFrame& rFrame = pTop->GetFrame(); if ( !rFrame.IsMenuBarOn_Impl() ) return;
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;
// 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 SfxMedium* pMedium = pSh->GetMedium(); const SfxBoolItem* pItem = pMedium ? pMedium->GetItemSet().GetItem(SID_VIEWONLY, false) : nullptr; 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;
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 (isViewerAppMode)
{ // Skip if the slot is not allowed in viewer app mode if (pSlot && !pSlot->IsMode(SfxSlotMode::VIEWERAPP)) continue;
}
if ( bReadOnlyShell )
{ // only show ChildWindows if their slot is allowed for readonly documents if ( pSlot && !pSlot->IsMode( SfxSlotMode::READONLYDOC ) ) 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 );
}
SfxDispatcher *pActDispatcher = pTaskWin->GetBindings().GetDispatcher_Impl(); if ( pActDispatcher )
{ 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);
// 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;
// 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);
}
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();
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
/** 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;
if (std::find(std::begin(allowedList), std::end(allowedList), commandName) != std::end(allowedList)) returntrue; else returnfalse;
}
/** 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; returnfalse;
}
// Count the number of Shells in the linked dispatchers.
Flush();
sal_uInt16 nTotCount = xImp->aStack.size();
if (bReadOnly && pViewSh->IsAllowChangeComments())
bCheckForCommentCommands = true;
}
// 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;
// This check can be true only if Lokit is active and view is readonly. if (pSlot && bCheckForCommentCommands)
bReadOnly = !IsCommandAllowedInLokReadOnlyViewMode(pSlot->GetCommand());
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); returntrue;
}
}
returnfalse;
}
/** 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_);
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 (!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;
const css::uno::Reference< css::uno::XComponentContext >& xContext = comphelper::getProcessComponentContext();
css::uno::Reference< css::frame::XPopupMenuController > xPopupController(
xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
u"com.sun.star.comp.framework.ResourceMenuController"_ustr, aArgs, xContext ), css::uno::UNO_QUERY ); #ifdefined EMSCRIPTEN && ENABLE_QT5 // At least under Emscripten with Qt5, the QMenu::exec underlying the below call to // xPopupMenu->execute returns immediately, without going into a new event loop, and we need to
--> --------------------
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.