Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #ifdef SOLARIS
22 : #include <ctime>
23 : #endif
24 :
25 : #include <string>
26 : #include <com/sun/star/util/XURLTransformer.hpp>
27 : #include <com/sun/star/frame/XController.hpp>
28 : #include <com/sun/star/frame/XFrameActionListener.hpp>
29 : #include <com/sun/star/frame/XComponentLoader.hpp>
30 : #include <com/sun/star/frame/XFrame.hpp>
31 : #include <com/sun/star/frame/FrameActionEvent.hpp>
32 : #include <com/sun/star/frame/FrameAction.hpp>
33 : #include <com/sun/star/beans/PropertyValue.hpp>
34 : #include <cppuhelper/weak.hxx>
35 : #include <svl/eitem.hxx>
36 : #include <svl/intitem.hxx>
37 : #include <svl/stritem.hxx>
38 : #include <svl/visitem.hxx>
39 : #include <comphelper/processfactory.hxx>
40 :
41 : #include <sfx2/app.hxx>
42 : #include <sfx2/appuno.hxx>
43 : #include "statcach.hxx"
44 : #include <sfx2/msg.hxx>
45 : #include <sfx2/ctrlitem.hxx>
46 : #include <sfx2/dispatch.hxx>
47 : #include "sfxtypes.hxx"
48 : #include <sfx2/sfxuno.hxx>
49 : #include <sfx2/unoctitm.hxx>
50 : #include <sfx2/msgpool.hxx>
51 : #include <sfx2/viewfrm.hxx>
52 :
53 : using namespace ::com::sun::star;
54 : using namespace ::com::sun::star::uno;
55 : using namespace ::com::sun::star::util;
56 :
57 : //====================================================================
58 :
59 : DBG_NAME(SfxStateCache)
60 : DBG_NAME(SfxStateCacheSetState)
61 :
62 0 : SFX_IMPL_XINTERFACE_2( BindDispatch_Impl, OWeakObject, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener )
63 0 : SFX_IMPL_XTYPEPROVIDER_2( BindDispatch_Impl, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener )
64 :
65 : //-----------------------------------------------------------------------------
66 0 : 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 )
67 : : xDisp( rDisp )
68 : , aURL( rURL )
69 : , pCache( pStateCache )
70 0 : , pSlot( pS )
71 : {
72 : DBG_ASSERT( pCache && pSlot, "Invalid BindDispatch!");
73 0 : aStatus.IsEnabled = sal_True;
74 0 : }
75 :
76 0 : void SAL_CALL BindDispatch_Impl::disposing( const ::com::sun::star::lang::EventObject& ) throw( ::com::sun::star::uno::RuntimeException )
77 : {
78 0 : if ( xDisp.is() )
79 : {
80 0 : xDisp->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aURL );
81 0 : xDisp = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
82 : }
83 0 : }
84 :
85 0 : void SAL_CALL BindDispatch_Impl::statusChanged( const ::com::sun::star::frame::FeatureStateEvent& rEvent ) throw( ::com::sun::star::uno::RuntimeException )
86 : {
87 0 : aStatus = rEvent;
88 0 : if ( !pCache )
89 0 : return;
90 :
91 0 : ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > xRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY );
92 0 : if ( aStatus.Requery )
93 0 : pCache->Invalidate( sal_True );
94 : else
95 : {
96 0 : SfxPoolItem *pItem=NULL;
97 0 : sal_uInt16 nId = pCache->GetId();
98 0 : SfxItemState eState = SFX_ITEM_DISABLED;
99 0 : if ( !aStatus.IsEnabled )
100 : {
101 : // default
102 : }
103 0 : else if (aStatus.State.hasValue())
104 : {
105 0 : eState = SFX_ITEM_AVAILABLE;
106 0 : ::com::sun::star::uno::Any aAny = aStatus.State;
107 :
108 0 : ::com::sun::star::uno::Type pType = aAny.getValueType();
109 0 : if ( pType == ::getBooleanCppuType() )
110 : {
111 0 : sal_Bool bTemp = false;
112 0 : aAny >>= bTemp ;
113 0 : pItem = new SfxBoolItem( nId, bTemp );
114 : }
115 0 : else if ( pType == ::getCppuType((const sal_uInt16*)0) )
116 : {
117 0 : sal_uInt16 nTemp = 0;
118 0 : aAny >>= nTemp ;
119 0 : pItem = new SfxUInt16Item( nId, nTemp );
120 : }
121 0 : else if ( pType == ::getCppuType((const sal_uInt32*)0) )
122 : {
123 0 : sal_uInt32 nTemp = 0;
124 0 : aAny >>= nTemp ;
125 0 : pItem = new SfxUInt32Item( nId, nTemp );
126 : }
127 0 : else if ( pType == ::getCppuType((const ::rtl::OUString*)0) )
128 : {
129 0 : ::rtl::OUString sTemp ;
130 0 : aAny >>= sTemp ;
131 0 : pItem = new SfxStringItem( nId, sTemp );
132 : }
133 : else
134 : {
135 0 : if ( pSlot )
136 0 : pItem = pSlot->GetType()->CreateItem();
137 0 : if ( pItem )
138 : {
139 0 : pItem->SetWhich( nId );
140 0 : pItem->PutValue( aAny );
141 : }
142 : else
143 0 : pItem = new SfxVoidItem( nId );
144 0 : }
145 : }
146 : else
147 : {
148 : // DONTCARE status
149 0 : pItem = new SfxVoidItem(0);
150 0 : eState = SFX_ITEM_UNKNOWN;
151 : }
152 :
153 0 : for ( SfxControllerItem *pCtrl = pCache->GetItemLink();
154 : pCtrl;
155 : pCtrl = pCtrl->GetItemLink() )
156 0 : pCtrl->StateChanged( nId, eState, pItem );
157 :
158 0 : delete pItem;
159 0 : }
160 : }
161 :
162 0 : void BindDispatch_Impl::Release()
163 : {
164 0 : if ( xDisp.is() )
165 : {
166 0 : xDisp->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aURL );
167 0 : xDisp = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
168 : }
169 :
170 0 : pCache = NULL;
171 0 : release();
172 0 : }
173 :
174 0 : const ::com::sun::star::frame::FeatureStateEvent& BindDispatch_Impl::GetStatus() const
175 : {
176 0 : return aStatus;
177 : }
178 :
179 0 : void BindDispatch_Impl::Dispatch( uno::Sequence < beans::PropertyValue > aProps, sal_Bool bForceSynchron )
180 : {
181 0 : if ( xDisp.is() && aStatus.IsEnabled )
182 : {
183 0 : sal_Int32 nLength = aProps.getLength();
184 0 : aProps.realloc(nLength+1);
185 0 : aProps[nLength].Name = DEFINE_CONST_UNICODE("SynchronMode");
186 0 : aProps[nLength].Value <<= bForceSynchron ;
187 0 : xDisp->dispatch( aURL, aProps );
188 : }
189 0 : }
190 :
191 : //--------------------------------------------------------------------
192 : // This constructor for an invalid cache that is updated in the first request.
193 :
194 7088 : SfxStateCache::SfxStateCache( sal_uInt16 nFuncId ):
195 : pDispatch( 0 ),
196 : nId(nFuncId),
197 : pInternalController(0),
198 : pController(0),
199 : pLastItem( 0 ),
200 : eLastState( 0 ),
201 7088 : bItemVisible( sal_True )
202 : {
203 : DBG_CTOR(SfxStateCache, 0);
204 7088 : bCtrlDirty = sal_True;
205 7088 : bSlotDirty = sal_True;
206 7088 : bItemDirty = sal_True;
207 7088 : }
208 :
209 : //--------------------------------------------------------------------
210 : // The Destructor checks by assertion, even if controllers are registered.
211 :
212 11048 : SfxStateCache::~SfxStateCache()
213 : {
214 : DBG_DTOR(SfxStateCache, 0);
215 : DBG_ASSERT( pController == 0 && pInternalController == 0, "there are still Controllers registered" );
216 5524 : if ( !IsInvalidItem(pLastItem) )
217 5524 : delete pLastItem;
218 5524 : if ( pDispatch )
219 : {
220 0 : pDispatch->Release();
221 0 : pDispatch = NULL;
222 : }
223 5524 : }
224 :
225 : //--------------------------------------------------------------------
226 : // invalidates the cache (next request will force update)
227 120 : void SfxStateCache::Invalidate( sal_Bool bWithMsg )
228 : {
229 120 : bCtrlDirty = sal_True;
230 120 : if ( bWithMsg )
231 : {
232 75 : bSlotDirty = sal_True;
233 75 : aSlotServ.SetSlot( 0 );
234 75 : if ( pDispatch )
235 : {
236 0 : pDispatch->Release();
237 0 : pDispatch = NULL;
238 : }
239 : }
240 120 : }
241 :
242 : //--------------------------------------------------------------------
243 : // gets the corresponding function from the dispatcher or the cache
244 :
245 4875 : const SfxSlotServer* SfxStateCache::GetSlotServer( SfxDispatcher &rDispat , const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider > & xProv )
246 : {
247 : DBG_CHKTHIS(SfxStateCache, 0);
248 :
249 4875 : if ( bSlotDirty )
250 : {
251 : // get the SlotServer; we need it for internal controllers anyway, but also in most cases
252 1635 : rDispat._FindServer( nId, aSlotServ, sal_False );
253 :
254 : DBG_ASSERT( !pDispatch, "Old Dispatch not removed!" );
255 :
256 : // we don't need to check the dispatch provider if we only have an internal controller
257 1635 : if ( xProv.is() )
258 : {
259 1635 : const SfxSlot* pSlot = aSlotServ.GetSlot();
260 1635 : if ( !pSlot )
261 : // get the slot - even if it is disabled on the dispatcher
262 0 : pSlot = SfxSlotPool::GetSlotPool( rDispat.GetFrame() ).GetSlot( nId );
263 :
264 1635 : if ( !pSlot || !pSlot->pUnoName )
265 : {
266 0 : bSlotDirty = sal_False;
267 0 : bCtrlDirty = sal_True;
268 0 : return aSlotServ.GetSlot()? &aSlotServ: 0;
269 : }
270 :
271 : // create the dispatch URL from the slot data
272 1635 : ::com::sun::star::util::URL aURL;
273 1635 : ::rtl::OUString aCmd = DEFINE_CONST_UNICODE(".uno:");
274 1635 : aURL.Protocol = aCmd;
275 1635 : aURL.Path = ::rtl::OUString::createFromAscii( pSlot->GetUnoName() );
276 1635 : aCmd += aURL.Path;
277 1635 : aURL.Complete = aCmd;
278 1635 : aURL.Main = aCmd;
279 :
280 : // try to get a dispatch object for this command
281 1635 : ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > xDisp = xProv->queryDispatch( aURL, ::rtl::OUString(), 0 );
282 1635 : if ( xDisp.is() )
283 : {
284 : // test the dispatch object if it is just a wrapper for a SfxDispatcher
285 1635 : ::com::sun::star::uno::Reference< ::com::sun::star::lang::XUnoTunnel > xTunnel( xDisp, ::com::sun::star::uno::UNO_QUERY );
286 1635 : SfxOfficeDispatch* pDisp = NULL;
287 1635 : if ( xTunnel.is() )
288 : {
289 1635 : sal_Int64 nImplementation = xTunnel->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier());
290 1635 : pDisp = reinterpret_cast< SfxOfficeDispatch* >(sal::static_int_cast< sal_IntPtr >( nImplementation ));
291 : }
292 :
293 1635 : if ( pDisp )
294 : {
295 : // The intercepting object is an SFX component
296 : // If this dispatch object does not use the wanted dispatcher or the AppDispatcher, it's treated like any other UNO component
297 : // (intercepting by internal dispatches)
298 1635 : SfxDispatcher *pDispatcher = pDisp->GetDispatcher_Impl();
299 1635 : if ( pDispatcher == &rDispat || pDispatcher == SFX_APP()->GetAppDispatcher_Impl() )
300 : {
301 : // so we can use it directly
302 1635 : bSlotDirty = sal_False;
303 1635 : bCtrlDirty = sal_True;
304 1635 : return aSlotServ.GetSlot()? &aSlotServ: 0;
305 : }
306 : }
307 :
308 : // so the dispatch object isn't a SfxDispatcher wrapper or it is one, but it uses another dispatcher, but not rDispat
309 0 : pDispatch = new BindDispatch_Impl( xDisp, aURL, this, pSlot );
310 0 : pDispatch->acquire();
311 :
312 : // flags must be set before adding StatusListener because the dispatch object will set the state
313 0 : bSlotDirty = sal_False;
314 0 : bCtrlDirty = sal_True;
315 0 : xDisp->addStatusListener( pDispatch, aURL );
316 : }
317 0 : else if ( rDispat.GetFrame() )
318 : {
319 : ::com::sun::star::uno::Reference < ::com::sun::star::frame::XDispatchProvider > xFrameProv(
320 0 : rDispat.GetFrame()->GetFrame().GetFrameInterface(), ::com::sun::star::uno::UNO_QUERY );
321 0 : if ( xFrameProv != xProv )
322 0 : return GetSlotServer( rDispat, xFrameProv );
323 1635 : }
324 : }
325 :
326 0 : bSlotDirty = sal_False;
327 0 : bCtrlDirty = sal_True;
328 : }
329 :
330 : // we *always* return a SlotServer (if there is one); but in case of an external dispatch we might not use it
331 : // for the "real" (non internal) controllers
332 3240 : return aSlotServ.GetSlot()? &aSlotServ: 0;
333 : }
334 :
335 :
336 : //--------------------------------------------------------------------
337 :
338 : // Set Status in all Controllers
339 :
340 1560 : void SfxStateCache::SetState
341 : (
342 : SfxItemState eState, // <SfxItemState> from 'pState'
343 : const SfxPoolItem* pState, // Slot Status, 0 or -1
344 : sal_Bool bMaybeDirty
345 : )
346 :
347 : /* [Description]
348 :
349 : This method distributes the status of all of this SID bound
350 : <SfxControllerItem>s. If the value is the same as before, and if neither
351 : controller was registered nor invalidated inbetween, then no value is
352 : passed. This way the flickering is for example avoided in ListBoxes.
353 : */
354 : {
355 1560 : SetState_Impl( eState, pState, bMaybeDirty );
356 1560 : }
357 :
358 : //--------------------------------------------------------------------
359 :
360 0 : void SfxStateCache::SetVisibleState( sal_Bool bShow )
361 : {
362 0 : SfxItemState eState( SFX_ITEM_AVAILABLE );
363 0 : const SfxPoolItem* pState( NULL );
364 0 : sal_Bool bDeleteItem( sal_False );
365 :
366 0 : if ( bShow != bItemVisible )
367 : {
368 0 : bItemVisible = bShow;
369 0 : if ( bShow )
370 : {
371 0 : if ( IsInvalidItem(pLastItem) || ( pLastItem == NULL ))
372 : {
373 0 : pState = new SfxVoidItem( nId );
374 0 : bDeleteItem = sal_True;
375 : }
376 : else
377 0 : pState = pLastItem;
378 :
379 0 : eState = eLastState;
380 : }
381 : else
382 : {
383 0 : pState = new SfxVisibilityItem( nId, sal_False );
384 0 : bDeleteItem = sal_True;
385 : }
386 :
387 : // Update Controller
388 0 : if ( !pDispatch && pController )
389 : {
390 0 : for ( SfxControllerItem *pCtrl = pController;
391 : pCtrl;
392 : pCtrl = pCtrl->GetItemLink() )
393 0 : pCtrl->StateChanged( nId, eState, pState );
394 : }
395 :
396 0 : if ( pInternalController )
397 0 : pInternalController->StateChanged( nId, eState, pState );
398 :
399 0 : if ( bDeleteItem )
400 0 : delete pState;
401 : }
402 0 : }
403 :
404 : //--------------------------------------------------------------------
405 :
406 1560 : void SfxStateCache::SetState_Impl
407 : (
408 : SfxItemState eState, // <SfxItemState> from 'pState'
409 : const SfxPoolItem* pState, // Slot Status, 0 or -1
410 : sal_Bool bMaybeDirty
411 : )
412 : {
413 : (void)bMaybeDirty; //unused
414 : DBG_CHKTHIS(SfxStateCache, 0);
415 :
416 : // If a hard update occurs between enter- and leave-registrations is a
417 : // can also intermediate Cached exist without controller.
418 1560 : if ( !pController && !pInternalController )
419 1560 : return;
420 :
421 : DBG_ASSERT( bMaybeDirty || !bSlotDirty, "setting state of dirty message" );
422 : DBG_ASSERT( SfxControllerItem::GetItemState(pState) == eState, "invalid SfxItemState" );
423 : DBG_PROFSTART(SfxStateCacheSetState);
424 :
425 : // does the controller have to be notified at all?
426 1560 : bool bNotify = bItemDirty;
427 1560 : if ( !bItemDirty )
428 : {
429 : bool bBothAvailable = pLastItem && pState &&
430 45 : !IsInvalidItem(pState) && !IsInvalidItem(pLastItem);
431 : DBG_ASSERT( !bBothAvailable || pState != pLastItem, "setting state with own item" );
432 45 : if ( bBothAvailable )
433 33 : bNotify = pState->Type() != pLastItem->Type() ||
434 33 : *pState != *pLastItem;
435 : else
436 12 : bNotify = ( pState != pLastItem ) || ( eState != eLastState );
437 : }
438 :
439 1560 : if ( bNotify )
440 : {
441 : // Update Controller
442 1515 : if ( !pDispatch && pController )
443 : {
444 350 : for ( SfxControllerItem *pCtrl = pController;
445 : pCtrl;
446 : pCtrl = pCtrl->GetItemLink() )
447 200 : pCtrl->StateChanged( nId, eState, pState );
448 : }
449 :
450 1515 : if ( pInternalController )
451 1515 : ((SfxDispatchController_Impl *)pInternalController)->StateChanged( nId, eState, pState, &aSlotServ );
452 :
453 : // Remember new value
454 1515 : if ( !IsInvalidItem(pLastItem) )
455 1515 : DELETEZ(pLastItem);
456 1515 : if ( pState && !IsInvalidItem(pState) )
457 922 : pLastItem = pState->Clone();
458 : else
459 593 : pLastItem = 0;
460 1515 : eLastState = eState;
461 1515 : bItemDirty = sal_False;
462 : }
463 :
464 1560 : bCtrlDirty = sal_False;
465 : DBG_PROFSTOP(SfxStateCacheSetState);
466 : }
467 :
468 :
469 : //--------------------------------------------------------------------
470 : // Set old status again in all the controllers
471 :
472 0 : void SfxStateCache::SetCachedState( sal_Bool bAlways )
473 : {
474 : DBG_CHKTHIS(SfxStateCache, 0);
475 : DBG_ASSERT(pController==NULL||pController->GetId()==nId, "Cache with wrong ControllerItem" );
476 : DBG_PROFSTART(SfxStateCacheSetState);
477 :
478 : // Only update if cached item exists and also able to process.
479 : // (If the State is sent, it must be ensured that a SlotServer is present,
480 : // see SfxControllerItem:: GetCoreMetric())
481 0 : if ( bAlways || ( !bItemDirty && !bSlotDirty ) )
482 : {
483 : // Update Controller
484 0 : if ( !pDispatch && pController )
485 : {
486 0 : for ( SfxControllerItem *pCtrl = pController;
487 : pCtrl;
488 : pCtrl = pCtrl->GetItemLink() )
489 0 : pCtrl->StateChanged( nId, eLastState, pLastItem );
490 : }
491 :
492 0 : if ( pInternalController )
493 0 : ((SfxDispatchController_Impl *)pInternalController)->StateChanged( nId, eLastState, pLastItem, &aSlotServ );
494 :
495 : // Controller is now ok
496 0 : bCtrlDirty = sal_True;
497 : }
498 :
499 : DBG_PROFSTOP(SfxStateCacheSetState);
500 0 : }
501 :
502 :
503 : //--------------------------------------------------------------------
504 : // Destroy FloatingWindows in all Controls with this Id
505 :
506 6990 : void SfxStateCache::DeleteFloatingWindows()
507 : {
508 : DBG_CHKTHIS(SfxStateCache, 0);
509 :
510 6990 : SfxControllerItem *pNextCtrl=0;
511 11710 : for ( SfxControllerItem *pCtrl=pController; pCtrl; pCtrl=pNextCtrl )
512 : {
513 : OSL_TRACE(rtl::OStringBuffer(RTL_CONSTASCII_STRINGPARAM("pCtrl: ")).append(reinterpret_cast<sal_Int64>(pCtrl)).getStr());
514 4720 : pNextCtrl = pCtrl->GetItemLink();
515 4720 : pCtrl->DeleteFloatingWindow();
516 : }
517 6990 : }
518 :
519 104 : ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > SfxStateCache::GetDispatch() const
520 : {
521 104 : if ( pDispatch )
522 0 : return pDispatch->xDisp;
523 104 : return ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
524 : }
525 :
526 0 : void SfxStateCache::Dispatch( const SfxItemSet* pSet, sal_Bool bForceSynchron )
527 : {
528 : // protect pDispatch against destruction in the call
529 0 : ::com::sun::star::uno::Reference < ::com::sun::star::frame::XStatusListener > xKeepAlive( pDispatch );
530 0 : if ( pDispatch )
531 : {
532 0 : uno::Sequence < beans::PropertyValue > aArgs;
533 0 : if (pSet)
534 0 : TransformItems( nId, *pSet, aArgs );
535 0 : pDispatch->Dispatch( aArgs, bForceSynchron );
536 0 : }
537 0 : }
538 :
539 :
540 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|