Files
libreoffice/sfx2/source/control/bindings.cxx
Jochen Nitschke 70dee11c2e use a define for invalid SfxPoolItem
and use helper function IsInvalidItem() more

Change-Id: Ifd651a336a7bd4ef4f945219d765afb827465438
Reviewed-on: https://gerrit.libreoffice.org/33611
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
Reviewed-by: Jochen Nitschke <j.nitschke+logerrit@ok.de>
Tested-by: Jochen Nitschke <j.nitschke+logerrit@ok.de>
2017-01-27 15:58:28 +00:00

1969 lines
62 KiB
C++

/* -*- 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 <sal/config.h>
#include <iomanip>
#include <sal/log.hxx>
#include <svl/itempool.hxx>
#include <svl/itemiter.hxx>
#include <svl/eitem.hxx>
#include <svl/aeitem.hxx>
#include <svl/intitem.hxx>
#include <svl/stritem.hxx>
#include <svl/visitem.hxx>
#include <com/sun/star/util/URLTransformer.hpp>
#include <com/sun/star/util/XURLTransformer.hpp>
#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp>
#include <com/sun/star/frame/XDispatch.hpp>
#include <com/sun/star/frame/XDispatchProvider.hpp>
#include <com/sun/star/frame/DispatchResultState.hpp>
#include <com/sun/star/frame/XStatusListener.hpp>
#include <com/sun/star/frame/FrameSearchFlag.hpp>
#include <com/sun/star/frame/XDispatchProviderInterception.hpp>
#include <com/sun/star/frame/FeatureStateEvent.hpp>
#include <com/sun/star/frame/DispatchDescriptor.hpp>
#include <com/sun/star/frame/XController.hpp>
#include <comphelper/processfactory.hxx>
#include "itemdel.hxx"
//Includes below due to nInReschedule
#include "appdata.hxx"
#include <sfx2/bindings.hxx>
#include <sfx2/msg.hxx>
#include "statcach.hxx"
#include <sfx2/ctrlitem.hxx>
#include <sfx2/app.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/request.hxx>
#include <sfx2/objface.hxx>
#include "sfxtypes.hxx"
#include "workwin.hxx"
#include <sfx2/unoctitm.hxx>
#include <sfx2/sfx.hrc>
#include <sfx2/sfxuno.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/msgpool.hxx>
#include <com/sun/star/frame/XModuleManager.hpp>
#include <memory>
#include <unordered_map>
#include <vector>
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
static sal_uInt16 nTimeOut = 300;
#define TIMEOUT_FIRST nTimeOut
#define TIMEOUT_UPDATING 20
typedef std::unordered_map< sal_uInt16, bool > InvalidateSlotMap;
typedef std::vector<SfxStateCache*> SfxStateCacheArr_Impl;
struct SfxFoundCache_Impl
{
sal_uInt16 nWhichId; // If available: Which-Id, else: nSlotId
const SfxSlot* pSlot; // Pointer to <Master-Slot>
SfxStateCache* pCache; // Pointer to StatusCache, if possible NULL
SfxFoundCache_Impl(sal_uInt16 nW, const SfxSlot *pS, SfxStateCache *pC ):
nWhichId(nW),
pSlot(pS),
pCache(pC)
{}
};
class SfxFoundCacheArr_Impl
{
typedef std::vector<std::unique_ptr<SfxFoundCache_Impl> > DataType;
DataType maData;
public:
SfxFoundCache_Impl& operator[] ( size_t i )
{
return *maData[i].get();
}
size_t size() const
{
return maData.size();
}
void push_back( SfxFoundCache_Impl* p )
{
maData.push_back(std::unique_ptr<SfxFoundCache_Impl>(p));
}
};
enum class SfxPopupAction
{
DELETE,
HIDE,
SHOW
};
class SfxBindings_Impl
{
public:
css::uno::Reference< css::frame::XDispatchRecorder > xRecorder;
css::uno::Reference< css::frame::XDispatchProvider > xProv;
SfxWorkWindow* pWorkWin;
SfxBindings* pSubBindings;
SfxBindings* pSuperBindings;
SfxStateCacheArr_Impl* pCaches; // One chache for each binding
sal_uInt16 nCachedFunc1; // index for the last one called
sal_uInt16 nCachedFunc2; // index for the second last called
sal_uInt16 nMsgPos; // Message-Position relative the one to be updated
SfxPopupAction ePopupAction; // Checked in DeleteFloatinWindow()
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; // for volatile Slots
bool bInUpdate; // for Assertions
bool bInNextJob; // for Assertions
bool bFirstRound; // First round in Update
sal_uInt16 nFirstShell; // Shell, the first round is preferred
sal_uInt16 nOwnRegLevel; // Counts the real Locks, except those of the Super Bindings
InvalidateSlotMap 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->ePopupAction = SfxPopupAction::DELETE;
pImpl->nCachedFunc1 = 0;
pImpl->nCachedFunc2 = 0;
pImpl->bCtrlReleased = false;
pImpl->bFirstRound = false;
pImpl->bInNextJob = false;
pImpl->bInUpdate = false;
pImpl->pSubBindings = nullptr;
pImpl->pSuperBindings = nullptr;
pImpl->pWorkWin = nullptr;
pImpl->nOwnRegLevel = nRegLevel;
// all caches are valid (no pending invalidate-job)
// create the list of caches
pImpl->pCaches = new SfxStateCacheArr_Impl;
pImpl->aAutoTimer.SetInvokeHandler( LINK(this, SfxBindings, NextJob) );
pImpl->aAutoTimer.SetDebugName( "sfx::SfxBindings aAutoTimer" );
}
SfxBindings::~SfxBindings()
/* [Description]
Destructor of the SfxBindings class. The one, for each <SfxApplication>
existing Instance is automatically destroyed by the <SfxApplication>
after the execution of <SfxApplication::Exit()>.
The still existing <SfxControllerItem> 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
for(SfxStateCacheArr_Impl::const_iterator it = pImpl->pCaches->begin(); it != pImpl->pCaches->end(); ++it)
delete *it;
DELETEZ( pImpl->pWorkWin );
delete pImpl->pCaches;
}
void SfxBindings::DeleteControllers_Impl()
{
// in the first round delete SfxPopupWindows
sal_uInt16 nCount = pImpl->pCaches->size();
sal_uInt16 nCache;
for ( nCache = 0; nCache < nCount; ++nCache )
{
// Remember were you are
SfxStateCache *pCache = (*pImpl->pCaches)[nCache];
sal_uInt16 nSlotId = pCache->GetId();
// Re-align, because the cache may have been reduced
sal_uInt16 nNewCount = pImpl->pCaches->size();
if ( nNewCount < nCount )
{
nCache = GetSlotPos(nSlotId);
if ( nCache >= nNewCount ||
nSlotId != (*pImpl->pCaches)[nCache]->GetId() )
--nCache;
nCount = nNewCount;
}
}
// Delete all Caches
for ( nCache = pImpl->pCaches->size(); nCache > 0; --nCache )
{
// Get Cache via css::sdbcx::Index
SfxStateCache *pCache = (*pImpl->pCaches)[ nCache-1 ];
// 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
if( nCache-1 < (sal_uInt16) pImpl->pCaches->size() )
delete (*pImpl->pCaches)[nCache-1];
pImpl->pCaches->erase(pImpl->pCaches->begin()+ nCache - 1);
}
}
void SfxBindings::HidePopups( bool bHide )
{
// Hide SfxPopupWindows
HidePopupCtrls_Impl( bHide );
SfxBindings *pSub = pImpl->pSubBindings;
while ( pSub )
{
pImpl->pSubBindings->HidePopupCtrls_Impl( bHide );
pSub = pSub->pImpl->pSubBindings;
}
// Hide SfxChildWindows
DBG_ASSERT( pDispatcher, "HidePopups not allowed without dispatcher" );
if ( pImpl->pWorkWin )
pImpl->pWorkWin->HidePopups_Impl( bHide, true );
}
void SfxBindings::HidePopupCtrls_Impl( bool bHide )
{
if ( bHide )
{
// Hide SfxPopupWindows
pImpl->ePopupAction = SfxPopupAction::HIDE;
}
else
{
// Show SfxPopupWindows
pImpl->ePopupAction = SfxPopupAction::SHOW;
}
pImpl->ePopupAction = SfxPopupAction::DELETE;
}
void SfxBindings::Update_Impl
(
SfxStateCache* pCache // The up to date SfxStatusCache
)
{
if( pCache->GetDispatch().is() && pCache->GetItemLink() )
{
pCache->SetCachedState(true);
if ( !pCache->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;
SfxItemSet *pSet = CreateSet_Impl( pCache, pRealSlot, &pMsgServer, aFound );
bool bUpdated = false;
if ( pSet )
{
// Query Status
if ( rDispat.FillState_( *pMsgServer, *pSet, pRealSlot ) )
{
// Post Status
const SfxInterface *pInterface =
rDispat.GetShell(pMsgServer->GetShellLevel())->GetInterface();
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( pInterface, aFound[nPos], pItem, eState );
}
bUpdated = true;
}
delete pSet;
}
if ( !bUpdated && pCache )
{
// When pCache == NULL and no SlotServer
// (for example due to locked Dispatcher! ),
// obviously do not try to update
SfxFoundCache_Impl aFoundCache(0, pRealSlot, pCache );
UpdateControllers_Impl( nullptr, aFoundCache, nullptr, SfxItemState::DISABLED);
}
}
void SfxBindings::InvalidateSlotsInMap_Impl()
{
InvalidateSlotMap::const_iterator pIter = pImpl->m_aInvalidateSlots.begin();
while ( pIter != pImpl->m_aInvalidateSlots.end() )
{
Invalidate( pIter->first );
++pIter;
}
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
)
{
DBG_ASSERT( pImpl->pCaches != nullptr, "SfxBindings not initialized" );
if ( pDispatcher )
pDispatcher->Flush();
if ( pImpl->pSubBindings )
pImpl->pSubBindings->Update( nId );
SfxStateCache* pCache = GetStateCache( nId );
if ( pCache )
{
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()
{
DBG_ASSERT( pImpl->pCaches != nullptr, "SfxBindings not initialized" );
if ( pImpl->pSubBindings )
pImpl->pSubBindings->Update();
if ( pDispatcher )
{
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.FirstItem();
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.FirstItem();
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 */
sal_uInt16* 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. */
)
{
DBG_ASSERT( pImpl->pCaches != nullptr, "SfxBindings not initialized" );
// is the specified function bound?
const sal_uInt16 nStart = ( pPos ? *pPos : 0 );
const sal_uInt16 nPos = GetSlotPos( nId, nStart );
if ( nPos < pImpl->pCaches->size() &&
(*pImpl->pCaches)[nPos]->GetId() == nId )
{
if ( pPos )
*pPos = nPos;
return (*pImpl->pCaches)[nPos];
}
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 (SfxStateCache* 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 ( sal_uInt16 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];
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, /* <SfxShell> 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 )
{
for (SfxStateCache* 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;
pImpl->nFirstShell = nLevel;
}
}
}
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 )
{
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();
}
}
}
bool SfxBindings::IsBound( sal_uInt16 nSlotId )
{
DBG_ASSERT( pImpl->pCaches != nullptr, "SfxBindings not initialized" );
sal_uInt16 nStartSearchAt = 0;
return GetStateCache(nSlotId, &nStartSearchAt ) != nullptr;
}
sal_uInt16 SfxBindings::GetSlotPos( sal_uInt16 nId, sal_uInt16 nStartSearchAt )
{
DBG_ASSERT( pImpl->pCaches != nullptr, "SfxBindings not initialized" );
// 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
sal_uInt16 nTemp = pImpl->nCachedFunc1;
pImpl->nCachedFunc1 = pImpl->nCachedFunc2;
pImpl->nCachedFunc2 = nTemp;
return pImpl->nCachedFunc1;
}
// binary search, if not found, seek to target-position
if ( pImpl->pCaches->size() <= nStartSearchAt )
{
return 0;
}
if ( (sal_uInt16) pImpl->pCaches->size() == (nStartSearchAt+1) )
{
return (*pImpl->pCaches)[nStartSearchAt]->GetId() >= nId ? 0 : 1;
}
sal_uInt16 nLow = nStartSearchAt;
sal_uInt16 nMid = 0;
sal_uInt16 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 = (int) nId - (int) ( ((*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;
}
sal_uInt16 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) >= (sal_uInt16) 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();
sal_uInt16 nPos = GetSlotPos(nId);
if ( nPos >= pImpl->pCaches->size() ||
(*pImpl->pCaches)[nPos]->GetId() != nId )
{
SfxStateCache* pCache = new SfxStateCache(nId);
pImpl->pCaches->insert( pImpl->pCaches->begin() + nPos, pCache );
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->pCaches != nullptr, "SfxBindings not initialized" );
DBG_ASSERT( !pImpl->bInNextJob, "SfxBindings::Release while status-updating" );
ENTERREGISTRATIONS();
// find the bound function
sal_uInt16 nId = rItem.GetId();
sal_uInt16 nPos = GetSlotPos(nId);
SfxStateCache* pCache = (nPos < pImpl->pCaches->size()) ? (*pImpl->pCaches)[nPos] : 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();
}
const SfxPoolItem* SfxBindings::ExecuteSynchron( sal_uInt16 nId, const SfxPoolItem** ppItems )
{
DBG_ASSERT( pImpl->pCaches != nullptr, "SfxBindings not initialized" );
if( !nId || !pDispatcher )
return nullptr;
return Execute_Impl( nId, ppItems, 0, SfxCallMode::SYNCHRON, nullptr );
}
bool SfxBindings::Execute( sal_uInt16 nId, const SfxPoolItem** ppItems, SfxCallMode nCallMode )
{
DBG_ASSERT( pImpl->pCaches != nullptr, "SfxBindings not initialized" );
if( !nId || !pDispatcher )
return false;
const SfxPoolItem* pRet = Execute_Impl( nId, ppItems, 0, nCallMode, nullptr );
return ( pRet != nullptr );
}
const SfxPoolItem* SfxBindings::Execute_Impl( sal_uInt16 nId, const SfxPoolItem** ppItems, sal_uInt16 nModi, SfxCallMode nCallMode,
const SfxPoolItem **ppInternalArgs, bool bGlobalOnly )
{
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, bGlobalOnly );
pBind = pBind->pImpl->pSubBindings;
}
}
SfxDispatcher &rDispatcher = *pDispatcher;
rDispatcher.Flush();
// get SlotServer (Slot+ShellLevel) and Shell from cache
std::unique_ptr<SfxStateCache> 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 );
}
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 *pPool;
if ( eRet == css::frame::DispatchResultState::DONTKNOW )
pPool = new SfxVoidItem( nId );
else
pPool = new SfxBoolItem( nId, eRet == css::frame::DispatchResultState::SUCCESS);
DeleteItemOnIdle( pPool );
return pPool;
}
// 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 nullptr;
}
else
{
pShell = rDispatcher.GetShell( pServer->GetShellLevel() );
pSlot = pServer->GetSlot();
}
if ( bGlobalOnly )
if ( dynamic_cast< const SfxModule *>( pShell ) == nullptr && dynamic_cast< const SfxApplication *>( pShell ) == nullptr && dynamic_cast< const SfxViewFrame *>( pShell ) == nullptr )
return nullptr;
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 SfxPoolItem* pRet = aReq.GetReturnValue();
if ( !pRet )
{
SfxPoolItem *pVoid = new SfxVoidItem( nId );
DeleteItemOnIdle( pVoid );
pRet = pVoid;
}
return pRet;
}
void SfxBindings::Execute_Impl( SfxRequest& aReq, const SfxSlot* pSlot, SfxShell* pShell )
{
SfxItemPool &rPool = pShell->GetPool();
if ( SfxSlotKind::Enum == pSlot->GetKind() )
{
// for Enum-Slots, the Master has to be executed with the value
// of the enums
const SfxSlot *pRealSlot = pShell->GetInterface()->GetRealSlot(pSlot);
const sal_uInt16 nSlotId = pRealSlot->GetSlotId();
aReq.SetSlot( nSlotId );
aReq.AppendItem( SfxAllEnumItem( rPool.GetWhich(nSlotId), pSlot->GetValue() ) );
pDispatcher->Execute_( *pShell, *pRealSlot, aReq, aReq.GetCallMode() | SfxCallMode::RECORD );
}
else 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 aFunc = pSlot->GetStateFnc();
pShell->CallState( aFunc, 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 ( dynamic_cast< const SfxBoolItem *>( pOldItem ) != nullptr )
{
// we can toggle Bools
bool bOldValue = static_cast<const SfxBoolItem *>(pOldItem)->GetValue();
SfxBoolItem *pNewItem = static_cast<SfxBoolItem*>(pOldItem->Clone());
pNewItem->SetValue( !bOldValue );
aReq.AppendItem( *pNewItem );
delete pNewItem;
}
else if ( dynamic_cast< const SfxEnumItemInterface *>( pOldItem ) != nullptr &&
static_cast<const SfxEnumItemInterface *>(pOldItem)->HasBoolValue())
{
// and Enums with Bool-Interface
SfxEnumItemInterface *pNewItem =
static_cast<SfxEnumItemInterface*>(pOldItem->Clone());
pNewItem->SetBoolValue(!static_cast<const SfxEnumItemInterface *>(pOldItem)->GetBoolValue());
aReq.AppendItem( *pNewItem );
delete pNewItem;
}
else {
OSL_FAIL( "Toggle only for Enums and Bools allowed" );
}
}
else if ( SfxItemState::DONTCARE == eState )
{
// Create one Status-Item for each Factory
SfxPoolItem *pNewItem = pSlot->GetType()->CreateItem();
DBG_ASSERT( pNewItem, "Toggle to slot without ItemFactory" );
pNewItem->SetWhich( nWhich );
if ( dynamic_cast< const SfxBoolItem *>( pNewItem ) != nullptr )
{
// we can toggle Bools
static_cast<SfxBoolItem*>(pNewItem)->SetValue( true );
aReq.AppendItem( *pNewItem );
}
else if ( dynamic_cast< const SfxEnumItemInterface *>( pNewItem ) != nullptr &&
static_cast<SfxEnumItemInterface *>(pNewItem)->HasBoolValue())
{
// and Enums with Bool-Interface
static_cast<SfxEnumItemInterface*>(pNewItem)->SetBoolValue(true);
aReq.AppendItem( *pNewItem );
}
else {
OSL_FAIL( "Toggle only for Enums and Bools allowed" );
}
delete pNewItem;
}
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()
{
DBG_ASSERT( pImpl->pCaches != nullptr, "SfxBindings not initialized" );
// synchronize
pDispatcher->Flush();
if ( pImpl->bAllMsgDirty )
{
if ( !nRegLevel )
{
css::uno::Reference < css::frame::XFrame > xFrame
( pDispatcher->GetFrame()->GetFrame().GetFrameInterface(), UNO_QUERY );
pImpl->bContextChanged = false;
}
else
pImpl->bContextChanged = true;
}
for (SfxStateCache* pCache : *pImpl->pCaches)
{
//GetSlotServer can modify pImpl->pCaches
pCache->GetSlotServer(*pDispatcher, pImpl->xProv);
}
pImpl->bMsgDirty = pImpl->bAllMsgDirty = false;
Broadcast( SfxHint(SfxHintId::DocChanged) );
}
SfxItemSet* SfxBindings::CreateSet_Impl
(
SfxStateCache*& pCache, // 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->pCaches != nullptr, "SfxBindings not initialized" );
DBG_ASSERT( !pImpl->bMsgDirty, "CreateSet_Impl with dirty MessageServer" );
assert(pDispatcher);
const SfxSlotServer* pMsgSvr = pCache->GetSlotServer(*pDispatcher, pImpl->xProv);
if (!pMsgSvr)
return nullptr;
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 nullptr;
SfxItemPool &rPool = pShell->GetPool();
// get the status method, which is served by the pCache
SfxStateFunc pFnc = nullptr;
const SfxInterface *pInterface = pShell->GetInterface();
if ( SfxSlotKind::Enum == pMsgSvr->GetSlot()->GetKind() )
{
pRealSlot = pInterface->GetRealSlot(pMsgSvr->GetSlot());
pCache = GetStateCache( pRealSlot->GetSlotId() );
}
else
pRealSlot = pMsgSvr->GetSlot();
// Note: pCache can be NULL!
pFnc = pRealSlot->GetStateFnc();
// the RealSlot is always on
SfxFoundCache_Impl *pFound = new SfxFoundCache_Impl(
pRealSlot->GetWhich(rPool), pRealSlot, pCache );
rFound.push_back( pFound );
// 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
sal_uInt16 nCachePos = pImpl->nMsgPos;
const SfxSlot *pSibling = pRealSlot->GetNextSlot();
// the Slots ODF a interfaces ar 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 the slot is a non-dirty master slot, then maybe one of his slaves
// is dirty? Then the master slot is still inserted.
if ( !bInsert && bSameMethod && pSibling->GetLinkedSlot() )
{
// Also check slave slots for Binding
const SfxSlot* pFirstSlave = pSibling->GetLinkedSlot();
for ( const SfxSlot *pSlaveSlot = pFirstSlave;
!bInsert;
pSlaveSlot = pSlaveSlot->GetNextSlot())
{
// the slaves points to its master
DBG_ASSERT(pSlaveSlot->GetLinkedSlot() == pSibling,
"Wrong Master/Slave relationship!");
sal_uInt16 nCurMsgPos = pImpl->nMsgPos;
const SfxStateCache *pSlaveCache =
GetStateCache( pSlaveSlot->GetSlotId(), &nCurMsgPos );
// Is the slave slot chached and dirty ?
bInsert = pSlaveCache && pSlaveCache->IsControllerDirty();
// Slaves are chained together in a circle
if (pSlaveSlot->GetNextSlot() == pFirstSlave)
break;
}
}
if ( bInsert && bSameMethod )
{
SfxFoundCache_Impl *pFoundCache = new SfxFoundCache_Impl(
pSibling->GetWhich(rPool),
pSibling, pSiblingCache );
rFound.push_back( pFoundCache );
}
pSibling = pSibling->GetNextSlot();
}
// Create a Set from the ranges
std::unique_ptr<sal_uInt16[]> pRanges(new sal_uInt16[rFound.size() * 2 + 1]);
int j = 0;
sal_uInt16 i = 0;
while ( i < rFound.size() )
{
pRanges[j++] = rFound[i].nWhichId;
// consecutive numbers
for ( ; i < rFound.size()-1; ++i )
if ( rFound[i].nWhichId+1 != rFound[i+1].nWhichId )
break;
pRanges[j++] = rFound[i++].nWhichId;
}
pRanges[j] = 0; // terminating NULL
SfxItemSet *pSet = new SfxItemSet(rPool, pRanges.get());
pRanges.reset();
return pSet;
}
void SfxBindings::UpdateControllers_Impl
(
const SfxInterface* pIF, // Id of the current serving Interface
const SfxFoundCache_Impl& rFound, // Cache, Slot, Which etc.
const SfxPoolItem* pItem, // item to send to controller
SfxItemState eState // state of item
)
{
DBG_ASSERT( !rFound.pSlot || SfxSlotKind::Enum != rFound.pSlot->GetKind(),
"direct update of enum slot isn't allowed" );
SfxStateCache* pCache = rFound.pCache;
const SfxSlot* pSlot = rFound.pSlot;
DBG_ASSERT( !pCache || !pSlot || pCache->GetId() == pSlot->GetSlotId(), "SID mismatch" );
// bound until now, the Controller to update the Slot.
if ( pCache && pCache->IsControllerDirty() )
{
if ( SfxItemState::DONTCARE == eState )
{
// ambiguous
pCache->SetState( SfxItemState::DONTCARE, INVALID_POOL_ITEM );
}
else if ( SfxItemState::DEFAULT == eState &&
SfxItemPool::IsSlot(rFound.nWhichId) )
{
// no Status or Default but without Pool
SfxVoidItem aVoid(0);
pCache->SetState( SfxItemState::UNKNOWN, &aVoid );
}
else if ( SfxItemState::DISABLED == eState )
pCache->SetState(SfxItemState::DISABLED, nullptr);
else
pCache->SetState(SfxItemState::DEFAULT, pItem);
}
// Update the slots for so far available and bound Controllers for
// Slave-Slots (Enum-value)
DBG_ASSERT( !pSlot || nullptr == pSlot->GetLinkedSlot() || !pItem ||
dynamic_cast< const SfxEnumItemInterface *>( pItem ) != nullptr,
"master slot with non-enum-type found" );
const SfxSlot *pFirstSlave = pSlot ? pSlot->GetLinkedSlot() : nullptr;
if ( pIF && pFirstSlave)
{
// Items cast on EnumItem
const SfxEnumItemInterface *pEnumItem = dynamic_cast< const SfxEnumItemInterface* >(pItem);
if ( eState == SfxItemState::DEFAULT && !pEnumItem )
eState = SfxItemState::DONTCARE;
else
eState = SfxControllerItem::GetItemState( pEnumItem );
// Iterate over all Slaves-Slots
for ( const SfxSlot *pSlave = pFirstSlave; pSlave; pSlave = pSlave->GetNextSlot() )
{
DBG_ASSERT(pSlave, "Wrong SlaveSlot binding!");
DBG_ASSERT(SfxSlotKind::Enum == pSlave->GetKind(),"non enum slaves aren't allowed");
DBG_ASSERT(pSlave->GetMasterSlotId() == pSlot->GetSlotId(),"Wrong MasterSlot!");
// Binding exist for function ?
SfxStateCache *pEnumCache = GetStateCache( pSlave->GetSlotId() );
if ( pEnumCache )
{
pEnumCache->Invalidate(false);
// HACK(CONTROL/SELECT Kram) ???
if ( eState == SfxItemState::DONTCARE && rFound.nWhichId == 10144 )
{
SfxVoidItem aVoid(0);
pEnumCache->SetState( SfxItemState::UNKNOWN, &aVoid );
if (pSlave->GetNextSlot() == pFirstSlave)
break;
continue;
}
if ( SfxItemState::DISABLED == eState || (pEnumItem && !pEnumItem->IsEnabled( pSlave->GetSlotId())) )
{
// disabled
pEnumCache->SetState(SfxItemState::DISABLED, nullptr);
}
else if ( SfxItemState::DEFAULT == eState && pEnumItem )
{
// Determine enum value
sal_uInt16 nValue = pEnumItem->GetEnumValue();
SfxBoolItem aBool( rFound.nWhichId, pSlave->GetValue() == nValue );
pEnumCache->SetState(SfxItemState::DEFAULT, &aBool);
}
else
{
// ambiguous
pEnumCache->SetState( SfxItemState::DONTCARE, INVALID_POOL_ITEM );
}
}
if (pSlave->GetNextSlot() == pFirstSlave)
break;
}
}
}
IMPL_LINK( SfxBindings, NextJob, Timer *, pTimer, void )
{
NextJob_Impl(pTimer);
}
bool SfxBindings::NextJob_Impl(Timer * pTimer)
{
const unsigned MAX_INPUT_DELAY = 200;
DBG_ASSERT( pImpl->pCaches != nullptr, "SfxBindings not initialized" );
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 && !pSfxApp->Get_Impl()->nInReschedule;
sal_uInt16 nLoops = 10;
pImpl->bInNextJob = true;
const sal_uInt16 nCount = pImpl->pCaches->size();
while ( pImpl->nMsgPos < nCount )
{
// iterate through the bound functions
bool bJobDone = false;
while ( !bJobDone )
{
SfxStateCache* pCache = (*pImpl->pCaches)[pImpl->nMsgPos];
DBG_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(const char *pFile, int nLine)
{
SAL_INFO(
"sfx.control",
std::setw(std::min(nRegLevel, sal_uInt16(8))) << ' ' << "this = " << this
<< " Level = " << nRegLevel << " SfxBindings::EnterRegistrations "
<< (pFile
? 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( const char *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()->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];
// 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);
delete pCache;
}
}
}
// restart background-processing
pImpl->nMsgPos = 0;
if ( !pFrame || !pFrame->GetObjectShell() )
return;
if ( pImpl->pCaches && !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
? SAL_STREAM("File: " << pFile << " Line: " << nLine) : ""));
}
void SfxBindings::SetDispatcher( SfxDispatcher *pDisp )
{
SfxDispatcher *pOldDispat = pDispatcher;
if ( pDisp != pDispatcher )
{
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 )
{
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<SfxPoolItem> &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->pUnoName )
return SfxItemState::DISABLED;
css::util::URL aURL;
OUString aCmd( ".uno:" );
aURL.Protocol = aCmd;
aURL.Path = OUString::createFromAscii(pSlot->GetUnoName());
aCmd += aURL.Path;
aURL.Complete = aCmd;
aURL.Main = aCmd;
if ( !xDisp.is() )
xDisp = pImpl->xProv->queryDispatch( aURL, OUString(), 0 );
if ( xDisp.is() )
{
css::uno::Reference< css::lang::XUnoTunnel > xTunnel( xDisp, css::uno::UNO_QUERY );
SfxOfficeDispatch* pDisp = nullptr;
if ( xTunnel.is() )
{
sal_Int64 nImplementation = xTunnel->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier());
pDisp = reinterpret_cast< SfxOfficeDispatch* >( sal::static_int_cast< sal_IntPtr >( nImplementation ));
}
if ( !pDisp )
{
bool bDeleteCache = false;
if ( !pCache )
{
pCache = new SfxStateCache( nSlot );
pCache->GetSlotServer( *GetDispatcher_Impl(), pImpl->xProv );
bDeleteCache = true;
}
SfxItemState eState = SfxItemState::SET;
BindDispatch_Impl *pBind = new BindDispatch_Impl( xDisp, aURL, pCache, pSlot );
pBind->acquire();
xDisp->addStatusListener( pBind, aURL );
if ( !pBind->GetStatus().IsEnabled )
{
eState = SfxItemState::DISABLED;
}
else
{
css::uno::Any aAny = pBind->GetStatus().State;
css::uno::Type aType = aAny.getValueType();
if ( aType == cppu::UnoType<bool>::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<sal_uInt32>::get() )
{
sal_uInt32 nTemp = 0;
aAny >>= nTemp ;
rpState.reset(new SfxUInt32Item( nSlot, nTemp ));
}
else if ( aType == cppu::UnoType<OUString>::get() )
{
OUString sTemp ;
aAny >>= sTemp ;
rpState.reset(new SfxStringItem( nSlot, sTemp ));
}
else
rpState.reset(new SfxVoidItem( nSlot ));
}
xDisp->removeStatusListener( pBind, aURL );
pBind->Release();
if ( bDeleteCache )
DELETEZ( pCache );
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.
const SfxPoolItem *pItem = nullptr;
SfxItemState eState = pDispatcher->QueryState( nSlot, pItem );
if ( eState == SfxItemState::SET )
{
DBG_ASSERT( pItem, "SfxItemState::SET but no item!" );
if ( pItem )
rpState.reset(pItem->Clone());
}
else if ( eState == SfxItemState::DEFAULT && pItem )
{
rpState.reset(pItem->Clone());
}
return eState;
}
void SfxBindings::SetSubBindings_Impl( SfxBindings *pSub )
{
if ( pImpl->pSubBindings )
{
pImpl->pSubBindings->SetDispatchProvider_Impl( css::uno::Reference< css::frame::XDispatchProvider > () );
pImpl->pSubBindings->pImpl->pSuperBindings = nullptr;
}
pImpl->pSubBindings = pSub;
if ( pSub )
{
pImpl->pSubBindings->SetDispatchProvider_Impl( pImpl->xProv );
pSub->pImpl->pSuperBindings = this;
}
}
SfxBindings* SfxBindings::GetSubBindings_Impl() const
{
return pImpl->pSubBindings;
}
void SfxBindings::SetWorkWindow_Impl( SfxWorkWindow* pWork )
{
pImpl->pWorkWin = pWork;
}
SfxWorkWindow* SfxBindings::GetWorkWindow_Impl() const
{
return pImpl->pWorkWin;
}
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 )
{
css::uno::Reference< css::frame::XDispatch > xDisp;
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 ) );
}
const 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 >& 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;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */