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