office-gobmx/sfx2/source/control/bindings.cxx
Gabor Kelemen 144057a54a Recheck modules s[f-t]* with IWYU
See tdf#42949 for motivation

Change-Id: I1f520aad1b1c942ad5616d96851016fc366ac58f
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/130203
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
2022-03-01 08:21:00 +01:00

1783 lines
54 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 <comphelper/servicehelper.hxx>
#include <osl/diagnose.h>
#include <sal/log.hxx>
#include <svl/itempool.hxx>
#include <svl/itemiter.hxx>
#include <svl/eitem.hxx>
#include <svl/intitem.hxx>
#include <svl/stritem.hxx>
#include <vcl/svapp.hxx>
#include <vcl/timer.hxx>
#include <com/sun/star/frame/XDispatch.hpp>
#include <com/sun/star/frame/XDispatchProvider.hpp>
#include <com/sun/star/frame/DispatchResultState.hpp>
#include <itemdel.hxx>
//Includes below due to nInReschedule
#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/module.hxx>
#include <sfx2/request.hxx>
#include <workwin.hxx>
#include <unoctitm.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/msgpool.hxx>
#include <cstddef>
#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>
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 <Master-Slot>
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<SfxFoundCache_Impl> 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<SfxWorkWindow> mxWorkWin;
SfxBindings* pSubBindings;
std::vector<std::unique_ptr<SfxStateCache>> 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.SetInvokeHandler( LINK(this, SfxBindings, NextJob) );
}
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
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;
for ( nCache = 0; nCache < nCount; ++nCache )
{
// 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);
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 ].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<SfxItemSet> 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<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 ( 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, /* <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 )
return;
for (std::unique_ptr<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;
}
}
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<int>(nId) - static_cast<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;
}
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<SfxStateCache>(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();
}
const SfxPoolItem* SfxBindings::ExecuteSynchron( sal_uInt16 nId, const SfxPoolItem** ppItems )
{
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 )
{
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 );
std::unique_ptr<SfxPoolItem> pPool;
if ( eRet == css::frame::DispatchResultState::DONTKNOW )
pPool.reset( new SfxVoidItem( nId ) );
else
pPool.reset( new SfxBoolItem( nId, eRet == css::frame::DispatchResultState::SUCCESS) );
auto pTemp = pPool.get();
DeleteItemOnIdle( std::move(pPool) );
return pTemp;
}
// 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 )
{
std::unique_ptr<SfxPoolItem> pVoid(new SfxVoidItem( nId ));
pRet = pVoid.get();
DeleteItemOnIdle( std::move(pVoid) );
}
return pRet;
}
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<SfxBoolItem> pNewItem(static_cast<SfxBoolItem*>(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<SfxEnumItemInterface> pNewItem(
static_cast<SfxEnumItemInterface*>(pOldEnumItem->Clone()));
pNewItem->SetBoolValue(!pOldEnumItem->GetBoolValue());
aReq.AppendItem( *pNewItem );
}
}
else {
OSL_FAIL( "Toggle only for Enums and Bools allowed" );
}
}
else if ( SfxItemState::DONTCARE == eState )
{
// Create one Status-Item for each Factory
std::unique_ptr<SfxPoolItem> pNewItem = pSlot->GetType()->CreateItem();
DBG_ASSERT( pNewItem, "Toggle to slot without ItemFactory" );
pNewItem->SetWhich( nWhich );
if ( auto pNewBoolItem = dynamic_cast<SfxBoolItem *>( pNewItem.get() ) )
{
// we can toggle Bools
pNewBoolItem->SetValue( true );
aReq.AppendItem( *pNewItem );
}
else if ( auto pEnumItem = dynamic_cast<SfxEnumItemInterface *>( 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<SfxItemSet> 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::DONTCARE == eState )
{
// ambiguous
rCache.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);
rCache.SetState( SfxItemState::UNKNOWN, &aVoid );
}
else if ( SfxItemState::DISABLED == eState )
rCache.SetState(SfxItemState::DISABLED, nullptr);
else
rCache.SetState(SfxItemState::DEFAULT, pItem);
}
IMPL_LINK( SfxBindings, NextJob, Timer *, pTimer, void )
{
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();
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() && !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
? 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<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() )
{
if (!comphelper::getFromUnoTunnel<SfxOfficeDispatch>(xDisp))
{
bool bDeleteCache = false;
if ( !pCache )
{
pCache = new SfxStateCache( nSlot );
pCache->GetSlotServer( *GetDispatcher_Impl(), pImpl->xProv );
bDeleteCache = true;
}
SfxItemState eState = SfxItemState::SET;
rtl::Reference<BindDispatch_Impl> 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<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( 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.
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::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<css::frame::XDispatch> xDispatch =
pImpl->xProv->queryDispatch(aURL, OUString(), 0);
if (!xDispatch.is())
return 0;
css::uno::Reference<css::lang::XUnoTunnel> xTunnel(xDispatch, css::uno::UNO_QUERY);
if (!xTunnel.is())
return 0;
sal_Int64 nHandle = xTunnel->getSomething(SfxOfficeDispatch::getUnoTunnelId());
if (!nHandle)
return 0;
SfxOfficeDispatch* pDispatch = reinterpret_cast<SfxOfficeDispatch*>(sal::static_int_cast<sal_IntPtr>(nHandle));
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<SfxWorkWindow> 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;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */