/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //Includes below due to nInReschedule #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::util; #define TIMEOUT_FIRST 300 #define TIMEOUT_UPDATING 20 struct SfxFoundCache_Impl { sal_uInt16 nWhichId; // If available: Which-Id, else: nSlotId const SfxSlot* pSlot; // Pointer to SfxStateCache& rCache; // Pointer to StatusCache SfxFoundCache_Impl(sal_uInt16 nW, const SfxSlot *pS, SfxStateCache& rC) : nWhichId(nW) , pSlot(pS) , rCache(rC) {} }; class SfxFoundCacheArr_Impl { std::vector maData; public: SfxFoundCache_Impl& operator[] ( size_t i ) { return maData[i]; } size_t size() const { return maData.size(); } void push_back( SfxFoundCache_Impl p ) { maData.push_back(p); } }; class SfxBindings_Impl { public: css::uno::Reference< css::frame::XDispatchRecorder > xRecorder; css::uno::Reference< css::frame::XDispatchProvider > xProv; std::unique_ptr mxWorkWin; SfxBindings* pSubBindings; std::vector> pCaches; // One cache for each binding std::size_t nCachedFunc1; // index for the last one called std::size_t nCachedFunc2; // index for the second last called std::size_t nMsgPos; // Message-Position relative the one to be updated bool bContextChanged; bool bMsgDirty; // Has a MessageServer been invalidated? bool bAllMsgDirty; // Has a MessageServer been invalidated? bool bAllDirty; // After InvalidateAll bool bCtrlReleased; // while EnterRegistrations AutoTimer aAutoTimer { "sfx::SfxBindings aAutoTimer" }; // for volatile Slots bool bInUpdate; // for Assertions bool bInNextJob; // for Assertions bool bFirstRound; // First round in Update sal_uInt16 nOwnRegLevel; // Counts the real Locks, except those of the Super Bindings std::unordered_map< sal_uInt16, bool > m_aInvalidateSlots; // store slots which are invalidated while in update }; SfxBindings::SfxBindings() : pImpl(new SfxBindings_Impl), pDispatcher(nullptr), nRegLevel(1) // first becomes 0, when the Dispatcher is set { pImpl->nMsgPos = 0; pImpl->bAllMsgDirty = true; pImpl->bContextChanged = false; pImpl->bMsgDirty = true; pImpl->bAllDirty = true; pImpl->nCachedFunc1 = 0; pImpl->nCachedFunc2 = 0; pImpl->bCtrlReleased = false; pImpl->bFirstRound = false; pImpl->bInNextJob = false; pImpl->bInUpdate = false; pImpl->pSubBindings = nullptr; pImpl->nOwnRegLevel = nRegLevel; // all caches are valid (no pending invalidate-job) // create the list of caches pImpl->aAutoTimer.SetPriority(TaskPriority::HIGH_IDLE); pImpl->aAutoTimer.SetInvokeHandler( LINK(this, SfxBindings, NextJob) ); } SfxBindings::~SfxBindings() /* [Description] Destructor of the SfxBindings class. The one, for each existing Instance is automatically destroyed by the after the execution of . The still existing instances, which are registered by the SfxBindings instance, are automatically destroyed in the Destructor. These are usually the Floating-Toolboxen, Value-Sets etc. Arrays of SfxControllerItems may at this time no longer exist. */ { // The SubBindings should not be locked! pImpl->pSubBindings = nullptr; ENTERREGISTRATIONS(); pImpl->aAutoTimer.Stop(); DeleteControllers_Impl(); // Delete Caches pImpl->pCaches.clear(); pImpl->mxWorkWin.reset(); } void SfxBindings::DeleteControllers_Impl() { // in the first round delete Controllers std::size_t nCount = pImpl->pCaches.size(); std::size_t nCache = 0; while (nCache < nCount) { // Remember were you are SfxStateCache *pCache = pImpl->pCaches[nCache].get(); sal_uInt16 nSlotId = pCache->GetId(); // Re-align, because the cache may have been reduced std::size_t nNewCount = pImpl->pCaches.size(); if ( nNewCount < nCount ) { nCache = GetSlotPos(nSlotId); nCount = nNewCount; if ( nCache >= nNewCount || nSlotId != pImpl->pCaches[nCache]->GetId() ) { continue; } } ++nCache; } // Delete all Caches for ( nCache = pImpl->pCaches.size(); nCache > 0; --nCache ) { // Get Cache via css::sdbcx::Index SfxStateCache *pCache = pImpl->pCaches[ nCache-1 ].get(); // unbind all controllers in the cache SfxControllerItem *pNext; for ( SfxControllerItem *pCtrl = pCache->GetItemLink(); pCtrl; pCtrl = pNext ) { pNext = pCtrl->GetItemLink(); pCtrl->UnBind(); } if ( pCache->GetInternalController() ) pCache->GetInternalController()->UnBind(); // Delete Cache pImpl->pCaches.erase(pImpl->pCaches.begin() + nCache - 1); } } void SfxBindings::HidePopups( bool bHide ) { // Hide SfxChildWindows DBG_ASSERT( pDispatcher, "HidePopups not allowed without dispatcher" ); if ( pImpl->mxWorkWin ) pImpl->mxWorkWin->HidePopups_Impl( bHide ); } void SfxBindings::Update_Impl(SfxStateCache& rCache /*The up to date SfxStatusCache*/) { if (rCache.GetDispatch().is() && rCache.GetItemLink()) { rCache.SetCachedState(true); if (!rCache.GetInternalController()) return; } if ( !pDispatcher ) return; // gather together all with the same status method which are dirty SfxDispatcher &rDispat = *pDispatcher; const SfxSlot *pRealSlot = nullptr; const SfxSlotServer* pMsgServer = nullptr; SfxFoundCacheArr_Impl aFound; std::optional pSet = CreateSet_Impl(rCache, pRealSlot, &pMsgServer, aFound); bool bUpdated = false; if ( pSet ) { // Query Status if ( rDispat.FillState_( *pMsgServer, *pSet, pRealSlot ) ) { // Post Status for ( size_t nPos = 0; nPos < aFound.size(); ++nPos ) { const SfxFoundCache_Impl& rFound = aFound[nPos]; sal_uInt16 nWhich = rFound.nWhichId; const SfxPoolItem *pItem = nullptr; SfxItemState eState = pSet->GetItemState(nWhich, true, &pItem); if ( eState == SfxItemState::DEFAULT && SfxItemPool::IsWhich(nWhich) ) pItem = &pSet->Get(nWhich); UpdateControllers_Impl( rFound, pItem, eState ); } bUpdated = true; } pSet.reset(); } if (!bUpdated) { SfxFoundCache_Impl aFoundCache(0, pRealSlot, rCache); UpdateControllers_Impl( aFoundCache, nullptr, SfxItemState::DISABLED); } } void SfxBindings::InvalidateSlotsInMap_Impl() { for (auto const& slot : pImpl->m_aInvalidateSlots) Invalidate( slot.first ); pImpl->m_aInvalidateSlots.clear(); } void SfxBindings::AddSlotToInvalidateSlotsMap_Impl( sal_uInt16 nId ) { pImpl->m_aInvalidateSlots[nId] = true; } void SfxBindings::Update ( sal_uInt16 nId // the bound and up-to-date Slot-Id ) { if ( pDispatcher ) pDispatcher->Flush(); if ( pImpl->pSubBindings ) pImpl->pSubBindings->Update( nId ); SfxStateCache* pCache = GetStateCache( nId ); if ( !pCache ) return; pImpl->bInUpdate = true; if ( pImpl->bMsgDirty ) { UpdateSlotServer_Impl(); pCache = GetStateCache( nId ); } if (pCache) { bool bInternalUpdate = true; if( pCache->GetDispatch().is() && pCache->GetItemLink() ) { pCache->SetCachedState(true); bInternalUpdate = ( pCache->GetInternalController() != nullptr ); } if ( bInternalUpdate ) { // Query Status const SfxSlotServer* pMsgServer = pDispatcher ? pCache->GetSlotServer(*pDispatcher, pImpl->xProv) : nullptr; if ( !pCache->IsControllerDirty() ) { pImpl->bInUpdate = false; InvalidateSlotsInMap_Impl(); return; } if (!pMsgServer) { pCache->SetState(SfxItemState::DISABLED, nullptr); pImpl->bInUpdate = false; InvalidateSlotsInMap_Impl(); return; } Update_Impl(*pCache); } pImpl->bAllDirty = false; } pImpl->bInUpdate = false; InvalidateSlotsInMap_Impl(); } void SfxBindings::Update() { if ( pImpl->pSubBindings ) pImpl->pSubBindings->Update(); if ( !pDispatcher ) return; if ( nRegLevel ) return; pImpl->bInUpdate = true; pDispatcher->Flush(); pDispatcher->Update_Impl(); while ( !NextJob_Impl(nullptr) ) ; // loop pImpl->bInUpdate = false; InvalidateSlotsInMap_Impl(); } void SfxBindings::SetState ( const SfxItemSet& rSet // status values to be set ) { // when locked then only invalidate if ( nRegLevel ) { SfxItemIter aIter(rSet); for ( const SfxPoolItem *pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem() ) Invalidate( pItem->Which() ); } else { // Status may be accepted only if all slot-pointers are set if ( pImpl->bMsgDirty ) UpdateSlotServer_Impl(); // Iterate over the itemset, update if the slot bound //! Bug: Use WhichIter and possibly send VoidItems up SfxItemIter aIter(rSet); for ( const SfxPoolItem *pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem() ) { SfxStateCache* pCache = GetStateCache( rSet.GetPool()->GetSlotId(pItem->Which()) ); if ( pCache ) { // Update status if ( !pCache->IsControllerDirty() ) pCache->Invalidate(false); pCache->SetState( SfxItemState::DEFAULT, pItem ); //! Not implemented: Updates from EnumSlots via master slots } } } } void SfxBindings::SetState ( const SfxPoolItem& rItem // Status value to be set ) { if ( nRegLevel ) { Invalidate( rItem.Which() ); } else { // Status may be accepted only if all slot-pointers are set if ( pImpl->bMsgDirty ) UpdateSlotServer_Impl(); //update if the slot bound DBG_ASSERT( SfxItemPool::IsSlot( rItem.Which() ), "cannot set items with which-id" ); SfxStateCache* pCache = GetStateCache( rItem.Which() ); if ( pCache ) { // Update Status if ( !pCache->IsControllerDirty() ) pCache->Invalidate(false); pCache->SetState( SfxItemState::DEFAULT, &rItem ); //! Not implemented: Updates from EnumSlots via master slots } } } SfxStateCache* SfxBindings::GetAnyStateCache_Impl( sal_uInt16 nId ) { SfxStateCache* pCache = GetStateCache( nId ); if ( !pCache && pImpl->pSubBindings ) return pImpl->pSubBindings->GetAnyStateCache_Impl( nId ); return pCache; } SfxStateCache* SfxBindings::GetStateCache ( sal_uInt16 nId /* Slot-Id, which SfxStatusCache is to be found */ ) { return GetStateCache(nId, nullptr); } SfxStateCache* SfxBindings::GetStateCache ( sal_uInt16 nId, /* Slot-Id, which SfxStatusCache is to be found */ std::size_t * pPos /* NULL for instance the position from which the bindings are to be searched binary. Returns the position back for where the nId was found, or where it was inserted. */ ) { // is the specified function bound? const std::size_t nStart = ( pPos ? *pPos : 0 ); const std::size_t nPos = GetSlotPos( nId, nStart ); if ( nPos < pImpl->pCaches.size() && pImpl->pCaches[nPos]->GetId() == nId ) { if ( pPos ) *pPos = nPos; return pImpl->pCaches[nPos].get(); } return nullptr; } void SfxBindings::InvalidateAll ( bool bWithMsg /* true Mark Slot Server as invalid false Slot Server remains valid */ ) { DBG_ASSERT( !pImpl->bInUpdate, "SfxBindings::Invalidate while in update" ); if ( pImpl->pSubBindings ) pImpl->pSubBindings->InvalidateAll( bWithMsg ); // everything is already set dirty or downing => nothing to do if ( !pDispatcher || ( pImpl->bAllDirty && ( !bWithMsg || pImpl->bAllMsgDirty ) ) || SfxGetpApp()->IsDowning() ) { return; } pImpl->bAllMsgDirty = pImpl->bAllMsgDirty || bWithMsg; pImpl->bMsgDirty = pImpl->bMsgDirty || pImpl->bAllMsgDirty || bWithMsg; pImpl->bAllDirty = true; for (std::unique_ptr& pCache : pImpl->pCaches) pCache->Invalidate(bWithMsg); pImpl->nMsgPos = 0; if ( !nRegLevel ) { pImpl->aAutoTimer.Stop(); pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST); pImpl->aAutoTimer.Start(); } } void SfxBindings::Invalidate ( const sal_uInt16* pIds /* numerically sorted NULL-terminated array of slot IDs (individual, not as a couple!) */ ) { if ( pImpl->bInUpdate ) { sal_Int32 i = 0; while ( pIds[i] != 0 ) AddSlotToInvalidateSlotsMap_Impl( pIds[i++] ); if ( pImpl->pSubBindings ) pImpl->pSubBindings->Invalidate( pIds ); return; } if ( pImpl->pSubBindings ) pImpl->pSubBindings->Invalidate( pIds ); // everything is already set dirty or downing => nothing to do if ( !pDispatcher || pImpl->bAllDirty || SfxGetpApp()->IsDowning() ) return; // Search binary in always smaller areas for ( std::size_t n = GetSlotPos(*pIds); *pIds && n < pImpl->pCaches.size(); n = GetSlotPos(*pIds, n) ) { // If SID is ever bound, then invalidate the cache SfxStateCache *pCache = pImpl->pCaches[n].get(); if ( pCache->GetId() == *pIds ) pCache->Invalidate(false); // Next SID if ( !*++pIds ) break; assert( *pIds > *(pIds-1) ); } // if not enticed to start update timer pImpl->nMsgPos = 0; if ( !nRegLevel ) { pImpl->aAutoTimer.Stop(); pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST); pImpl->aAutoTimer.Start(); } } void SfxBindings::InvalidateShell ( const SfxShell& rSh, /* whose Slot-Ids should be invalidated */ bool bDeep /* true also the SfxShell's inherited slot IDs are invalidated false the inherited and not overridden Slot-Ids are invalidated */ // for now always bDeep ) { DBG_ASSERT( !pImpl->bInUpdate, "SfxBindings::Invalidate while in update" ); if ( pImpl->pSubBindings ) pImpl->pSubBindings->InvalidateShell( rSh, bDeep ); if ( !pDispatcher || pImpl->bAllDirty || SfxGetpApp()->IsDowning() ) return; // flush now already, it is done in GetShellLevel (rsh) anyway, // important so that is set correctly: pImpl-> ball(Msg)Dirty pDispatcher->Flush(); if ((pImpl->bAllDirty && pImpl->bAllMsgDirty) || SfxGetpApp()->IsDowning()) { // if the next one is anyway, then all the servers are collected return; } // Find Level sal_uInt16 nLevel = pDispatcher->GetShellLevel(rSh); if ( nLevel == USHRT_MAX ) return; for (std::unique_ptr& pCache : pImpl->pCaches) { const SfxSlotServer *pMsgServer = pCache->GetSlotServer(*pDispatcher, pImpl->xProv); if ( pMsgServer && pMsgServer->GetShellLevel() == nLevel ) pCache->Invalidate(false); } pImpl->nMsgPos = 0; if ( !nRegLevel ) { pImpl->aAutoTimer.Stop(); pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST); pImpl->aAutoTimer.Start(); pImpl->bFirstRound = true; } } void SfxBindings::Invalidate ( sal_uInt16 nId // Status value to be set ) { if ( pImpl->bInUpdate ) { AddSlotToInvalidateSlotsMap_Impl( nId ); if ( pImpl->pSubBindings ) pImpl->pSubBindings->Invalidate( nId ); return; } if ( pImpl->pSubBindings ) pImpl->pSubBindings->Invalidate( nId ); if ( !pDispatcher || pImpl->bAllDirty || SfxGetpApp()->IsDowning() ) return; SfxStateCache* pCache = GetStateCache(nId); if ( pCache ) { pCache->Invalidate(false); pImpl->nMsgPos = std::min(GetSlotPos(nId), pImpl->nMsgPos); if ( !nRegLevel ) { pImpl->aAutoTimer.Stop(); pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST); pImpl->aAutoTimer.Start(); } } } void SfxBindings::Invalidate ( sal_uInt16 nId, // Status value to be set bool bWithItem, // Clear StateCache? bool bWithMsg // Get new SlotServer? ) { DBG_ASSERT( !pImpl->bInUpdate, "SfxBindings::Invalidate while in update" ); if ( pImpl->pSubBindings ) pImpl->pSubBindings->Invalidate( nId, bWithItem, bWithMsg ); if ( SfxGetpApp()->IsDowning() ) return; SfxStateCache* pCache = GetStateCache(nId); if ( !pCache ) return; if ( bWithItem ) pCache->ClearCache(); pCache->Invalidate(bWithMsg); if ( !pDispatcher || pImpl->bAllDirty ) return; pImpl->nMsgPos = std::min(GetSlotPos(nId), pImpl->nMsgPos); if ( !nRegLevel ) { pImpl->aAutoTimer.Stop(); pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST); pImpl->aAutoTimer.Start(); } } std::size_t SfxBindings::GetSlotPos( sal_uInt16 nId, std::size_t nStartSearchAt ) { // answer immediately if a function-seek comes repeated if ( pImpl->nCachedFunc1 < pImpl->pCaches.size() && pImpl->pCaches[pImpl->nCachedFunc1]->GetId() == nId ) { return pImpl->nCachedFunc1; } if ( pImpl->nCachedFunc2 < pImpl->pCaches.size() && pImpl->pCaches[pImpl->nCachedFunc2]->GetId() == nId ) { // swap the caches std::swap(pImpl->nCachedFunc1, pImpl->nCachedFunc2); return pImpl->nCachedFunc1; } // binary search, if not found, seek to target-position if ( pImpl->pCaches.size() <= nStartSearchAt ) { return 0; } if ( pImpl->pCaches.size() == (nStartSearchAt+1) ) { return pImpl->pCaches[nStartSearchAt]->GetId() >= nId ? 0 : 1; } std::size_t nLow = nStartSearchAt; std::size_t nMid = 0; std::size_t nHigh = 0; bool bFound = false; nHigh = pImpl->pCaches.size() - 1; while ( !bFound && nLow <= nHigh ) { nMid = (nLow + nHigh) >> 1; DBG_ASSERT( nMid < pImpl->pCaches.size(), "bsearch is buggy" ); int nDiff = static_cast(nId) - static_cast( (pImpl->pCaches[nMid])->GetId() ); if ( nDiff < 0) { if ( nMid == 0 ) break; nHigh = nMid - 1; } else if ( nDiff > 0 ) { nLow = nMid + 1; if ( nLow == 0 ) break; } else bFound = true; } std::size_t nPos = bFound ? nMid : nLow; DBG_ASSERT( nPos <= pImpl->pCaches.size(), "" ); DBG_ASSERT( nPos == pImpl->pCaches.size() || nId <= pImpl->pCaches[nPos]->GetId(), "" ); DBG_ASSERT( nPos == nStartSearchAt || nId > pImpl->pCaches[nPos-1]->GetId(), "" ); DBG_ASSERT( ( (nPos+1) >= pImpl->pCaches.size() ) || nId < pImpl->pCaches[nPos+1]->GetId(), "" ); pImpl->nCachedFunc2 = pImpl->nCachedFunc1; pImpl->nCachedFunc1 = nPos; return nPos; } void SfxBindings::RegisterInternal_Impl( SfxControllerItem& rItem ) { Register_Impl( rItem, true ); } void SfxBindings::Register( SfxControllerItem& rItem ) { Register_Impl( rItem, false ); } void SfxBindings::Register_Impl( SfxControllerItem& rItem, bool bInternal ) { // DBG_ASSERT( nRegLevel > 0, "registration without EnterRegistrations" ); DBG_ASSERT( !pImpl->bInNextJob, "SfxBindings::Register while status-updating" ); // insert new cache if it does not already exist sal_uInt16 nId = rItem.GetId(); std::size_t nPos = GetSlotPos(nId); if ( nPos >= pImpl->pCaches.size() || pImpl->pCaches[nPos]->GetId() != nId ) { pImpl->pCaches.insert( pImpl->pCaches.begin() + nPos, std::make_unique(nId) ); DBG_ASSERT( nPos == 0 || pImpl->pCaches[nPos]->GetId() > pImpl->pCaches[nPos-1]->GetId(), "" ); DBG_ASSERT( (nPos == pImpl->pCaches.size()-1) || pImpl->pCaches[nPos]->GetId() < pImpl->pCaches[nPos+1]->GetId(), "" ); pImpl->bMsgDirty = true; } // enqueue the new binding if ( bInternal ) { pImpl->pCaches[nPos]->SetInternalController( &rItem ); } else { SfxControllerItem *pOldItem = pImpl->pCaches[nPos]->ChangeItemLink(&rItem); rItem.ChangeItemLink(pOldItem); } } void SfxBindings::Release( SfxControllerItem& rItem ) { DBG_ASSERT( !pImpl->bInNextJob, "SfxBindings::Release while status-updating" ); ENTERREGISTRATIONS(); // find the bound function sal_uInt16 nId = rItem.GetId(); std::size_t nPos = GetSlotPos(nId); SfxStateCache* pCache = (nPos < pImpl->pCaches.size()) ? pImpl->pCaches[nPos].get() : nullptr; if ( pCache && pCache->GetId() == nId ) { if ( pCache->GetInternalController() == &rItem ) { pCache->ReleaseInternalController(); } else { // is this the first binding in the list? SfxControllerItem* pItem = pCache->GetItemLink(); if ( pItem == &rItem ) pCache->ChangeItemLink( rItem.GetItemLink() ); else { // search the binding in the list while ( pItem && pItem->GetItemLink() != &rItem ) pItem = pItem->GetItemLink(); // unlink it if it was found if ( pItem ) pItem->ChangeItemLink( rItem.GetItemLink() ); } } // was this the last controller? if ( pCache->GetItemLink() == nullptr && !pCache->GetInternalController() ) { pImpl->bCtrlReleased = true; } } LEAVEREGISTRATIONS(); } SfxPoolItemHolder SfxBindings::ExecuteSynchron( sal_uInt16 nId, const SfxPoolItem** ppItems ) { if( !nId || !pDispatcher ) return SfxPoolItemHolder(); return Execute_Impl( nId, ppItems, 0, SfxCallMode::SYNCHRON, nullptr ); } bool SfxBindings::Execute( sal_uInt16 nId, const SfxPoolItem** ppItems, SfxCallMode nCallMode ) { if( !nId || !pDispatcher ) return false; const SfxPoolItemHolder aRet(Execute_Impl(nId, ppItems, 0, nCallMode, nullptr)); return aRet.is(); } SfxPoolItemHolder SfxBindings::Execute_Impl( sal_uInt16 nId, const SfxPoolItem** ppItems, sal_uInt16 nModi, SfxCallMode nCallMode, const SfxPoolItem **ppInternalArgs ) { SfxStateCache *pCache = GetStateCache( nId ); if ( !pCache ) { SfxBindings *pBind = pImpl->pSubBindings; while ( pBind ) { if ( pBind->GetStateCache( nId ) ) return pBind->Execute_Impl( nId, ppItems, nModi, nCallMode, ppInternalArgs ); pBind = pBind->pImpl->pSubBindings; } } SfxDispatcher &rDispatcher = *pDispatcher; rDispatcher.Flush(); // get SlotServer (Slot+ShellLevel) and Shell from cache std::unique_ptr xCache; if ( !pCache ) { // Execution of non cached slots (Accelerators don't use Controllers) // slot is uncached, use SlotCache to handle external dispatch providers xCache.reset(new SfxStateCache(nId)); pCache = xCache.get(); } pCache->GetSlotServer( rDispatcher, pImpl->xProv ); // make pCache->GetDispatch() up to date if ( pCache->GetDispatch().is() ) { DBG_ASSERT( !ppInternalArgs, "Internal args get lost when dispatched!" ); SfxItemPool &rPool = GetDispatcher()->GetFrame()->GetObjectShell()->GetPool(); SfxRequest aReq( nId, nCallMode, rPool ); aReq.SetModifier( nModi ); if( ppItems ) while( *ppItems ) aReq.AppendItem( **ppItems++ ); // cache binds to an external dispatch provider sal_Int16 eRet = pCache->Dispatch( aReq.GetArgs(), nCallMode == SfxCallMode::SYNCHRON ); SfxPoolItem* pPoolItem(nullptr); if ( eRet == css::frame::DispatchResultState::DONTKNOW ) pPoolItem = new SfxVoidItem( nId ); else pPoolItem = new SfxBoolItem( nId, eRet == css::frame::DispatchResultState::SUCCESS); return SfxPoolItemHolder(rPool, pPoolItem, true); } // slot is handled internally by SfxDispatcher if ( pImpl->bMsgDirty ) UpdateSlotServer_Impl(); SfxShell *pShell=nullptr; const SfxSlot *pSlot=nullptr; const SfxSlotServer* pServer = pCache->GetSlotServer( rDispatcher, pImpl->xProv ); if ( !pServer ) { return SfxPoolItemHolder(); } else { pShell = rDispatcher.GetShell( pServer->GetShellLevel() ); pSlot = pServer->GetSlot(); } if (!pShell) return SfxPoolItemHolder(); SfxItemPool &rPool = pShell->GetPool(); SfxRequest aReq( nId, nCallMode, rPool ); aReq.SetModifier( nModi ); if( ppItems ) while( *ppItems ) aReq.AppendItem( **ppItems++ ); if ( ppInternalArgs ) { SfxAllItemSet aSet( rPool ); for ( const SfxPoolItem **pArg = ppInternalArgs; *pArg; ++pArg ) aSet.Put( **pArg ); aReq.SetInternalArgs_Impl( aSet ); } Execute_Impl( aReq, pSlot, pShell ); const SfxPoolItemHolder& rRetval(aReq.GetReturnValue()); if (!rRetval) return SfxPoolItemHolder(rPool, new SfxVoidItem( nId ), true); return rRetval; } void SfxBindings::Execute_Impl( SfxRequest& aReq, const SfxSlot* pSlot, SfxShell* pShell ) { SfxItemPool &rPool = pShell->GetPool(); if ( SfxSlotKind::Attribute == pSlot->GetKind() ) { // Which value has to be mapped for Attribute slots const sal_uInt16 nSlotId = pSlot->GetSlotId(); aReq.SetSlot( nSlotId ); if ( pSlot->IsMode(SfxSlotMode::TOGGLE) ) { // The value is attached to a toggleable attribute (Bools) sal_uInt16 nWhich = pSlot->GetWhich(rPool); SfxItemSet aSet(rPool, nWhich, nWhich); SfxStateFunc pFunc = pSlot->GetStateFnc(); (*pFunc)(pShell, aSet); const SfxPoolItem *pOldItem; SfxItemState eState = aSet.GetItemState(nWhich, true, &pOldItem); if ( eState == SfxItemState::DISABLED ) return; if ( SfxItemState::DEFAULT == eState && SfxItemPool::IsWhich(nWhich) ) pOldItem = &aSet.Get(nWhich); if ( SfxItemState::SET == eState || ( SfxItemState::DEFAULT == eState && SfxItemPool::IsWhich(nWhich) && pOldItem ) ) { if ( auto pOldBoolItem = dynamic_cast< const SfxBoolItem *>( pOldItem ) ) { // we can toggle Bools bool bOldValue = pOldBoolItem->GetValue(); std::unique_ptr pNewItem(static_cast(pOldItem->Clone())); pNewItem->SetValue( !bOldValue ); aReq.AppendItem( *pNewItem ); } else if ( auto pOldEnumItem = dynamic_cast< const SfxEnumItemInterface *>( pOldItem ) ) { if (pOldEnumItem->HasBoolValue()) { // and Enums with Bool-Interface std::unique_ptr pNewItem( static_cast(pOldEnumItem->Clone())); pNewItem->SetBoolValue(!pOldEnumItem->GetBoolValue()); aReq.AppendItem( *pNewItem ); } } else { OSL_FAIL( "Toggle only for Enums and Bools allowed" ); } } else if ( SfxItemState::INVALID == eState ) { // Create one Status-Item for each Factory std::unique_ptr pNewItem = pSlot->GetType()->CreateItem(); DBG_ASSERT( pNewItem, "Toggle to slot without ItemFactory" ); pNewItem->SetWhich( nWhich ); if ( auto pNewBoolItem = dynamic_cast( pNewItem.get() ) ) { // we can toggle Bools pNewBoolItem->SetValue( true ); aReq.AppendItem( *pNewItem ); } else if ( auto pEnumItem = dynamic_cast( pNewItem.get() ) ) { if (pEnumItem->HasBoolValue()) { // and Enums with Bool-Interface pEnumItem->SetBoolValue(true); aReq.AppendItem( *pNewItem ); } } else { OSL_FAIL( "Toggle only for Enums and Bools allowed" ); } } else { OSL_FAIL( "suspicious Toggle-Slot" ); } } pDispatcher->Execute_( *pShell, *pSlot, aReq, aReq.GetCallMode() | SfxCallMode::RECORD ); } else pDispatcher->Execute_( *pShell, *pSlot, aReq, aReq.GetCallMode() | SfxCallMode::RECORD ); } void SfxBindings::UpdateSlotServer_Impl() { // synchronize pDispatcher->Flush(); if ( pImpl->bAllMsgDirty ) { if ( !nRegLevel ) { pImpl->bContextChanged = false; } else pImpl->bContextChanged = true; } for (size_t i = 0; i < pImpl->pCaches.size(); ++i) { //GetSlotServer can modify pImpl->pCaches pImpl->pCaches[i]->GetSlotServer(*pDispatcher, pImpl->xProv); } pImpl->bMsgDirty = pImpl->bAllMsgDirty = false; Broadcast( SfxHint(SfxHintId::DocChanged) ); } std::optional SfxBindings::CreateSet_Impl ( SfxStateCache& rCache, // in: Status-Cache from nId const SfxSlot*& pRealSlot, // out: RealSlot to nId const SfxSlotServer** pMsgServer, // out: Slot-Server to nId SfxFoundCacheArr_Impl& rFound // out: List of Caches for Siblings ) { DBG_ASSERT( !pImpl->bMsgDirty, "CreateSet_Impl with dirty MessageServer" ); assert(pDispatcher); const SfxSlotServer* pMsgSvr = rCache.GetSlotServer(*pDispatcher, pImpl->xProv); if (!pMsgSvr) return {}; pRealSlot = nullptr; *pMsgServer = pMsgSvr; sal_uInt16 nShellLevel = pMsgSvr->GetShellLevel(); SfxShell *pShell = pDispatcher->GetShell( nShellLevel ); if ( !pShell ) // rare GPF when browsing through update from Inet-Notify return {}; SfxItemPool &rPool = pShell->GetPool(); // get the status method, which is served by the rCache SfxStateFunc pFnc = nullptr; pRealSlot = pMsgSvr->GetSlot(); pFnc = pRealSlot->GetStateFnc(); // the RealSlot is always on SfxFoundCache_Impl aFound(pRealSlot->GetWhich(rPool), pRealSlot, rCache); rFound.push_back( aFound ); // Search through the bindings for slots served by the same function. This , // will only affect slots which are present in the found interface. // The position of the Statecaches in StateCache-Array std::size_t nCachePos = pImpl->nMsgPos; const SfxSlot *pSibling = pRealSlot->GetNextSlot(); // the Slots ODF and interfaces are linked in a circle while ( pSibling > pRealSlot ) { SfxStateFunc pSiblingFnc=nullptr; SfxStateCache *pSiblingCache = GetStateCache( pSibling->GetSlotId(), &nCachePos ); // Is the slot cached ? if ( pSiblingCache ) { const SfxSlotServer *pServ = pSiblingCache->GetSlotServer(*pDispatcher, pImpl->xProv); if ( pServ && pServ->GetShellLevel() == nShellLevel ) pSiblingFnc = pServ->GetSlot()->GetStateFnc(); } // Does the slot have to be updated at all? bool bInsert = pSiblingCache && pSiblingCache->IsControllerDirty(); // It is not enough to ask for the same shell!! bool bSameMethod = pSiblingCache && pFnc == pSiblingFnc; if ( bInsert && bSameMethod ) { SfxFoundCache_Impl aFoundCache( pSibling->GetWhich(rPool), pSibling, *pSiblingCache); rFound.push_back( aFoundCache ); } pSibling = pSibling->GetNextSlot(); } // Create a Set from the ranges WhichRangesContainer ranges; size_t i = 0; while ( i < rFound.size() ) { const sal_uInt16 nWhich1 = rFound[i].nWhichId; // consecutive numbers for ( ; i < rFound.size()-1; ++i ) if ( rFound[i].nWhichId+1 != rFound[i+1].nWhichId ) break; const sal_uInt16 nWhich2 = rFound[i++].nWhichId; ranges = ranges.MergeRange(nWhich1, nWhich2); } SfxItemSet aSet(rPool, std::move(ranges)); return aSet; } void SfxBindings::UpdateControllers_Impl ( const SfxFoundCache_Impl& rFound, // Cache, Slot, Which etc. const SfxPoolItem* pItem, // item to send to controller SfxItemState eState // state of item ) { SfxStateCache& rCache = rFound.rCache; const SfxSlot* pSlot = rFound.pSlot; DBG_ASSERT( !pSlot || rCache.GetId() == pSlot->GetSlotId(), "SID mismatch" ); // bound until now, the Controller to update the Slot. if (!rCache.IsControllerDirty()) return; if ( SfxItemState::INVALID == eState ) { // ambiguous rCache.SetState( SfxItemState::INVALID, INVALID_POOL_ITEM ); } else if ( SfxItemState::DEFAULT == eState && SfxItemPool::IsSlot(rFound.nWhichId) ) { // no Status or Default but without Pool // tdf#162666 note that use DISABLED_POOL_ITEM needs to be // handled correctly in the cache, see comments there rCache.SetState( SfxItemState::UNKNOWN, DISABLED_POOL_ITEM ); } else if ( SfxItemState::DISABLED == eState ) rCache.SetState(SfxItemState::DISABLED, nullptr); else rCache.SetState(SfxItemState::DEFAULT, pItem); } IMPL_LINK( SfxBindings, NextJob, Timer *, pTimer, void ) { SfxViewFrame* pFrame = pDispatcher ? pDispatcher->GetFrame() : nullptr; SfxLokLanguageGuard aGuard(pFrame ? pFrame->GetViewShell() : nullptr); NextJob_Impl(pTimer); } bool SfxBindings::NextJob_Impl(Timer const * pTimer) { const unsigned MAX_INPUT_DELAY = 200; if ( Application::GetLastInputInterval() < MAX_INPUT_DELAY && pTimer ) { pImpl->aAutoTimer.SetTimeout(TIMEOUT_UPDATING); return true; } SfxApplication *pSfxApp = SfxGetpApp(); if( pDispatcher ) pDispatcher->Update_Impl(); // modifying the SfxObjectInterface-stack without SfxBindings => nothing to do SfxViewFrame* pFrame = pDispatcher ? pDispatcher->GetFrame() : nullptr; if ( (pFrame && !pFrame->GetObjectShell()->AcceptStateUpdate()) || pSfxApp->IsDowning() || pImpl->pCaches.empty() ) { return true; } if ( !pDispatcher || !pDispatcher->IsFlushed() ) { return true; } // if possible Update all server / happens in its own time slice if ( pImpl->bMsgDirty ) { UpdateSlotServer_Impl(); return false; } pImpl->bAllDirty = false; pImpl->aAutoTimer.SetTimeout(TIMEOUT_UPDATING); // at least 10 loops and further if more jobs are available but no input bool bPreEmptive = pTimer; sal_uInt16 nLoops = 10; pImpl->bInNextJob = true; const std::size_t nCount = pImpl->pCaches.size(); while ( pImpl->nMsgPos < nCount ) { // iterate through the bound functions bool bJobDone = false; while ( !bJobDone ) { SfxStateCache* pCache = pImpl->pCaches[pImpl->nMsgPos].get(); assert(pCache && "invalid SfxStateCache-position in job queue"); bool bWasDirty = pCache->IsControllerDirty(); if ( bWasDirty ) { Update_Impl(*pCache); DBG_ASSERT(nCount == pImpl->pCaches.size(), "Reschedule in StateChanged => buff"); } // skip to next function binding ++pImpl->nMsgPos; // keep job if it is not completed, but any input is available bJobDone = pImpl->nMsgPos >= nCount; if ( bJobDone && pImpl->bFirstRound ) { // Update of the preferred shell has been done, now may // also the others shells be updated bJobDone = false; pImpl->bFirstRound = false; pImpl->nMsgPos = 0; } if ( bWasDirty && !bJobDone && bPreEmptive && (--nLoops == 0) ) { pImpl->bInNextJob = false; return false; } } } pImpl->nMsgPos = 0; pImpl->aAutoTimer.Stop(); // Update round is finished pImpl->bInNextJob = false; Broadcast(SfxHint(SfxHintId::UpdateDone)); return true; } sal_uInt16 SfxBindings::EnterRegistrations(std::string_view pFile, int nLine) { SAL_INFO( "sfx.control", std::setw(std::min(nRegLevel, sal_uInt16(8))) << ' ' << "this = " << this << " Level = " << nRegLevel << " SfxBindings::EnterRegistrations " << (!pFile.empty() ? SAL_STREAM("File: " << pFile << " Line: " << nLine) : "")); // When bindings are locked, also lock sub bindings. if ( pImpl->pSubBindings ) { pImpl->pSubBindings->ENTERREGISTRATIONS(); // These EnterRegistrations are not "real" for the SubBindings pImpl->pSubBindings->pImpl->nOwnRegLevel--; // Synchronize Bindings pImpl->pSubBindings->nRegLevel = nRegLevel + pImpl->pSubBindings->pImpl->nOwnRegLevel + 1; } pImpl->nOwnRegLevel++; // check if this is the outer most level if ( ++nRegLevel == 1 ) { // stop background-processing pImpl->aAutoTimer.Stop(); // flush the cache pImpl->nCachedFunc1 = 0; pImpl->nCachedFunc2 = 0; // Mark if the all of the Caches have disappeared. pImpl->bCtrlReleased = false; } return nRegLevel; } void SfxBindings::LeaveRegistrations( std::string_view pFile, int nLine ) { DBG_ASSERT( nRegLevel, "Leave without Enter" ); // Only when the SubBindings are still locked by the Superbindings, // remove this lock (i.e. if there are more locks than "real" ones) if ( pImpl->pSubBindings && pImpl->pSubBindings->nRegLevel > pImpl->pSubBindings->pImpl->nOwnRegLevel ) { // Synchronize Bindings pImpl->pSubBindings->nRegLevel = nRegLevel + pImpl->pSubBindings->pImpl->nOwnRegLevel; // This LeaveRegistrations is not "real" for SubBindings pImpl->pSubBindings->pImpl->nOwnRegLevel++; pImpl->pSubBindings->LEAVEREGISTRATIONS(); } pImpl->nOwnRegLevel--; // check if this is the outer most level if ( --nRegLevel == 0 && SfxGetpApp() && !SfxGetpApp()->IsDowning() ) { if ( pImpl->bContextChanged ) { pImpl->bContextChanged = false; } SfxViewFrame* pFrame = pDispatcher->GetFrame(); // If possible remove unused Caches, for example prepare PlugInInfo if ( pImpl->bCtrlReleased ) { for ( sal_uInt16 nCache = pImpl->pCaches.size(); nCache > 0; --nCache ) { // Get Cache via css::sdbcx::Index SfxStateCache *pCache = pImpl->pCaches[nCache-1].get(); // No interested Controller present if ( pCache->GetItemLink() == nullptr && !pCache->GetInternalController() ) { // Remove Cache. Safety: first remove and then delete pImpl->pCaches.erase(pImpl->pCaches.begin() + nCache - 1); } } } // restart background-processing pImpl->nMsgPos = 0; if ( !pFrame || !pFrame->GetObjectShell() ) return; if ( !pImpl->pCaches.empty() ) { pImpl->aAutoTimer.Stop(); pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST); pImpl->aAutoTimer.Start(); } } SAL_INFO( "sfx.control", std::setw(std::min(nRegLevel, sal_uInt16(8))) << ' ' << "this = " << this << " Level = " << nRegLevel << " SfxBindings::LeaveRegistrations " << (!pFile.empty() ? SAL_STREAM("File: " << pFile << " Line: " << nLine) : "")); } void SfxBindings::SetDispatcher( SfxDispatcher *pDisp ) { SfxDispatcher *pOldDispat = pDispatcher; if ( pDisp == pDispatcher ) return; if ( pOldDispat ) { SfxBindings* pBind = pOldDispat->GetBindings(); while ( pBind ) { if ( pBind->pImpl->pSubBindings == this && pBind->pDispatcher != pDisp ) pBind->SetSubBindings_Impl( nullptr ); pBind = pBind->pImpl->pSubBindings; } } pDispatcher = pDisp; css::uno::Reference < css::frame::XDispatchProvider > xProv; if ( pDisp ) xProv.set( pDisp->GetFrame()->GetFrame().GetFrameInterface(), UNO_QUERY ); SetDispatchProvider_Impl( xProv ); InvalidateAll( true ); if ( pDispatcher && !pOldDispat ) { if ( pImpl->pSubBindings && pImpl->pSubBindings->pDispatcher != pOldDispat ) { OSL_FAIL( "SubBindings already set before activating!" ); pImpl->pSubBindings->ENTERREGISTRATIONS(); } LEAVEREGISTRATIONS(); } else if( !pDispatcher ) { ENTERREGISTRATIONS(); if ( pImpl->pSubBindings && pImpl->pSubBindings->pDispatcher != pOldDispat ) { OSL_FAIL( "SubBindings still set even when deactivating!" ); pImpl->pSubBindings->LEAVEREGISTRATIONS(); } } Broadcast( SfxHint( SfxHintId::DataChanged ) ); if ( !pDisp ) return; SfxBindings* pBind = pDisp->GetBindings(); while ( pBind && pBind != this ) { if ( !pBind->pImpl->pSubBindings ) { pBind->SetSubBindings_Impl( this ); break; } pBind = pBind->pImpl->pSubBindings; } } void SfxBindings::ClearCache_Impl( sal_uInt16 nSlotId ) { SfxStateCache* pCache = GetStateCache(nSlotId); if (!pCache) return; pCache->ClearCache(); } void SfxBindings::StartUpdate_Impl( bool bComplete ) { if ( pImpl->pSubBindings ) pImpl->pSubBindings->StartUpdate_Impl( bComplete ); if ( !bComplete ) // Update may be interrupted NextJob_Impl(&pImpl->aAutoTimer); else // Update all slots in a row NextJob_Impl(nullptr); } SfxItemState SfxBindings::QueryState( sal_uInt16 nSlot, std::unique_ptr &rpState ) { css::uno::Reference< css::frame::XDispatch > xDisp; SfxStateCache *pCache = GetStateCache( nSlot ); if ( pCache ) xDisp = pCache->GetDispatch(); if ( xDisp.is() || !pCache ) { const SfxSlot* pSlot = SfxSlotPool::GetSlotPool( pDispatcher->GetFrame() ).GetSlot( nSlot ); if ( !pSlot || pSlot->aUnoName.isEmpty() ) return SfxItemState::DISABLED; css::util::URL aURL; OUString aCmd( u".uno:"_ustr ); aURL.Protocol = aCmd; aURL.Path = pSlot->GetUnoName(); aCmd += aURL.Path; aURL.Complete = aCmd; aURL.Main = aCmd; if ( !xDisp.is() ) xDisp = pImpl->xProv->queryDispatch( aURL, OUString(), 0 ); if ( xDisp.is() ) { if (!dynamic_cast(xDisp.get())) { bool bDeleteCache = false; if ( !pCache ) { pCache = new SfxStateCache( nSlot ); pCache->GetSlotServer( *GetDispatcher_Impl(), pImpl->xProv ); bDeleteCache = true; } SfxItemState eState = SfxItemState::SET; rtl::Reference xBind(new BindDispatch_Impl( xDisp, aURL, pCache, pSlot )); xDisp->addStatusListener( xBind, aURL ); if ( !xBind->GetStatus().IsEnabled ) { eState = SfxItemState::DISABLED; } else { css::uno::Any aAny = xBind->GetStatus().State; const css::uno::Type& aType = aAny.getValueType(); if ( aType == cppu::UnoType::get() ) { bool bTemp = false; aAny >>= bTemp ; rpState.reset(new SfxBoolItem( nSlot, bTemp )); } else if ( aType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() ) { sal_uInt16 nTemp = 0; aAny >>= nTemp ; rpState.reset(new SfxUInt16Item( nSlot, nTemp )); } else if ( aType == cppu::UnoType::get() ) { sal_uInt32 nTemp = 0; aAny >>= nTemp ; rpState.reset(new SfxUInt32Item( nSlot, nTemp )); } else if ( aType == cppu::UnoType::get() ) { OUString sTemp ; aAny >>= sTemp ; rpState.reset(new SfxStringItem( nSlot, sTemp )); } else rpState.reset(new SfxVoidItem( nSlot )); } xDisp->removeStatusListener( xBind, aURL ); xBind->Release(); xBind.clear(); if ( bDeleteCache ) { delete pCache; pCache = nullptr; } return eState; } } } // Then test at the dispatcher to check if the returned items from // there are always DELETE_ON_IDLE, a copy of it has to be made in // order to allow for transition of ownership. SfxPoolItemHolder aResult; const SfxItemState eState(pDispatcher->QueryState(nSlot, aResult)); if (SfxItemState::SET == eState) { DBG_ASSERT( aResult.getItem(), "SfxItemState::SET but no item!" ); if (aResult) rpState.reset(aResult.getItem()->Clone()); } else if (SfxItemState::DEFAULT == eState && aResult) { rpState.reset(aResult.getItem()->Clone()); } return eState; } void SfxBindings::QueryControlState( sal_uInt16 nSlot, boost::property_tree::ptree& rState ) { if ( SfxGetpApp()->IsDowning() ) return; if ( pDispatcher ) pDispatcher->Flush(); if ( pImpl->pSubBindings ) pImpl->pSubBindings->QueryControlState( nSlot, rState ); SfxStateCache* pCache = GetStateCache( nSlot ); if ( !pCache ) return; if ( pImpl->bMsgDirty ) { UpdateSlotServer_Impl(); pCache = GetStateCache( nSlot ); } if (pCache && pCache->GetItemLink() ) { pCache->GetState(rState); } } sal_uInt16 SfxBindings::QuerySlotId( const util::URL& aURL ) { if (!pImpl) return 0; css::uno::Reference xDispatch = pImpl->xProv->queryDispatch(aURL, OUString(), 0); if (!xDispatch.is()) return 0; SfxOfficeDispatch* pDispatch = dynamic_cast(xDispatch.get()); if (!pDispatch) return 0; return pDispatch->GetId(); } void SfxBindings::SetSubBindings_Impl( SfxBindings *pSub ) { if ( pImpl->pSubBindings ) { pImpl->pSubBindings->SetDispatchProvider_Impl( css::uno::Reference< css::frame::XDispatchProvider > () ); } pImpl->pSubBindings = pSub; if ( pSub ) { pImpl->pSubBindings->SetDispatchProvider_Impl( pImpl->xProv ); } } SfxBindings* SfxBindings::GetSubBindings_Impl() const { return pImpl->pSubBindings; } void SfxBindings::SetWorkWindow_Impl( std::unique_ptr xWork ) { pImpl->mxWorkWin = std::move(xWork); } SfxWorkWindow* SfxBindings::GetWorkWindow_Impl() const { return pImpl->mxWorkWin.get(); } bool SfxBindings::IsInUpdate() const { bool bInUpdate = pImpl->bInUpdate; if ( !bInUpdate && pImpl->pSubBindings ) bInUpdate = pImpl->pSubBindings->IsInUpdate(); return bInUpdate; } void SfxBindings::SetVisibleState( sal_uInt16 nId, bool bShow ) { SfxStateCache *pCache = GetStateCache( nId ); if ( pCache ) pCache->SetVisibleState( bShow ); } void SfxBindings::SetActiveFrame( const css::uno::Reference< css::frame::XFrame > & rFrame ) { if ( rFrame.is() || !pDispatcher ) SetDispatchProvider_Impl( css::uno::Reference< css::frame::XDispatchProvider > ( rFrame, css::uno::UNO_QUERY ) ); else SetDispatchProvider_Impl( css::uno::Reference< css::frame::XDispatchProvider > ( pDispatcher->GetFrame()->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY ) ); } css::uno::Reference< css::frame::XFrame > SfxBindings::GetActiveFrame() const { const css::uno::Reference< css::frame::XFrame > xFrame( pImpl->xProv, css::uno::UNO_QUERY ); if ( xFrame.is() || !pDispatcher ) return xFrame; else return pDispatcher->GetFrame()->GetFrame().GetFrameInterface(); } void SfxBindings::SetDispatchProvider_Impl( const css::uno::Reference< css::frame::XDispatchProvider > & rProv ) { bool bInvalidate = ( rProv != pImpl->xProv ); if ( bInvalidate ) { pImpl->xProv = rProv; InvalidateAll( true ); } if ( pImpl->pSubBindings ) pImpl->pSubBindings->SetDispatchProvider_Impl( pImpl->xProv ); } const css::uno::Reference< css::frame::XDispatchRecorder >& SfxBindings::GetRecorder() const { return pImpl->xRecorder; } void SfxBindings::SetRecorder_Impl( css::uno::Reference< css::frame::XDispatchRecorder > const & rRecorder ) { pImpl->xRecorder = rRecorder; } void SfxBindings::ContextChanged_Impl() { if ( !pImpl->bInUpdate && ( !pImpl->bContextChanged || !pImpl->bAllMsgDirty ) ) { InvalidateAll( true ); } } uno::Reference < frame::XDispatch > SfxBindings::GetDispatch( const SfxSlot* pSlot, const util::URL& aURL, bool bMasterCommand ) { uno::Reference < frame::XDispatch > xRet; SfxStateCache* pCache = GetStateCache( pSlot->nSlotId ); if ( pCache && !bMasterCommand ) xRet = pCache->GetInternalDispatch(); if ( !xRet.is() ) { // dispatches for slaves are unbound, they don't have a state SfxOfficeDispatch* pDispatch = bMasterCommand ? new SfxOfficeDispatch( pDispatcher, pSlot, aURL ) : new SfxOfficeDispatch( *this, pDispatcher, pSlot, aURL ); pDispatch->SetMasterUnoCommand( bMasterCommand ); xRet.set( pDispatch ); if ( !pCache ) pCache = GetStateCache( pSlot->nSlotId ); DBG_ASSERT( pCache, "No cache for OfficeDispatch!" ); if ( pCache && !bMasterCommand ) pCache->SetInternalDispatch( xRet ); } return xRet; } Timer& SfxBindings::GetTimer() { return pImpl->aAutoTimer; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */