summaryrefslogtreecommitdiffstats
path: root/sfx2/source/control/statcach.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2/source/control/statcach.cxx')
-rw-r--r--sfx2/source/control/statcach.cxx503
1 files changed, 503 insertions, 0 deletions
diff --git a/sfx2/source/control/statcach.cxx b/sfx2/source/control/statcach.cxx
new file mode 100644
index 000000000..ef1eaf1a1
--- /dev/null
+++ b/sfx2/source/control/statcach.cxx
@@ -0,0 +1,503 @@
+/* -*- 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 .
+ */
+
+
+#ifdef __sun
+#include <ctime>
+#endif
+
+#include <framework/dispatchhelper.hxx>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/visitem.hxx>
+
+#include <sfx2/app.hxx>
+#include <statcach.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/ctrlitem.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <unoctitm.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+
+BindDispatch_Impl::BindDispatch_Impl( const css::uno::Reference< css::frame::XDispatch > & rDisp, const css::util::URL& rURL, SfxStateCache *pStateCache, const SfxSlot* pS )
+ : xDisp( rDisp )
+ , aURL( rURL )
+ , pCache( pStateCache )
+ , pSlot( pS )
+{
+ DBG_ASSERT( pCache && pSlot, "Invalid BindDispatch!");
+ aStatus.IsEnabled = true;
+}
+
+void SAL_CALL BindDispatch_Impl::disposing( const css::lang::EventObject& )
+{
+ if ( xDisp.is() )
+ {
+ xDisp->removeStatusListener( static_cast<css::frame::XStatusListener*>(this), aURL );
+ xDisp.clear();
+ }
+}
+
+void SAL_CALL BindDispatch_Impl::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ aStatus = rEvent;
+ if ( !pCache )
+ return;
+
+ css::uno::Reference< css::frame::XStatusListener > xKeepAlive(this);
+ if ( aStatus.Requery )
+ pCache->Invalidate( true );
+ else
+ {
+ std::unique_ptr<SfxPoolItem> pItem;
+ sal_uInt16 nId = pCache->GetId();
+ SfxItemState eState = SfxItemState::DISABLED;
+ if ( !aStatus.IsEnabled )
+ {
+ // default
+ }
+ else if (aStatus.State.hasValue())
+ {
+ eState = SfxItemState::DEFAULT;
+ css::uno::Any aAny = aStatus.State;
+
+ const css::uno::Type& aType = aAny.getValueType();
+ if ( aType == cppu::UnoType< bool >::get() )
+ {
+ bool bTemp = false;
+ aAny >>= bTemp ;
+ pItem.reset( new SfxBoolItem( nId, bTemp ) );
+ }
+ else if ( aType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() )
+ {
+ sal_uInt16 nTemp = 0;
+ aAny >>= nTemp ;
+ pItem.reset( new SfxUInt16Item( nId, nTemp ) );
+ }
+ else if ( aType == cppu::UnoType<sal_uInt32>::get() )
+ {
+ sal_uInt32 nTemp = 0;
+ aAny >>= nTemp ;
+ pItem.reset( new SfxUInt32Item( nId, nTemp ) );
+ }
+ else if ( aType == cppu::UnoType<OUString>::get() )
+ {
+ OUString sTemp ;
+ aAny >>= sTemp ;
+ pItem.reset( new SfxStringItem( nId, sTemp ) );
+ }
+ else
+ {
+ if ( pSlot )
+ pItem = pSlot->GetType()->CreateItem();
+ if ( pItem )
+ {
+ pItem->SetWhich( nId );
+ pItem->PutValue( aAny, 0 );
+ }
+ else
+ pItem.reset( new SfxVoidItem( nId ) );
+ }
+ }
+ else
+ {
+ // DONTCARE status
+ pItem.reset( new SfxVoidItem(0) );
+ eState = SfxItemState::UNKNOWN;
+ }
+
+ for ( SfxControllerItem *pCtrl = pCache->GetItemLink();
+ pCtrl;
+ pCtrl = pCtrl->GetItemLink() )
+ pCtrl->StateChangedAtToolBoxControl( nId, eState, pItem.get() );
+ }
+}
+
+void BindDispatch_Impl::Release()
+{
+ if ( xDisp.is() )
+ {
+ try
+ {
+ xDisp->removeStatusListener(static_cast<css::frame::XStatusListener*>(this), aURL);
+ }
+ catch (const lang::DisposedException&)
+ {
+ TOOLS_WARN_EXCEPTION("sfx", "BindDispatch_Impl::Release: xDisp is disposed: ");
+ }
+ xDisp.clear();
+ }
+ pCache = nullptr;
+}
+
+
+sal_Int16 BindDispatch_Impl::Dispatch( const css::uno::Sequence < css::beans::PropertyValue >& aProps, bool bForceSynchron )
+{
+ sal_Int16 eRet = css::frame::DispatchResultState::DONTKNOW;
+
+ if ( xDisp.is() && aStatus.IsEnabled )
+ {
+ ::rtl::Reference< ::framework::DispatchHelper > xHelper( new ::framework::DispatchHelper(nullptr));
+ css::uno::Any aResult = xHelper->executeDispatch(xDisp, aURL, bForceSynchron, aProps);
+
+ css::frame::DispatchResultEvent aEvent;
+ aResult >>= aEvent;
+
+ eRet = aEvent.State;
+ }
+
+ return eRet;
+}
+
+
+// This constructor for an invalid cache that is updated in the first request.
+
+SfxStateCache::SfxStateCache( sal_uInt16 nFuncId ):
+ nId(nFuncId),
+ pInternalController(nullptr),
+ pController(nullptr),
+ pLastItem( nullptr ),
+ eLastState( SfxItemState::UNKNOWN ),
+ bItemVisible( true )
+{
+ bCtrlDirty = true;
+ bSlotDirty = true;
+ bItemDirty = true;
+}
+
+
+// The Destructor checks by assertion, even if controllers are registered.
+
+SfxStateCache::~SfxStateCache()
+{
+ DBG_ASSERT( pController == nullptr && pInternalController == nullptr, "there are still Controllers registered" );
+ if ( !IsInvalidItem(pLastItem) )
+ delete pLastItem;
+ if ( mxDispatch.is() )
+ mxDispatch->Release();
+}
+
+
+// invalidates the cache (next request will force update)
+void SfxStateCache::Invalidate( bool bWithMsg )
+{
+ bCtrlDirty = true;
+ if ( bWithMsg )
+ {
+ bSlotDirty = true;
+ aSlotServ.SetSlot( nullptr );
+ if ( mxDispatch.is() )
+ mxDispatch->Release();
+ mxDispatch.clear();
+ }
+}
+
+
+// gets the corresponding function from the dispatcher or the cache
+
+const SfxSlotServer* SfxStateCache::GetSlotServer( SfxDispatcher &rDispat , const css::uno::Reference< css::frame::XDispatchProvider > & xProv )
+{
+
+ if ( bSlotDirty )
+ {
+ // get the SlotServer; we need it for internal controllers anyway, but also in most cases
+ rDispat.FindServer_( nId, aSlotServ );
+
+ DBG_ASSERT( !mxDispatch.is(), "Old Dispatch not removed!" );
+
+ // we don't need to check the dispatch provider if we only have an internal controller
+ if ( xProv.is() )
+ {
+ const SfxSlot* pSlot = aSlotServ.GetSlot();
+ if ( !pSlot )
+ // get the slot - even if it is disabled on the dispatcher
+ pSlot = SfxSlotPool::GetSlotPool( rDispat.GetFrame() ).GetSlot( nId );
+
+ if ( !pSlot || !pSlot->pUnoName )
+ {
+ bSlotDirty = false;
+ bCtrlDirty = true;
+ return aSlotServ.GetSlot()? &aSlotServ: nullptr;
+ }
+
+ // create the dispatch URL from the slot data
+ 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;
+
+ // try to get a dispatch object for this command
+ css::uno::Reference< css::frame::XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
+ if ( xDisp.is() )
+ {
+ // test the dispatch object if it is just a wrapper for a SfxDispatcher
+ css::uno::Reference< css::lang::XUnoTunnel > xTunnel( xDisp, css::uno::UNO_QUERY );
+ if (auto pDisp = comphelper::getFromUnoTunnel<SfxOfficeDispatch>(xTunnel))
+ {
+ // The intercepting object is an SFX component
+ // If this dispatch object does not use the wanted dispatcher or the AppDispatcher, it's treated like any other UNO component
+ // (intercepting by internal dispatches)
+ SfxDispatcher *pDispatcher = pDisp->GetDispatcher_Impl();
+ if ( pDispatcher == &rDispat || pDispatcher == SfxGetpApp()->GetAppDispatcher_Impl() )
+ {
+ // so we can use it directly
+ bSlotDirty = false;
+ bCtrlDirty = true;
+ return aSlotServ.GetSlot()? &aSlotServ: nullptr;
+ }
+ }
+
+ // so the dispatch object isn't a SfxDispatcher wrapper or it is one, but it uses another dispatcher, but not rDispat
+ mxDispatch = new BindDispatch_Impl( xDisp, aURL, this, pSlot );
+
+ // flags must be set before adding StatusListener because the dispatch object will set the state
+ bSlotDirty = false;
+ bCtrlDirty = true;
+ xDisp->addStatusListener( mxDispatch, aURL );
+ }
+ else if ( rDispat.GetFrame() )
+ {
+ css::uno::Reference < css::frame::XDispatchProvider > xFrameProv(
+ rDispat.GetFrame()->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY );
+ if ( xFrameProv != xProv )
+ return GetSlotServer( rDispat, xFrameProv );
+ }
+ }
+
+ bSlotDirty = false;
+ bCtrlDirty = true;
+ }
+
+ // we *always* return a SlotServer (if there is one); but in case of an external dispatch we might not use it
+ // for the "real" (non internal) controllers
+ return aSlotServ.GetSlot()? &aSlotServ: nullptr;
+}
+
+
+// Set Status in all Controllers
+
+void SfxStateCache::SetState
+(
+ SfxItemState eState, // <SfxItemState> from 'pState'
+ const SfxPoolItem* pState, // Slot Status, 0 or -1
+ bool bMaybeDirty
+)
+
+/* [Description]
+
+ This method distributes the status of all of this SID bound
+ <SfxControllerItem>s. If the value is the same as before, and if neither
+ controller was registered nor invalidated inbetween, then no value is
+ passed. This way the flickering is for example avoided in ListBoxes.
+*/
+{
+ SetState_Impl( eState, pState, bMaybeDirty );
+}
+
+void SfxStateCache::GetState
+(
+ boost::property_tree::ptree& rState
+)
+{
+ if ( !mxDispatch.is() && pController )
+ {
+ for ( SfxControllerItem *pCtrl = pController;
+ pCtrl;
+ pCtrl = pCtrl->GetItemLink() )
+ pCtrl->GetControlState( nId, rState );
+ }
+}
+
+void SfxStateCache::SetVisibleState( bool bShow )
+{
+ if ( bShow == bItemVisible )
+ return;
+
+ SfxItemState eState( SfxItemState::DEFAULT );
+ const SfxPoolItem* pState( nullptr );
+ bool bDeleteItem( false );
+
+ bItemVisible = bShow;
+ if ( bShow )
+ {
+ if ( IsInvalidItem(pLastItem) || ( pLastItem == nullptr ))
+ {
+ pState = new SfxVoidItem( nId );
+ bDeleteItem = true;
+ }
+ else
+ pState = pLastItem;
+
+ eState = eLastState;
+ }
+ else
+ {
+ pState = new SfxVisibilityItem( nId, false );
+ bDeleteItem = true;
+ }
+
+ // Update Controller
+ if ( !mxDispatch.is() && pController )
+ {
+ for ( SfxControllerItem *pCtrl = pController;
+ pCtrl;
+ pCtrl = pCtrl->GetItemLink() )
+ pCtrl->StateChangedAtToolBoxControl( nId, eState, pState );
+ }
+
+ if ( pInternalController )
+ pInternalController->StateChangedAtToolBoxControl( nId, eState, pState );
+
+ if ( bDeleteItem )
+ delete pState;
+}
+
+
+void SfxStateCache::SetState_Impl
+(
+ SfxItemState eState, // <SfxItemState> from 'pState'
+ const SfxPoolItem* pState, // Slot Status, 0 or -1
+ bool bMaybeDirty
+)
+{
+ // If a hard update occurs between enter- and leave-registrations is a
+ // can also intermediate Cached exist without controller.
+ if ( !pController && !pInternalController )
+ return;
+
+ DBG_ASSERT( bMaybeDirty || !bSlotDirty, "setting state of dirty message" );
+ DBG_ASSERT( SfxControllerItem::GetItemState(pState) == eState, "invalid SfxItemState" );
+
+ // does the controller have to be notified at all?
+ bool bNotify = bItemDirty;
+ if ( !bItemDirty )
+ {
+ bool bBothAvailable = pLastItem && pState &&
+ !IsInvalidItem(pState) && !IsInvalidItem(pLastItem);
+ DBG_ASSERT( !bBothAvailable || pState != pLastItem, "setting state with own item" );
+ if ( bBothAvailable )
+ bNotify = typeid(*pState) != typeid(*pLastItem) ||
+ *pState != *pLastItem;
+ else
+ bNotify = ( pState != pLastItem ) || ( eState != eLastState );
+ }
+
+ if ( bNotify )
+ {
+ // Update Controller
+ if ( !mxDispatch.is() && pController )
+ {
+ for ( SfxControllerItem *pCtrl = pController;
+ pCtrl;
+ pCtrl = pCtrl->GetItemLink() )
+ pCtrl->StateChangedAtToolBoxControl( nId, eState, pState );
+ }
+
+ if ( pInternalController )
+ static_cast<SfxDispatchController_Impl *>(pInternalController)->StateChanged( nId, eState, pState, &aSlotServ );
+
+ // Remember new value
+ if ( !IsInvalidItem(pLastItem) )
+ {
+ delete pLastItem;
+ pLastItem = nullptr;
+ }
+ if ( pState && !IsInvalidItem(pState) )
+ pLastItem = pState->Clone();
+ else
+ pLastItem = nullptr;
+ eLastState = eState;
+ bItemDirty = false;
+ }
+
+ bCtrlDirty = false;
+}
+
+
+// Set old status again in all the controllers
+
+void SfxStateCache::SetCachedState( bool bAlways )
+{
+ DBG_ASSERT(pController==nullptr||pController->GetId()==nId, "Cache with wrong ControllerItem" );
+
+ // Only update if cached item exists and also able to process.
+ // (If the State is sent, it must be ensured that a SlotServer is present,
+ // see SfxControllerItem:: GetCoreMetric())
+ if ( !(bAlways || ( !bItemDirty && !bSlotDirty )) )
+ return;
+
+ // Update Controller
+ if ( !mxDispatch.is() && pController )
+ {
+ for ( SfxControllerItem *pCtrl = pController;
+ pCtrl;
+ pCtrl = pCtrl->GetItemLink() )
+ pCtrl->StateChangedAtToolBoxControl( nId, eLastState, pLastItem );
+ }
+
+ if ( pInternalController )
+ static_cast<SfxDispatchController_Impl *>(pInternalController)->StateChanged( nId, eLastState, pLastItem, &aSlotServ );
+
+ // Controller is now ok
+ bCtrlDirty = true;
+}
+
+
+css::uno::Reference< css::frame::XDispatch > SfxStateCache::GetDispatch() const
+{
+ if ( mxDispatch.is() )
+ return mxDispatch->xDisp;
+ return css::uno::Reference< css::frame::XDispatch > ();
+}
+
+sal_Int16 SfxStateCache::Dispatch( const SfxItemSet* pSet, bool bForceSynchron )
+{
+ // protect pDispatch against destruction in the call
+ rtl::Reference<BindDispatch_Impl> xKeepAlive( mxDispatch );
+ sal_Int16 eRet = css::frame::DispatchResultState::DONTKNOW;
+
+ if ( mxDispatch.is() )
+ {
+ uno::Sequence < beans::PropertyValue > aArgs;
+ if (pSet)
+ TransformItems( nId, *pSet, aArgs );
+
+ eRet = mxDispatch->Dispatch( aArgs, bForceSynchron );
+ }
+
+ return eRet;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */