xref: /AOO41X/main/sfx2/source/control/statcach.cxx (revision d119d52d53d0b2180f2ae51341d882123be2af2b)
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