xref: /trunk/main/sfx2/source/control/statcach.cxx (revision d119d52d)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sfx2.hxx"
26 
27 #ifdef SOLARIS
28 // HACK: prevent conflict between STLPORT and Workshop headers on Solaris 8
29 #include <ctime>
30 #endif
31 
32 #include <string> // HACK: prevent conflict between STLPORT and Workshop headers
33 #include <com/sun/star/util/XURLTransformer.hpp>
34 #include <com/sun/star/frame/XController.hpp>
35 #include <com/sun/star/frame/XFrameActionListener.hpp>
36 #include <com/sun/star/frame/XComponentLoader.hpp>
37 #include <com/sun/star/frame/XFrame.hpp>
38 #include <com/sun/star/frame/FrameActionEvent.hpp>
39 #include <com/sun/star/frame/FrameAction.hpp>
40 #include <com/sun/star/beans/PropertyValue.hpp>
41 #include <cppuhelper/weak.hxx>
42 #include <svl/eitem.hxx>
43 #include <svl/intitem.hxx>
44 #include <svl/stritem.hxx>
45 #include <svl/visitem.hxx>
46 #include <comphelper/processfactory.hxx>
47 
48 #ifndef GCC
49 #endif
50 
51 #include <sfx2/app.hxx>
52 #include <sfx2/appuno.hxx>
53 #include "statcach.hxx"
54 #include <sfx2/msg.hxx>
55 #include <sfx2/ctrlitem.hxx>
56 #include <sfx2/dispatch.hxx>
57 #include "sfxtypes.hxx"
58 #include <sfx2/sfxuno.hxx>
59 #include <sfx2/unoctitm.hxx>
60 #include <sfx2/msgpool.hxx>
61 #include <sfx2/viewfrm.hxx>
62 
63 using namespace ::com::sun::star;
64 using namespace ::com::sun::star::uno;
65 using namespace ::com::sun::star::util;
66 
67 //====================================================================
68 
69 DBG_NAME(SfxStateCache)
DBG_NAME(SfxStateCacheSetState)70 DBG_NAME(SfxStateCacheSetState)
71 
72 SFX_IMPL_XINTERFACE_2( BindDispatch_Impl, OWeakObject, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener )
73 SFX_IMPL_XTYPEPROVIDER_2( BindDispatch_Impl, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener )
74 
75 //-----------------------------------------------------------------------------
76 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 )
77 	: xDisp( rDisp )
78 	, aURL( rURL )
79 	, pCache( pStateCache )
80     , pSlot( pS )
81 {
82     DBG_ASSERT( pCache && pSlot, "Invalid BindDispatch!");
83     aStatus.IsEnabled = sal_True;
84 }
85 
disposing(const::com::sun::star::lang::EventObject &)86 void SAL_CALL BindDispatch_Impl::disposing( const ::com::sun::star::lang::EventObject& ) throw( ::com::sun::star::uno::RuntimeException )
87 {
88 	if ( xDisp.is() )
89 	{
90         xDisp->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aURL );
91 		xDisp = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
92 	}
93 }
94 
statusChanged(const::com::sun::star::frame::FeatureStateEvent & rEvent)95 void SAL_CALL  BindDispatch_Impl::statusChanged( const ::com::sun::star::frame::FeatureStateEvent& rEvent ) throw( ::com::sun::star::uno::RuntimeException )
96 {
97 	aStatus = rEvent;
98     if ( !pCache )
99 		return;
100 
101 	::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener >  xRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY );
102 	if ( aStatus.Requery )
103 		pCache->Invalidate( sal_True );
104 	else
105 	{
106         SfxPoolItem *pItem=NULL;
107         sal_uInt16 nId = pCache->GetId();
108         SfxItemState eState = SFX_ITEM_DISABLED;
109         // pCache->Invalidate( sal_False );
110         if ( !aStatus.IsEnabled )
111         {
112             // default
113         }
114         else if (aStatus.State.hasValue())
115 		{
116             eState = SFX_ITEM_AVAILABLE;
117 			::com::sun::star::uno::Any aAny = aStatus.State;
118 
119 			::com::sun::star::uno::Type pType =	aAny.getValueType();
120 			if ( pType == ::getBooleanCppuType() )
121 			{
122 				sal_Bool bTemp = false;
123 				aAny >>= bTemp ;
124 				pItem = new SfxBoolItem( nId, bTemp );
125 			}
126 			else if ( pType == ::getCppuType((const sal_uInt16*)0) )
127 			{
128 				sal_uInt16 nTemp = 0;
129 				aAny >>= nTemp ;
130 				pItem = new SfxUInt16Item( nId, nTemp );
131 			}
132 			else if ( pType == ::getCppuType((const sal_uInt32*)0) )
133 			{
134 				sal_uInt32 nTemp = 0;
135 				aAny >>= nTemp ;
136 				pItem = new SfxUInt32Item( nId, nTemp );
137 			}
138 			else if ( pType == ::getCppuType((const ::rtl::OUString*)0) )
139 			{
140 				::rtl::OUString sTemp ;
141 				aAny >>= sTemp ;
142 				pItem = new SfxStringItem( nId, sTemp );
143 			}
144 			else
145             {
146                 if ( pSlot )
147                     pItem = pSlot->GetType()->CreateItem();
148                 if ( pItem )
149                 {
150                     pItem->SetWhich( nId );
151                     pItem->PutValue( aAny );
152                 }
153                 else
154                     pItem = new SfxVoidItem( nId );
155             }
156 		}
157 		else
158 		{
159 			// DONTCARE status
160             pItem = new SfxVoidItem(0);
161             eState = SFX_ITEM_UNKNOWN;
162 		}
163 
164         for ( SfxControllerItem *pCtrl = pCache->GetItemLink();
165             pCtrl;
166             pCtrl = pCtrl->GetItemLink() )
167             pCtrl->StateChanged( nId, eState, pItem );
168 
169        delete pItem;
170 	}
171 }
172 
Release()173 void BindDispatch_Impl::Release()
174 {
175 	if ( xDisp.is() )
176 	{
177         xDisp->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aURL );
178 		xDisp = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
179 	}
180 
181 	pCache = NULL;
182 	release();
183 }
184 
GetStatus() const185 const ::com::sun::star::frame::FeatureStateEvent& BindDispatch_Impl::GetStatus() const
186 {
187 	return aStatus;
188 }
189 
Dispatch(uno::Sequence<beans::PropertyValue> aProps,sal_Bool bForceSynchron)190 void BindDispatch_Impl::Dispatch( uno::Sequence < beans::PropertyValue > aProps, sal_Bool bForceSynchron )
191 {
192     if ( xDisp.is() && aStatus.IsEnabled )
193     {
194 		sal_Int32 nLength = aProps.getLength();
195         aProps.realloc(nLength+1);
196         aProps[nLength].Name = DEFINE_CONST_UNICODE("SynchronMode");
197         aProps[nLength].Value <<= bForceSynchron ;
198         xDisp->dispatch( aURL, aProps );
199     }
200 }
201 
202 //--------------------------------------------------------------------
203 
204 /*	Dieser Konstruktor fuer einen ungueltigen Cache, der sich also
205 	bei der ersten Anfrage zun"achst updated.
206  */
207 
SfxStateCache(sal_uInt16 nFuncId)208 SfxStateCache::SfxStateCache( sal_uInt16 nFuncId ):
209     pDispatch( 0 ),
210     nId(nFuncId),
211     pInternalController(0),
212     pController(0),
213     pLastItem( 0 ),
214     eLastState( 0 ),
215     bItemVisible( sal_True )
216 {
217 	DBG_MEMTEST();
218 	DBG_CTOR(SfxStateCache,	0);
219 	bCtrlDirty = sal_True;
220 	bSlotDirty = sal_True;
221 	bItemDirty = sal_True;
222 }
223 
224 //--------------------------------------------------------------------
225 
226 /*	Der Destruktor pr"uft per Assertion, ob noch Controller angemeldet
227 	sind.
228  */
229 
~SfxStateCache()230 SfxStateCache::~SfxStateCache()
231 {
232 	DBG_MEMTEST();
233 	DBG_DTOR(SfxStateCache,	0);
234     DBG_ASSERT( pController == 0 && pInternalController == 0, "es sind noch Controller angemeldet" );
235 	if ( !IsInvalidItem(pLastItem) )
236 		delete pLastItem;
237 	if ( pDispatch )
238 	{
239 		pDispatch->Release();
240 		pDispatch = NULL;
241 	}
242 }
243 
244 //--------------------------------------------------------------------
245 // invalidates the cache (next request will force update)
Invalidate(sal_Bool bWithMsg)246 void SfxStateCache::Invalidate( sal_Bool bWithMsg )
247 {
248 	bCtrlDirty = sal_True;
249 	if ( bWithMsg )
250 	{
251 		bSlotDirty = sal_True;
252 		aSlotServ.SetSlot( 0 );
253 		if ( pDispatch )
254 		{
255 			pDispatch->Release();
256 			pDispatch = NULL;
257 		}
258 	}
259 }
260 
261 //--------------------------------------------------------------------
262 
263 // gets the corresponding function from the dispatcher or the cache
264 
GetSlotServer(SfxDispatcher & rDispat,const::com::sun::star::uno::Reference<::com::sun::star::frame::XDispatchProvider> & xProv)265 const SfxSlotServer* SfxStateCache::GetSlotServer( SfxDispatcher &rDispat , const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider > & xProv )
266 {
267 	DBG_MEMTEST();
268 	DBG_CHKTHIS(SfxStateCache, 0);
269 
270 	if ( bSlotDirty )
271 	{
272         // get the SlotServer; we need it for internal controllers anyway, but also in most cases
273         rDispat._FindServer( nId, aSlotServ, sal_False );
274 
275         DBG_ASSERT( !pDispatch, "Old Dispatch not removed!" );
276 
277         // we don't need to check the dispatch provider if we only have an internal controller
278         if ( xProv.is() )
279         {
280             const SfxSlot* pSlot = aSlotServ.GetSlot();
281             if ( !pSlot )
282                 // get the slot - even if it is disabled on the dispatcher
283                 pSlot = SfxSlotPool::GetSlotPool( rDispat.GetFrame() ).GetSlot( nId );
284 
285             if ( !pSlot || !pSlot->pUnoName )
286             {
287                 bSlotDirty = sal_False;
288                 bCtrlDirty = sal_True;
289                 return aSlotServ.GetSlot()? &aSlotServ: 0;
290             }
291 
292             // create the dispatch URL from the slot data
293             ::com::sun::star::util::URL aURL;
294             ::rtl::OUString aCmd = DEFINE_CONST_UNICODE(".uno:");
295             aURL.Protocol = aCmd;
296             aURL.Path = ::rtl::OUString::createFromAscii( pSlot->GetUnoName() );
297             aCmd += aURL.Path;
298             aURL.Complete = aCmd;
299             aURL.Main = aCmd;
300 
301             // try to get a dispatch object for this command
302             ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch >  xDisp = xProv->queryDispatch( aURL, ::rtl::OUString(), 0 );
303             if ( xDisp.is() )
304             {
305                 // test the dispatch object if it is just a wrapper for a SfxDispatcher
306 				::com::sun::star::uno::Reference< ::com::sun::star::lang::XUnoTunnel > xTunnel( xDisp, ::com::sun::star::uno::UNO_QUERY );
307                 SfxOfficeDispatch* pDisp = NULL;
308                 if ( xTunnel.is() )
309                 {
310                     sal_Int64 nImplementation = xTunnel->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier());
311                     pDisp = reinterpret_cast< SfxOfficeDispatch* >(sal::static_int_cast< sal_IntPtr >( nImplementation ));
312                 }
313 
314                 if ( pDisp )
315                 {
316                     // The intercepting object is an SFX component
317                     // If this dispatch object does not use the wanted dispatcher or the AppDispatcher, it's treated like any other UNO component
318                     // (intercepting by internal dispatches)
319                     SfxDispatcher *pDispatcher = pDisp->GetDispatcher_Impl();
320 					if ( pDispatcher == &rDispat || pDispatcher == SFX_APP()->GetAppDispatcher_Impl() )
321 					{
322                         // so we can use it directly
323                         bSlotDirty = sal_False;
324                         bCtrlDirty = sal_True;
325                         return aSlotServ.GetSlot()? &aSlotServ: 0;
326                     }
327                 }
328 
329                 // so the dispatch object isn't a SfxDispatcher wrapper or it is one, but it uses another dispatcher, but not rDispat
330                 pDispatch = new BindDispatch_Impl( xDisp, aURL, this, pSlot );
331                 pDispatch->acquire();
332 
333                 // flags must be set before adding StatusListener because the dispatch object will set the state
334                 bSlotDirty = sal_False;
335                 bCtrlDirty = sal_True;
336                 xDisp->addStatusListener( pDispatch, aURL );
337             }
338             else if ( rDispat.GetFrame() )
339             {
340                 ::com::sun::star::uno::Reference < ::com::sun::star::frame::XDispatchProvider > xFrameProv(
341                         rDispat.GetFrame()->GetFrame().GetFrameInterface(), ::com::sun::star::uno::UNO_QUERY );
342                 if ( xFrameProv != xProv )
343                     return GetSlotServer( rDispat, xFrameProv );
344             }
345         }
346 
347 		bSlotDirty = sal_False;
348 		bCtrlDirty = sal_True;
349 	}
350 
351     // we *always* return a SlotServer (if there is one); but in case of an external dispatch we might not use it
352     // for the "real" (non internal) controllers
353     return aSlotServ.GetSlot()? &aSlotServ: 0;
354 }
355 
356 
357 //--------------------------------------------------------------------
358 
359 // Status setzen in allen Controllern
360 
SetState(SfxItemState eState,const SfxPoolItem * pState,sal_Bool bMaybeDirty)361 void SfxStateCache::SetState
362 (
363 	SfxItemState		eState, 	// <SfxItemState> von 'pState'
364 	const SfxPoolItem*	pState,		// Status des Slots, ggf. 0 oder -1
365 	sal_Bool bMaybeDirty
366 )
367 
368 /*	[Beschreibung]
369 
370 	Diese Methode verteilt die Status auf alle an dieser SID gebundenen
371 	<SfxControllerItem>s. Ist der Wert derselbe wie zuvor und wurde in-
372 	zwischen weder ein Controller angemeldet, noch ein Controller invalidiert,
373 	dann wird kein Wert weitergeleitet. Dadurch wird z.B. Flackern in
374 	ListBoxen vermieden.
375 */
376 
377 {
378 //    if ( pDispatch )
379 //        return;
380 	SetState_Impl( eState, pState, bMaybeDirty );
381 }
382 
383 //--------------------------------------------------------------------
384 
SetVisibleState(sal_Bool bShow)385 void SfxStateCache::SetVisibleState( sal_Bool bShow )
386 {
387 	SfxItemState		eState( SFX_ITEM_AVAILABLE );
388 	const SfxPoolItem*	pState( NULL );
389 	sal_Bool            bNotify( sal_False );
390     sal_Bool            bDeleteItem( sal_False );
391 
392     if ( bShow != bItemVisible )
393     {
394         bItemVisible = bShow;
395         if ( bShow )
396         {
397             if ( IsInvalidItem(pLastItem) || ( pLastItem == NULL ))
398             {
399                 pState = new SfxVoidItem( nId );
400                 bDeleteItem = sal_True;
401             }
402             else
403                 pState = pLastItem;
404 
405             eState = eLastState;
406             bNotify = ( pState != 0 );
407         }
408         else
409         {
410             pState = new SfxVisibilityItem( nId, sal_False );
411             bDeleteItem = sal_True;
412         }
413 
414         // Controller updaten
415         if ( !pDispatch && pController )
416         {
417             for ( SfxControllerItem *pCtrl = pController;
418                     pCtrl;
419                     pCtrl = pCtrl->GetItemLink() )
420                 pCtrl->StateChanged( nId, eState, pState );
421         }
422 
423         if ( pInternalController )
424             pInternalController->StateChanged( nId, eState, pState );
425 
426         if ( !bDeleteItem )
427             delete pState;
428     }
429 }
430 
431 //--------------------------------------------------------------------
432 
SetState_Impl(SfxItemState eState,const SfxPoolItem * pState,sal_Bool bMaybeDirty)433 void SfxStateCache::SetState_Impl
434 (
435 	SfxItemState		eState, 	// <SfxItemState> von 'pState'
436 	const SfxPoolItem*	pState,		// Status des Slots, ggf. 0 oder -1
437 	sal_Bool bMaybeDirty
438 )
439 {
440     (void)bMaybeDirty; //unused
441 	DBG_MEMTEST();
442 	DBG_CHKTHIS(SfxStateCache, 0);
443 
444 	// wenn zwischen Enter- und LeaveRegistrations ein hartes Update kommt
445 	// k"onnen zwischenzeitlich auch Cached ohne Controller exisitieren
446     if ( !pController && !pInternalController )
447 		return;
448 
449 	DBG_ASSERT( bMaybeDirty || !bSlotDirty, "setting state of dirty message" );
450 //	DBG_ASSERT( bCtrlDirty || ( aSlotServ.GetSlot() && aSlotServ.GetSlot()->IsMode(SFX_SLOT_VOLATILE) ), ! Discussed with MBA
451 //				"setting state of non dirty controller" );
452     DBG_ASSERT( SfxControllerItem::GetItemState(pState) == eState, "invalid SfxItemState" );
453 	DBG_PROFSTART(SfxStateCacheSetState);
454 
455 	// m"ussen die Controller "uberhaupt benachrichtigt werden?
456 	bool bNotify = bItemDirty;
457 	if ( !bItemDirty )
458 	{
459 		bool bBothAvailable = pLastItem && pState &&
460 					!IsInvalidItem(pState) && !IsInvalidItem(pLastItem);
461 		DBG_ASSERT( !bBothAvailable || pState != pLastItem, "setting state with own item" );
462 		if ( bBothAvailable )
463 			bNotify = pState->Type() != pLastItem->Type() ||
464 					  *pState != *pLastItem;
465 		else
466 			bNotify = ( pState != pLastItem ) || ( eState != eLastState );
467 	}
468 
469 	if ( bNotify )
470 	{
471 		// Controller updaten
472         if ( !pDispatch && pController )
473         {
474             for ( SfxControllerItem *pCtrl = pController;
475                 pCtrl;
476                 pCtrl = pCtrl->GetItemLink() )
477                 pCtrl->StateChanged( nId, eState, pState );
478         }
479 
480         if ( pInternalController )
481             ((SfxDispatchController_Impl *)pInternalController)->StateChanged( nId, eState, pState, &aSlotServ );
482 
483 		// neuen Wert merken
484 		if ( !IsInvalidItem(pLastItem) )
485 			DELETEZ(pLastItem);
486 		if ( pState && !IsInvalidItem(pState) )
487 			pLastItem = pState->Clone();
488 		else
489 			pLastItem = 0;
490 		eLastState = eState;
491 		bItemDirty = sal_False;
492 	}
493 
494 	bCtrlDirty = sal_False;
495 	DBG_PROFSTOP(SfxStateCacheSetState);
496 }
497 
498 
499 //--------------------------------------------------------------------
500 
501 // alten Status in allen Controllern nochmal setzen
502 
SetCachedState(sal_Bool bAlways)503 void SfxStateCache::SetCachedState( sal_Bool bAlways )
504 {
505 	DBG_MEMTEST();
506 	DBG_CHKTHIS(SfxStateCache, 0);
507 	DBG_ASSERT(pController==NULL||pController->GetId()==nId, "Cache mit falschem ControllerItem" );
508 	DBG_PROFSTART(SfxStateCacheSetState);
509 
510 	// nur updaten wenn cached item vorhanden und auch verarbeitbar
511 	// (Wenn der State gesendet wird, mu\s sichergestellt sein, da\s ein
512 	// Slotserver vorhanden ist, s. SfxControllerItem::GetCoreMetric() )
513 	if ( bAlways || ( !bItemDirty && !bSlotDirty ) )
514 	{
515 		// Controller updaten
516         if ( !pDispatch && pController )
517         {
518             for ( SfxControllerItem *pCtrl = pController;
519                 pCtrl;
520                 pCtrl = pCtrl->GetItemLink() )
521                 pCtrl->StateChanged( nId, eLastState, pLastItem );
522         }
523 
524         if ( pInternalController )
525             ((SfxDispatchController_Impl *)pInternalController)->StateChanged( nId, eLastState, pLastItem, &aSlotServ );
526 
527 		// Controller sind jetzt ok
528 		bCtrlDirty = sal_True;
529 	}
530 
531 	DBG_PROFSTOP(SfxStateCacheSetState);
532 }
533 
534 
535 //--------------------------------------------------------------------
536 
537 // FloatingWindows in allen Controls mit dieser Id zerstoeren
538 
DeleteFloatingWindows()539 void SfxStateCache::DeleteFloatingWindows()
540 {
541 	DBG_MEMTEST();
542 	DBG_CHKTHIS(SfxStateCache, 0);
543 
544 	SfxControllerItem *pNextCtrl=0;
545 	for ( SfxControllerItem *pCtrl=pController; pCtrl; pCtrl=pNextCtrl )
546 	{
547 		DBG_TRACE((ByteString("pCtrl: ").Append(ByteString::CreateFromInt64((sal_uIntPtr)pCtrl))).GetBuffer());
548 		pNextCtrl = pCtrl->GetItemLink();
549 		pCtrl->DeleteFloatingWindow();
550 	}
551 }
552 
GetDispatch() const553 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch >  SfxStateCache::GetDispatch() const
554 {
555 	if ( pDispatch )
556 		return pDispatch->xDisp;
557 	return ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
558 }
559 
Dispatch(const SfxItemSet * pSet,sal_Bool bForceSynchron)560 void SfxStateCache::Dispatch( const SfxItemSet* pSet, sal_Bool bForceSynchron )
561 {
562 	// protect pDispatch against destruction in the call
563 	::com::sun::star::uno::Reference < ::com::sun::star::frame::XStatusListener > xKeepAlive( pDispatch );
564 	if ( pDispatch )
565 	{
566 		uno::Sequence < beans::PropertyValue > aArgs;
567 		if (pSet)
568 			TransformItems( nId, *pSet, aArgs );
569         pDispatch->Dispatch( aArgs, bForceSynchron );
570 	}
571 }
572 
573 
574