1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: statcach.cxx,v $
10 * $Revision: 1.36.180.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sfx2.hxx"
35 // HACK: prevent conflict between STLPORT and Workshop headers on Solaris 8
39 #include <string> // HACK: prevent conflict between STLPORT and Workshop headers
40 #include <com/sun/star/util/XURLTransformer.hpp>
41 #include <com/sun/star/frame/XController.hpp>
42 #include <com/sun/star/frame/XFrameActionListener.hpp>
43 #include <com/sun/star/frame/XComponentLoader.hpp>
44 #include <com/sun/star/frame/XFrame.hpp>
45 #include <com/sun/star/frame/FrameActionEvent.hpp>
46 #include <com/sun/star/frame/FrameAction.hpp>
47 #include <com/sun/star/beans/PropertyValue.hpp>
48 #include <cppuhelper/weak.hxx>
49 #include <svtools/eitem.hxx>
50 #include <svtools/intitem.hxx>
51 #include <svtools/stritem.hxx>
52 #include <svtools/visitem.hxx>
53 #include <comphelper/processfactory.hxx>
58 #include <sfx2/app.hxx>
59 #include <sfx2/appuno.hxx>
60 #include "statcach.hxx"
61 #include <sfx2/msg.hxx>
62 #include <sfx2/ctrlitem.hxx>
63 #include <sfx2/dispatch.hxx>
64 #include "sfxtypes.hxx"
65 #include <sfx2/sfxuno.hxx>
66 #include <sfx2/unoctitm.hxx>
67 #include <sfx2/msgpool.hxx>
68 #include <sfx2/viewfrm.hxx>
70 using namespace ::com::sun::star
;
71 using namespace ::com::sun::star::uno
;
72 using namespace ::com::sun::star::util
;
74 //====================================================================
76 DBG_NAME(SfxStateCache
)
77 DBG_NAME(SfxStateCacheSetState
)
79 SFX_IMPL_XINTERFACE_2( BindDispatch_Impl
, OWeakObject
, ::com::sun::star::frame::XStatusListener
, ::com::sun::star::lang::XEventListener
)
80 SFX_IMPL_XTYPEPROVIDER_2( BindDispatch_Impl
, ::com::sun::star::frame::XStatusListener
, ::com::sun::star::lang::XEventListener
)
82 //-----------------------------------------------------------------------------
83 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
)
86 , pCache( pStateCache
)
89 DBG_ASSERT( pCache
&& pSlot
, "Invalid BindDispatch!");
90 aStatus
.IsEnabled
= sal_True
;
93 void SAL_CALL
BindDispatch_Impl::disposing( const ::com::sun::star::lang::EventObject
& ) throw( ::com::sun::star::uno::RuntimeException
)
97 xDisp
->removeStatusListener( (::com::sun::star::frame::XStatusListener
*) this, aURL
);
98 xDisp
= ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatch
> ();
102 void SAL_CALL
BindDispatch_Impl::statusChanged( const ::com::sun::star::frame::FeatureStateEvent
& rEvent
) throw( ::com::sun::star::uno::RuntimeException
)
108 ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XStatusListener
> xRef( (::cppu::OWeakObject
*)this, ::com::sun::star::uno::UNO_QUERY
);
109 if ( aStatus
.Requery
)
110 pCache
->Invalidate( sal_True
);
113 SfxPoolItem
*pItem
=NULL
;
114 sal_uInt16 nId
= pCache
->GetId();
115 SfxItemState eState
= SFX_ITEM_DISABLED
;
116 // pCache->Invalidate( sal_False );
117 if ( !aStatus
.IsEnabled
)
121 else if (aStatus
.State
.hasValue())
123 eState
= SFX_ITEM_AVAILABLE
;
124 ::com::sun::star::uno::Any aAny
= aStatus
.State
;
126 ::com::sun::star::uno::Type pType
= aAny
.getValueType();
127 if ( pType
== ::getBooleanCppuType() )
129 sal_Bool bTemp
= false;
131 pItem
= new SfxBoolItem( nId
, bTemp
);
133 else if ( pType
== ::getCppuType((const sal_uInt16
*)0) )
135 sal_uInt16 nTemp
= 0;
137 pItem
= new SfxUInt16Item( nId
, nTemp
);
139 else if ( pType
== ::getCppuType((const sal_uInt32
*)0) )
141 sal_uInt32 nTemp
= 0;
143 pItem
= new SfxUInt32Item( nId
, nTemp
);
145 else if ( pType
== ::getCppuType((const ::rtl::OUString
*)0) )
147 ::rtl::OUString sTemp
;
149 pItem
= new SfxStringItem( nId
, sTemp
);
154 pItem
= pSlot
->GetType()->CreateItem();
157 pItem
->SetWhich( nId
);
158 pItem
->PutValue( aAny
);
161 pItem
= new SfxVoidItem( nId
);
167 pItem
= new SfxVoidItem(0);
168 eState
= SFX_ITEM_UNKNOWN
;
171 for ( SfxControllerItem
*pCtrl
= pCache
->GetItemLink();
173 pCtrl
= pCtrl
->GetItemLink() )
174 pCtrl
->StateChanged( nId
, eState
, pItem
);
180 void BindDispatch_Impl::Release()
184 xDisp
->removeStatusListener( (::com::sun::star::frame::XStatusListener
*) this, aURL
);
185 xDisp
= ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatch
> ();
192 const ::com::sun::star::frame::FeatureStateEvent
& BindDispatch_Impl::GetStatus() const
197 void BindDispatch_Impl::Dispatch( uno::Sequence
< beans::PropertyValue
> aProps
, sal_Bool bForceSynchron
)
199 if ( xDisp
.is() && aStatus
.IsEnabled
)
201 sal_Int32 nLength
= aProps
.getLength();
202 aProps
.realloc(nLength
+1);
203 aProps
[nLength
].Name
= DEFINE_CONST_UNICODE("SynchronMode");
204 aProps
[nLength
].Value
<<= bForceSynchron
;
205 xDisp
->dispatch( aURL
, aProps
);
209 //--------------------------------------------------------------------
211 /* Dieser Konstruktor fuer einen ungueltigen Cache, der sich also
212 bei der ersten Anfrage zun"achst updated.
215 SfxStateCache::SfxStateCache( sal_uInt16 nFuncId
):
218 pInternalController(0),
222 bItemVisible( sal_True
)
225 DBG_CTOR(SfxStateCache
, 0);
226 bCtrlDirty
= sal_True
;
227 bSlotDirty
= sal_True
;
228 bItemDirty
= sal_True
;
231 //--------------------------------------------------------------------
233 /* Der Destruktor pr"uft per Assertion, ob noch Controller angemeldet
237 SfxStateCache::~SfxStateCache()
240 DBG_DTOR(SfxStateCache
, 0);
241 DBG_ASSERT( pController
== 0 && pInternalController
== 0, "es sind noch Controller angemeldet" );
242 if ( !IsInvalidItem(pLastItem
) )
246 pDispatch
->Release();
251 //--------------------------------------------------------------------
252 // invalidates the cache (next request will force update)
253 void SfxStateCache::Invalidate( sal_Bool bWithMsg
)
255 bCtrlDirty
= sal_True
;
258 bSlotDirty
= sal_True
;
259 aSlotServ
.SetSlot( 0 );
262 pDispatch
->Release();
268 //--------------------------------------------------------------------
270 // gets the corresponding function from the dispatcher or the cache
272 const SfxSlotServer
* SfxStateCache::GetSlotServer( SfxDispatcher
&rDispat
, const ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatchProvider
> & xProv
)
275 DBG_CHKTHIS(SfxStateCache
, 0);
279 // get the SlotServer; we need it for internal controllers anyway, but also in most cases
280 rDispat
._FindServer( nId
, aSlotServ
, sal_False
);
282 DBG_ASSERT( !pDispatch
, "Old Dispatch not removed!" );
284 // we don't need to check the dispatch provider if we only have an internal controller
287 const SfxSlot
* pSlot
= aSlotServ
.GetSlot();
289 // get the slot - even if it is disabled on the dispatcher
290 pSlot
= SfxSlotPool::GetSlotPool( rDispat
.GetFrame() ).GetSlot( nId
);
292 if ( !pSlot
|| !pSlot
->pUnoName
)
294 bSlotDirty
= sal_False
;
295 bCtrlDirty
= sal_True
;
296 return aSlotServ
.GetSlot()? &aSlotServ
: 0;
299 // create the dispatch URL from the slot data
300 ::com::sun::star::util::URL aURL
;
301 ::rtl::OUString aCmd
= DEFINE_CONST_UNICODE(".uno:");
302 aURL
.Protocol
= aCmd
;
303 aURL
.Path
= ::rtl::OUString::createFromAscii( pSlot
->GetUnoName() );
305 aURL
.Complete
= aCmd
;
308 // try to get a dispatch object for this command
309 ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatch
> xDisp
= xProv
->queryDispatch( aURL
, ::rtl::OUString(), 0 );
312 // test the dispatch object if it is just a wrapper for a SfxDispatcher
313 ::com::sun::star::uno::Reference
< ::com::sun::star::lang::XUnoTunnel
> xTunnel( xDisp
, ::com::sun::star::uno::UNO_QUERY
);
314 SfxOfficeDispatch
* pDisp
= NULL
;
317 sal_Int64 nImplementation
= xTunnel
->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier());
318 pDisp
= reinterpret_cast< SfxOfficeDispatch
* >(sal::static_int_cast
< sal_IntPtr
>( nImplementation
));
323 // The intercepting object is an SFX component
324 // If this dispatch object does not use the wanted dispatcher or the AppDispatcher, it's treated like any other UNO component
325 // (intercepting by internal dispatches)
326 SfxDispatcher
*pDispatcher
= pDisp
->GetDispatcher_Impl();
327 if ( pDispatcher
== &rDispat
|| pDispatcher
== SFX_APP()->GetAppDispatcher_Impl() )
329 // so we can use it directly
330 bSlotDirty
= sal_False
;
331 bCtrlDirty
= sal_True
;
332 return aSlotServ
.GetSlot()? &aSlotServ
: 0;
336 // so the dispatch object isn't a SfxDispatcher wrapper or it is one, but it uses another dispatcher, but not rDispat
337 pDispatch
= new BindDispatch_Impl( xDisp
, aURL
, this, pSlot
);
338 pDispatch
->acquire();
340 // flags must be set before adding StatusListener because the dispatch object will set the state
341 bSlotDirty
= sal_False
;
342 bCtrlDirty
= sal_True
;
343 xDisp
->addStatusListener( pDispatch
, aURL
);
345 else if ( rDispat
.GetFrame() )
347 ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatchProvider
> xFrameProv(
348 rDispat
.GetFrame()->GetFrame()->GetFrameInterface(), ::com::sun::star::uno::UNO_QUERY
);
349 if ( xFrameProv
!= xProv
)
350 return GetSlotServer( rDispat
, xFrameProv
);
354 bSlotDirty
= sal_False
;
355 bCtrlDirty
= sal_True
;
358 // we *always* return a SlotServer (if there is one); but in case of an external dispatch we might not use it
359 // for the "real" (non internal) controllers
360 return aSlotServ
.GetSlot()? &aSlotServ
: 0;
364 //--------------------------------------------------------------------
366 // Status setzen in allen Controllern
368 void SfxStateCache::SetState
370 SfxItemState eState
, // <SfxItemState> von 'pState'
371 const SfxPoolItem
* pState
, // Status des Slots, ggf. 0 oder -1
377 Diese Methode verteilt die Status auf alle an dieser SID gebundenen
378 <SfxControllerItem>s. Ist der Wert derselbe wie zuvor und wurde in-
379 zwischen weder ein Controller angemeldet, noch ein Controller invalidiert,
380 dann wird kein Wert weitergeleitet. Dadurch wird z.B. Flackern in
387 SetState_Impl( eState
, pState
, bMaybeDirty
);
390 //--------------------------------------------------------------------
392 void SfxStateCache::SetVisibleState( BOOL bShow
)
394 SfxItemState
eState( SFX_ITEM_AVAILABLE
);
395 const SfxPoolItem
* pState( NULL
);
396 sal_Bool
bNotify( sal_False
);
397 sal_Bool
bDeleteItem( sal_False
);
399 if ( bShow
!= bItemVisible
)
401 bItemVisible
= bShow
;
404 if ( IsInvalidItem(pLastItem
) || ( pLastItem
== NULL
))
406 pState
= new SfxVoidItem( nId
);
407 bDeleteItem
= sal_True
;
413 bNotify
= ( pState
!= 0 );
417 pState
= new SfxVisibilityItem( nId
, FALSE
);
418 bDeleteItem
= sal_True
;
421 // Controller updaten
422 if ( !pDispatch
&& pController
)
424 for ( SfxControllerItem
*pCtrl
= pController
;
426 pCtrl
= pCtrl
->GetItemLink() )
427 pCtrl
->StateChanged( nId
, eState
, pState
);
430 if ( pInternalController
)
431 pInternalController
->StateChanged( nId
, eState
, pState
);
438 //--------------------------------------------------------------------
440 void SfxStateCache::SetState_Impl
442 SfxItemState eState
, // <SfxItemState> von 'pState'
443 const SfxPoolItem
* pState
, // Status des Slots, ggf. 0 oder -1
447 (void)bMaybeDirty
; //unused
449 DBG_CHKTHIS(SfxStateCache
, 0);
451 // wenn zwischen Enter- und LeaveRegistrations ein hartes Update kommt
452 // k"onnen zwischenzeitlich auch Cached ohne Controller exisitieren
453 if ( !pController
&& !pInternalController
)
456 DBG_ASSERT( bMaybeDirty
|| !bSlotDirty
, "setting state of dirty message" );
457 // DBG_ASSERT( bCtrlDirty || ( aSlotServ.GetSlot() && aSlotServ.GetSlot()->IsMode(SFX_SLOT_VOLATILE) ), ! Discussed with MBA
458 // "setting state of non dirty controller" );
459 DBG_ASSERT( SfxControllerItem::GetItemState(pState
) == eState
, "invalid SfxItemState" );
460 DBG_PROFSTART(SfxStateCacheSetState
);
462 // m"ussen die Controller "uberhaupt benachrichtigt werden?
463 FASTBOOL bNotify
= bItemDirty
;
466 FASTBOOL bBothAvailable
= pLastItem
&& pState
&&
467 !IsInvalidItem(pState
) && !IsInvalidItem(pLastItem
);
468 DBG_ASSERT( !bBothAvailable
|| pState
!= pLastItem
, "setting state with own item" );
469 if ( bBothAvailable
)
470 bNotify
= pState
->Type() != pLastItem
->Type() ||
471 *pState
!= *pLastItem
;
473 bNotify
= ( pState
!= pLastItem
) || ( eState
!= eLastState
);
478 // Controller updaten
479 if ( !pDispatch
&& pController
)
481 for ( SfxControllerItem
*pCtrl
= pController
;
483 pCtrl
= pCtrl
->GetItemLink() )
484 pCtrl
->StateChanged( nId
, eState
, pState
);
487 if ( pInternalController
)
488 ((SfxDispatchController_Impl
*)pInternalController
)->StateChanged( nId
, eState
, pState
, &aSlotServ
);
491 if ( !IsInvalidItem(pLastItem
) )
493 if ( pState
&& !IsInvalidItem(pState
) )
494 pLastItem
= pState
->Clone();
498 bItemDirty
= sal_False
;
501 bCtrlDirty
= sal_False
;
502 DBG_PROFSTOP(SfxStateCacheSetState
);
506 //--------------------------------------------------------------------
508 // alten Status in allen Controllern nochmal setzen
510 void SfxStateCache::SetCachedState( BOOL bAlways
)
513 DBG_CHKTHIS(SfxStateCache
, 0);
514 DBG_ASSERT(pController
==NULL
||pController
->GetId()==nId
, "Cache mit falschem ControllerItem" );
515 DBG_PROFSTART(SfxStateCacheSetState
);
517 // nur updaten wenn cached item vorhanden und auch verarbeitbar
518 // (Wenn der State gesendet wird, mu\s sichergestellt sein, da\s ein
519 // Slotserver vorhanden ist, s. SfxControllerItem::GetCoreMetric() )
520 if ( bAlways
|| ( !bItemDirty
&& !bSlotDirty
) )
522 // Controller updaten
523 if ( !pDispatch
&& pController
)
525 for ( SfxControllerItem
*pCtrl
= pController
;
527 pCtrl
= pCtrl
->GetItemLink() )
528 pCtrl
->StateChanged( nId
, eLastState
, pLastItem
);
531 if ( pInternalController
)
532 ((SfxDispatchController_Impl
*)pInternalController
)->StateChanged( nId
, eLastState
, pLastItem
, &aSlotServ
);
534 // Controller sind jetzt ok
535 bCtrlDirty
= sal_True
;
538 DBG_PROFSTOP(SfxStateCacheSetState
);
542 //--------------------------------------------------------------------
544 // FloatingWindows in allen Controls mit dieser Id zerstoeren
546 void SfxStateCache::DeleteFloatingWindows()
549 DBG_CHKTHIS(SfxStateCache
, 0);
551 SfxControllerItem
*pNextCtrl
=0;
552 for ( SfxControllerItem
*pCtrl
=pController
; pCtrl
; pCtrl
=pNextCtrl
)
554 DBG_TRACE((ByteString("pCtrl: ").Append(ByteString::CreateFromInt64((sal_uIntPtr
)pCtrl
))).GetBuffer());
555 pNextCtrl
= pCtrl
->GetItemLink();
556 pCtrl
->DeleteFloatingWindow();
560 ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatch
> SfxStateCache::GetDispatch() const
563 return pDispatch
->xDisp
;
564 return ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatch
> ();
567 void SfxStateCache::Dispatch( const SfxItemSet
* pSet
, sal_Bool bForceSynchron
)
569 // protect pDispatch against destruction in the call
570 ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XStatusListener
> xKeepAlive( pDispatch
);
573 uno::Sequence
< beans::PropertyValue
> aArgs
;
575 TransformItems( nId
, *pSet
, aArgs
);
576 pDispatch
->Dispatch( aArgs
, bForceSynchron
);