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-orpop-commandsarepending.
*/ bool SfxDispatcher::IsFlushed() const
{ return xImp->bFlushed;
}
/** This method performs outstanding push- and pop- commands. For <SfxShell>s, whicharenewonthestack,the<SfxShell::Activate(bool)>isinvoked withbMDI==sal_True,forSfxShellsthatareremovedfromthestack,the <SfxShell::Deactivate(bool)>isinvokedwithbMDI==sal_True
*/ void SfxDispatcher::Flush()
{ if (!xImp->bFlushed) FlushImpl();
}
/** With this method, a <SfxShell> pushed on to the SfxDispatcher. TheSfxShellisfirstmarkedforpushandatimerissetup. Firstwhenthetimerhascounteddowntozerothepush (<SfxDispatcher::Flush()>)isactuallyperformedandthe <SfxBindings>isinvalidated.Whilethetimeriscountingdown theopposingpushandpopcommandsonthesameSfxShellare leveledout.
*/ void SfxDispatcher::Push(SfxShell& rShell)
{
Pop( rShell, SfxDispatcherPopFlags::PUSH );
}
/** This method checks whether a particular <SfxShell> instance is ontheSfxDispatcher.
/** With this method it can be determined whether the SfxDispatcher is lockedorunlocked.AlockedSfxDispatcherdoesnotperform<SfxRequest>s andnolongerprovidesanystatusinformation.Itbehavesasifallthe slotsaredisabled.
/** Helper function to check whether a slot can be executed and checktheexecutionitself
*/ 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.Itisnotinitiallylockedandisconsideredflushed.
*/
SfxDispatcher::SfxDispatcher(SfxViewFrame *pViewFrame)
{
Construct_Impl();
xImp->pFrame = pViewFrame;
}
/** The destructor of the SfxDispatcher class should not be called when the SfxDispatcherinstanceisactive.Itmay,however,stillbea<SfxShell> pointeronthestack.
*/
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. TheSfxShellismarkedforpoppingandatimerissetup.Onlywhenthe timerhasreachedtheend,thepopisactuallyperformed (<SfxDispatcher::Flush()>)andthe<SfxBindings>isinvalidated. Whilethetimerisrunningtheopposingpushandpopcommandsonone SfxShellcanceleachotherout.
// 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 changesonthestack(<SfxDispatcher::Push()>and<SfxDispatcher::Pop())
/** This method returns a pointer to the <SfxBinding> Instance on which the SfxDispatcheriscurrentlybound.ASfxDispatcherisonlyboundto theSfxBindingswhenitis<UI-aktiv>.IfitisnotUI-active, aNULL-pointerisreturned.
/** Returns a pointer to the <SfxViewFrame> instance, which belongs to thisSfxDispatcher.Ifitisabouttheapplicationdispatcher, aNULL-pointerisreturned.
*/
SfxViewFrame* SfxDispatcher::GetFrame() const
{ return xImp->pFrame;
}
/** This method controls the activation of a dispatcher.
/** Helper function to put from rItem below the Which-ID in the pool of the ItemSetsrSet.
*/ 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;
}
}
/** 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. Thepassedarraymustberetaineduntilthedestructororthenext <SetSlotFilter()>,itisnotdeletedfromthedispatcher,soitcanthusbe static.
// HACK(here should be used an enum) ??? @paramnEnable1==true:onlyenablespecifiedslots,disableallother 0==false:disablespecifiedslots,firstenableallother @paramnCountNumberofSIDsinthefollowingArray @parampSIDssortedArrayof'nCount'SIDs
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. TherequiredslotsIDs(partlyconvertedtoWhich-IDsofthepool) mustbepresentinrstate.
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 // keep the underlying QMenu instance alive via the static lastPopupController chain here, until // the next popup menu is opened (there can only ever be a single popup menu open, so it // suffices to have a single static lastPopupController instance): static css::uno::Reference<css::frame::XPopupMenuController> lastPopupController; if (lastPopupController.is()) { if (css::uno::Reference<css::lang::XComponent> component(
lastPopupController, css::uno::UNO_QUERY);
component.is())
{
component->dispose();
}
lastPopupController = xPopupController;
} #endif
rtl::Reference< VCLXPopupMenu > xPopupMenu = new VCLXPopupMenu();
if ( !xPopupController.is() || !xPopupMenu.is() ) return;
/** 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);
}
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.