| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sfx2.hxx" |
| |
| #ifdef SOLARIS |
| // HACK: prevent conflict between STLPORT and Workshop headers on Solaris 8 |
| #include <ctime> |
| #endif |
| |
| #include <string> // HACK: prevent conflict between STLPORT and Workshop headers |
| #include <com/sun/star/util/XURLTransformer.hpp> |
| #include <com/sun/star/frame/XController.hpp> |
| #include <com/sun/star/frame/XFrameActionListener.hpp> |
| #include <com/sun/star/frame/XComponentLoader.hpp> |
| #include <com/sun/star/frame/XFrame.hpp> |
| #include <com/sun/star/frame/FrameActionEvent.hpp> |
| #include <com/sun/star/frame/FrameAction.hpp> |
| #include <com/sun/star/beans/PropertyValue.hpp> |
| #include <cppuhelper/weak.hxx> |
| #include <svl/eitem.hxx> |
| #include <svl/intitem.hxx> |
| #include <svl/stritem.hxx> |
| #include <svl/visitem.hxx> |
| #include <comphelper/processfactory.hxx> |
| |
| #ifndef GCC |
| #endif |
| |
| #include <sfx2/app.hxx> |
| #include <sfx2/appuno.hxx> |
| #include "statcach.hxx" |
| #include <sfx2/msg.hxx> |
| #include <sfx2/ctrlitem.hxx> |
| #include <sfx2/dispatch.hxx> |
| #include "sfxtypes.hxx" |
| #include <sfx2/sfxuno.hxx> |
| #include <sfx2/unoctitm.hxx> |
| #include <sfx2/msgpool.hxx> |
| #include <sfx2/viewfrm.hxx> |
| |
| using namespace ::com::sun::star; |
| using namespace ::com::sun::star::uno; |
| using namespace ::com::sun::star::util; |
| |
| //==================================================================== |
| |
| DBG_NAME(SfxStateCache) |
| DBG_NAME(SfxStateCacheSetState) |
| |
| SFX_IMPL_XINTERFACE_2( BindDispatch_Impl, OWeakObject, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener ) |
| SFX_IMPL_XTYPEPROVIDER_2( BindDispatch_Impl, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener ) |
| |
| //----------------------------------------------------------------------------- |
| BindDispatch_Impl::BindDispatch_Impl( const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > & rDisp, const ::com::sun::star::util::URL& rURL, SfxStateCache *pStateCache, const SfxSlot* pS ) |
| : xDisp( rDisp ) |
| , aURL( rURL ) |
| , pCache( pStateCache ) |
| , pSlot( pS ) |
| { |
| DBG_ASSERT( pCache && pSlot, "Invalid BindDispatch!"); |
| aStatus.IsEnabled = sal_True; |
| } |
| |
| void SAL_CALL BindDispatch_Impl::disposing( const ::com::sun::star::lang::EventObject& ) throw( ::com::sun::star::uno::RuntimeException ) |
| { |
| if ( xDisp.is() ) |
| { |
| xDisp->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aURL ); |
| xDisp = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > (); |
| } |
| } |
| |
| void SAL_CALL BindDispatch_Impl::statusChanged( const ::com::sun::star::frame::FeatureStateEvent& rEvent ) throw( ::com::sun::star::uno::RuntimeException ) |
| { |
| aStatus = rEvent; |
| if ( !pCache ) |
| return; |
| |
| ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > xRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY ); |
| if ( aStatus.Requery ) |
| pCache->Invalidate( sal_True ); |
| else |
| { |
| SfxPoolItem *pItem=NULL; |
| sal_uInt16 nId = pCache->GetId(); |
| SfxItemState eState = SFX_ITEM_DISABLED; |
| // pCache->Invalidate( sal_False ); |
| if ( !aStatus.IsEnabled ) |
| { |
| // default |
| } |
| else if (aStatus.State.hasValue()) |
| { |
| eState = SFX_ITEM_AVAILABLE; |
| ::com::sun::star::uno::Any aAny = aStatus.State; |
| |
| ::com::sun::star::uno::Type pType = aAny.getValueType(); |
| if ( pType == ::getBooleanCppuType() ) |
| { |
| sal_Bool bTemp = false; |
| aAny >>= bTemp ; |
| pItem = new SfxBoolItem( nId, bTemp ); |
| } |
| else if ( pType == ::getCppuType((const sal_uInt16*)0) ) |
| { |
| sal_uInt16 nTemp = 0; |
| aAny >>= nTemp ; |
| pItem = new SfxUInt16Item( nId, nTemp ); |
| } |
| else if ( pType == ::getCppuType((const sal_uInt32*)0) ) |
| { |
| sal_uInt32 nTemp = 0; |
| aAny >>= nTemp ; |
| pItem = new SfxUInt32Item( nId, nTemp ); |
| } |
| else if ( pType == ::getCppuType((const ::rtl::OUString*)0) ) |
| { |
| ::rtl::OUString sTemp ; |
| aAny >>= sTemp ; |
| pItem = new SfxStringItem( nId, sTemp ); |
| } |
| else |
| { |
| if ( pSlot ) |
| pItem = pSlot->GetType()->CreateItem(); |
| if ( pItem ) |
| { |
| pItem->SetWhich( nId ); |
| pItem->PutValue( aAny ); |
| } |
| else |
| pItem = new SfxVoidItem( nId ); |
| } |
| } |
| else |
| { |
| // DONTCARE status |
| pItem = new SfxVoidItem(0); |
| eState = SFX_ITEM_UNKNOWN; |
| } |
| |
| for ( SfxControllerItem *pCtrl = pCache->GetItemLink(); |
| pCtrl; |
| pCtrl = pCtrl->GetItemLink() ) |
| pCtrl->StateChanged( nId, eState, pItem ); |
| |
| delete pItem; |
| } |
| } |
| |
| void BindDispatch_Impl::Release() |
| { |
| if ( xDisp.is() ) |
| { |
| xDisp->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aURL ); |
| xDisp = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > (); |
| } |
| |
| pCache = NULL; |
| release(); |
| } |
| |
| const ::com::sun::star::frame::FeatureStateEvent& BindDispatch_Impl::GetStatus() const |
| { |
| return aStatus; |
| } |
| |
| void BindDispatch_Impl::Dispatch( uno::Sequence < beans::PropertyValue > aProps, sal_Bool bForceSynchron ) |
| { |
| if ( xDisp.is() && aStatus.IsEnabled ) |
| { |
| sal_Int32 nLength = aProps.getLength(); |
| aProps.realloc(nLength+1); |
| aProps[nLength].Name = DEFINE_CONST_UNICODE("SynchronMode"); |
| aProps[nLength].Value <<= bForceSynchron ; |
| xDisp->dispatch( aURL, aProps ); |
| } |
| } |
| |
| //-------------------------------------------------------------------- |
| |
| /* Dieser Konstruktor fuer einen ungueltigen Cache, der sich also |
| bei der ersten Anfrage zun"achst updated. |
| */ |
| |
| SfxStateCache::SfxStateCache( sal_uInt16 nFuncId ): |
| pDispatch( 0 ), |
| nId(nFuncId), |
| pInternalController(0), |
| pController(0), |
| pLastItem( 0 ), |
| eLastState( 0 ), |
| bItemVisible( sal_True ) |
| { |
| DBG_MEMTEST(); |
| DBG_CTOR(SfxStateCache, 0); |
| bCtrlDirty = sal_True; |
| bSlotDirty = sal_True; |
| bItemDirty = sal_True; |
| } |
| |
| //-------------------------------------------------------------------- |
| |
| /* Der Destruktor pr"uft per Assertion, ob noch Controller angemeldet |
| sind. |
| */ |
| |
| SfxStateCache::~SfxStateCache() |
| { |
| DBG_MEMTEST(); |
| DBG_DTOR(SfxStateCache, 0); |
| DBG_ASSERT( pController == 0 && pInternalController == 0, "es sind noch Controller angemeldet" ); |
| if ( !IsInvalidItem(pLastItem) ) |
| delete pLastItem; |
| if ( pDispatch ) |
| { |
| pDispatch->Release(); |
| pDispatch = NULL; |
| } |
| } |
| |
| //-------------------------------------------------------------------- |
| // invalidates the cache (next request will force update) |
| void SfxStateCache::Invalidate( sal_Bool bWithMsg ) |
| { |
| bCtrlDirty = sal_True; |
| if ( bWithMsg ) |
| { |
| bSlotDirty = sal_True; |
| aSlotServ.SetSlot( 0 ); |
| if ( pDispatch ) |
| { |
| pDispatch->Release(); |
| pDispatch = NULL; |
| } |
| } |
| } |
| |
| //-------------------------------------------------------------------- |
| |
| // gets the corresponding function from the dispatcher or the cache |
| |
| const SfxSlotServer* SfxStateCache::GetSlotServer( SfxDispatcher &rDispat , const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider > & xProv ) |
| { |
| DBG_MEMTEST(); |
| DBG_CHKTHIS(SfxStateCache, 0); |
| |
| if ( bSlotDirty ) |
| { |
| // get the SlotServer; we need it for internal controllers anyway, but also in most cases |
| rDispat._FindServer( nId, aSlotServ, sal_False ); |
| |
| DBG_ASSERT( !pDispatch, "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 = sal_False; |
| bCtrlDirty = sal_True; |
| return aSlotServ.GetSlot()? &aSlotServ: 0; |
| } |
| |
| // create the dispatch URL from the slot data |
| ::com::sun::star::util::URL aURL; |
| ::rtl::OUString aCmd = DEFINE_CONST_UNICODE(".uno:"); |
| aURL.Protocol = aCmd; |
| aURL.Path = ::rtl::OUString::createFromAscii( pSlot->GetUnoName() ); |
| aCmd += aURL.Path; |
| aURL.Complete = aCmd; |
| aURL.Main = aCmd; |
| |
| // try to get a dispatch object for this command |
| ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > xDisp = xProv->queryDispatch( aURL, ::rtl::OUString(), 0 ); |
| if ( xDisp.is() ) |
| { |
| // test the dispatch object if it is just a wrapper for a SfxDispatcher |
| ::com::sun::star::uno::Reference< ::com::sun::star::lang::XUnoTunnel > xTunnel( xDisp, ::com::sun::star::uno::UNO_QUERY ); |
| SfxOfficeDispatch* pDisp = NULL; |
| if ( xTunnel.is() ) |
| { |
| sal_Int64 nImplementation = xTunnel->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier()); |
| pDisp = reinterpret_cast< SfxOfficeDispatch* >(sal::static_int_cast< sal_IntPtr >( nImplementation )); |
| } |
| |
| if ( pDisp ) |
| { |
| // 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 == SFX_APP()->GetAppDispatcher_Impl() ) |
| { |
| // so we can use it directly |
| bSlotDirty = sal_False; |
| bCtrlDirty = sal_True; |
| return aSlotServ.GetSlot()? &aSlotServ: 0; |
| } |
| } |
| |
| // so the dispatch object isn't a SfxDispatcher wrapper or it is one, but it uses another dispatcher, but not rDispat |
| pDispatch = new BindDispatch_Impl( xDisp, aURL, this, pSlot ); |
| pDispatch->acquire(); |
| |
| // flags must be set before adding StatusListener because the dispatch object will set the state |
| bSlotDirty = sal_False; |
| bCtrlDirty = sal_True; |
| xDisp->addStatusListener( pDispatch, aURL ); |
| } |
| else if ( rDispat.GetFrame() ) |
| { |
| ::com::sun::star::uno::Reference < ::com::sun::star::frame::XDispatchProvider > xFrameProv( |
| rDispat.GetFrame()->GetFrame().GetFrameInterface(), ::com::sun::star::uno::UNO_QUERY ); |
| if ( xFrameProv != xProv ) |
| return GetSlotServer( rDispat, xFrameProv ); |
| } |
| } |
| |
| bSlotDirty = sal_False; |
| bCtrlDirty = sal_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: 0; |
| } |
| |
| |
| //-------------------------------------------------------------------- |
| |
| // Status setzen in allen Controllern |
| |
| void SfxStateCache::SetState |
| ( |
| SfxItemState eState, // <SfxItemState> von 'pState' |
| const SfxPoolItem* pState, // Status des Slots, ggf. 0 oder -1 |
| sal_Bool bMaybeDirty |
| ) |
| |
| /* [Beschreibung] |
| |
| Diese Methode verteilt die Status auf alle an dieser SID gebundenen |
| <SfxControllerItem>s. Ist der Wert derselbe wie zuvor und wurde in- |
| zwischen weder ein Controller angemeldet, noch ein Controller invalidiert, |
| dann wird kein Wert weitergeleitet. Dadurch wird z.B. Flackern in |
| ListBoxen vermieden. |
| */ |
| |
| { |
| // if ( pDispatch ) |
| // return; |
| SetState_Impl( eState, pState, bMaybeDirty ); |
| } |
| |
| //-------------------------------------------------------------------- |
| |
| void SfxStateCache::SetVisibleState( sal_Bool bShow ) |
| { |
| SfxItemState eState( SFX_ITEM_AVAILABLE ); |
| const SfxPoolItem* pState( NULL ); |
| sal_Bool bNotify( sal_False ); |
| sal_Bool bDeleteItem( sal_False ); |
| |
| if ( bShow != bItemVisible ) |
| { |
| bItemVisible = bShow; |
| if ( bShow ) |
| { |
| if ( IsInvalidItem(pLastItem) || ( pLastItem == NULL )) |
| { |
| pState = new SfxVoidItem( nId ); |
| bDeleteItem = sal_True; |
| } |
| else |
| pState = pLastItem; |
| |
| eState = eLastState; |
| bNotify = ( pState != 0 ); |
| } |
| else |
| { |
| pState = new SfxVisibilityItem( nId, sal_False ); |
| bDeleteItem = sal_True; |
| } |
| |
| // Controller updaten |
| if ( !pDispatch && pController ) |
| { |
| for ( SfxControllerItem *pCtrl = pController; |
| pCtrl; |
| pCtrl = pCtrl->GetItemLink() ) |
| pCtrl->StateChanged( nId, eState, pState ); |
| } |
| |
| if ( pInternalController ) |
| pInternalController->StateChanged( nId, eState, pState ); |
| |
| if ( !bDeleteItem ) |
| delete pState; |
| } |
| } |
| |
| //-------------------------------------------------------------------- |
| |
| void SfxStateCache::SetState_Impl |
| ( |
| SfxItemState eState, // <SfxItemState> von 'pState' |
| const SfxPoolItem* pState, // Status des Slots, ggf. 0 oder -1 |
| sal_Bool bMaybeDirty |
| ) |
| { |
| (void)bMaybeDirty; //unused |
| DBG_MEMTEST(); |
| DBG_CHKTHIS(SfxStateCache, 0); |
| |
| // wenn zwischen Enter- und LeaveRegistrations ein hartes Update kommt |
| // k"onnen zwischenzeitlich auch Cached ohne Controller exisitieren |
| if ( !pController && !pInternalController ) |
| return; |
| |
| DBG_ASSERT( bMaybeDirty || !bSlotDirty, "setting state of dirty message" ); |
| // DBG_ASSERT( bCtrlDirty || ( aSlotServ.GetSlot() && aSlotServ.GetSlot()->IsMode(SFX_SLOT_VOLATILE) ), ! Discussed with MBA |
| // "setting state of non dirty controller" ); |
| DBG_ASSERT( SfxControllerItem::GetItemState(pState) == eState, "invalid SfxItemState" ); |
| DBG_PROFSTART(SfxStateCacheSetState); |
| |
| // m"ussen die Controller "uberhaupt benachrichtigt werden? |
| 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 = pState->Type() != pLastItem->Type() || |
| *pState != *pLastItem; |
| else |
| bNotify = ( pState != pLastItem ) || ( eState != eLastState ); |
| } |
| |
| if ( bNotify ) |
| { |
| // Controller updaten |
| if ( !pDispatch && pController ) |
| { |
| for ( SfxControllerItem *pCtrl = pController; |
| pCtrl; |
| pCtrl = pCtrl->GetItemLink() ) |
| pCtrl->StateChanged( nId, eState, pState ); |
| } |
| |
| if ( pInternalController ) |
| ((SfxDispatchController_Impl *)pInternalController)->StateChanged( nId, eState, pState, &aSlotServ ); |
| |
| // neuen Wert merken |
| if ( !IsInvalidItem(pLastItem) ) |
| DELETEZ(pLastItem); |
| if ( pState && !IsInvalidItem(pState) ) |
| pLastItem = pState->Clone(); |
| else |
| pLastItem = 0; |
| eLastState = eState; |
| bItemDirty = sal_False; |
| } |
| |
| bCtrlDirty = sal_False; |
| DBG_PROFSTOP(SfxStateCacheSetState); |
| } |
| |
| |
| //-------------------------------------------------------------------- |
| |
| // alten Status in allen Controllern nochmal setzen |
| |
| void SfxStateCache::SetCachedState( sal_Bool bAlways ) |
| { |
| DBG_MEMTEST(); |
| DBG_CHKTHIS(SfxStateCache, 0); |
| DBG_ASSERT(pController==NULL||pController->GetId()==nId, "Cache mit falschem ControllerItem" ); |
| DBG_PROFSTART(SfxStateCacheSetState); |
| |
| // nur updaten wenn cached item vorhanden und auch verarbeitbar |
| // (Wenn der State gesendet wird, mu\s sichergestellt sein, da\s ein |
| // Slotserver vorhanden ist, s. SfxControllerItem::GetCoreMetric() ) |
| if ( bAlways || ( !bItemDirty && !bSlotDirty ) ) |
| { |
| // Controller updaten |
| if ( !pDispatch && pController ) |
| { |
| for ( SfxControllerItem *pCtrl = pController; |
| pCtrl; |
| pCtrl = pCtrl->GetItemLink() ) |
| pCtrl->StateChanged( nId, eLastState, pLastItem ); |
| } |
| |
| if ( pInternalController ) |
| ((SfxDispatchController_Impl *)pInternalController)->StateChanged( nId, eLastState, pLastItem, &aSlotServ ); |
| |
| // Controller sind jetzt ok |
| bCtrlDirty = sal_True; |
| } |
| |
| DBG_PROFSTOP(SfxStateCacheSetState); |
| } |
| |
| |
| //-------------------------------------------------------------------- |
| |
| // FloatingWindows in allen Controls mit dieser Id zerstoeren |
| |
| void SfxStateCache::DeleteFloatingWindows() |
| { |
| DBG_MEMTEST(); |
| DBG_CHKTHIS(SfxStateCache, 0); |
| |
| SfxControllerItem *pNextCtrl=0; |
| for ( SfxControllerItem *pCtrl=pController; pCtrl; pCtrl=pNextCtrl ) |
| { |
| DBG_TRACE((ByteString("pCtrl: ").Append(ByteString::CreateFromInt64((sal_uIntPtr)pCtrl))).GetBuffer()); |
| pNextCtrl = pCtrl->GetItemLink(); |
| pCtrl->DeleteFloatingWindow(); |
| } |
| } |
| |
| ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > SfxStateCache::GetDispatch() const |
| { |
| if ( pDispatch ) |
| return pDispatch->xDisp; |
| return ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > (); |
| } |
| |
| void SfxStateCache::Dispatch( const SfxItemSet* pSet, sal_Bool bForceSynchron ) |
| { |
| // protect pDispatch against destruction in the call |
| ::com::sun::star::uno::Reference < ::com::sun::star::frame::XStatusListener > xKeepAlive( pDispatch ); |
| if ( pDispatch ) |
| { |
| uno::Sequence < beans::PropertyValue > aArgs; |
| if (pSet) |
| TransformItems( nId, *pSet, aArgs ); |
| pDispatch->Dispatch( aArgs, bForceSynchron ); |
| } |
| } |
| |
| |