Bump version to 4.1-6
[LibreOffice.git] / sfx2 / source / control / statcach.cxx
blob5b42f5fb8b8efec04bc5172e682a350e5cac72af
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #ifdef SOLARIS
22 #include <ctime>
23 #endif
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>
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>
53 using namespace ::com::sun::star;
54 using namespace ::com::sun::star::uno;
55 using namespace ::com::sun::star::util;
57 //====================================================================
59 DBG_NAME(SfxStateCache)
60 DBG_NAME(SfxStateCacheSetState)
62 SFX_IMPL_XINTERFACE_2( BindDispatch_Impl, OWeakObject, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener )
63 SFX_IMPL_XTYPEPROVIDER_2( BindDispatch_Impl, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener )
65 //-----------------------------------------------------------------------------
66 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 , pSlot( pS )
72 DBG_ASSERT( pCache && pSlot, "Invalid BindDispatch!");
73 aStatus.IsEnabled = sal_True;
76 void SAL_CALL BindDispatch_Impl::disposing( const ::com::sun::star::lang::EventObject& ) throw( ::com::sun::star::uno::RuntimeException )
78 if ( xDisp.is() )
80 xDisp->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aURL );
81 xDisp = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
85 void SAL_CALL BindDispatch_Impl::statusChanged( const ::com::sun::star::frame::FeatureStateEvent& rEvent ) throw( ::com::sun::star::uno::RuntimeException )
87 aStatus = rEvent;
88 if ( !pCache )
89 return;
91 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > xRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY );
92 if ( aStatus.Requery )
93 pCache->Invalidate( sal_True );
94 else
96 SfxPoolItem *pItem=NULL;
97 sal_uInt16 nId = pCache->GetId();
98 SfxItemState eState = SFX_ITEM_DISABLED;
99 if ( !aStatus.IsEnabled )
101 // default
103 else if (aStatus.State.hasValue())
105 eState = SFX_ITEM_AVAILABLE;
106 ::com::sun::star::uno::Any aAny = aStatus.State;
108 ::com::sun::star::uno::Type pType = aAny.getValueType();
109 if ( pType == ::getBooleanCppuType() )
111 sal_Bool bTemp = false;
112 aAny >>= bTemp ;
113 pItem = new SfxBoolItem( nId, bTemp );
115 else if ( pType == ::getCppuType((const sal_uInt16*)0) )
117 sal_uInt16 nTemp = 0;
118 aAny >>= nTemp ;
119 pItem = new SfxUInt16Item( nId, nTemp );
121 else if ( pType == ::getCppuType((const sal_uInt32*)0) )
123 sal_uInt32 nTemp = 0;
124 aAny >>= nTemp ;
125 pItem = new SfxUInt32Item( nId, nTemp );
127 else if ( pType == ::getCppuType((const OUString*)0) )
129 OUString sTemp ;
130 aAny >>= sTemp ;
131 pItem = new SfxStringItem( nId, sTemp );
133 else
135 if ( pSlot )
136 pItem = pSlot->GetType()->CreateItem();
137 if ( pItem )
139 pItem->SetWhich( nId );
140 pItem->PutValue( aAny );
142 else
143 pItem = new SfxVoidItem( nId );
146 else
148 // DONTCARE status
149 pItem = new SfxVoidItem(0);
150 eState = SFX_ITEM_UNKNOWN;
153 for ( SfxControllerItem *pCtrl = pCache->GetItemLink();
154 pCtrl;
155 pCtrl = pCtrl->GetItemLink() )
156 pCtrl->StateChanged( nId, eState, pItem );
158 delete pItem;
162 void BindDispatch_Impl::Release()
164 if ( xDisp.is() )
166 xDisp->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aURL );
167 xDisp = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
170 pCache = NULL;
171 release();
174 const ::com::sun::star::frame::FeatureStateEvent& BindDispatch_Impl::GetStatus() const
176 return aStatus;
179 void BindDispatch_Impl::Dispatch( uno::Sequence < beans::PropertyValue > aProps, sal_Bool bForceSynchron )
181 if ( xDisp.is() && aStatus.IsEnabled )
183 sal_Int32 nLength = aProps.getLength();
184 aProps.realloc(nLength+1);
185 aProps[nLength].Name = "SynchronMode";
186 aProps[nLength].Value <<= bForceSynchron ;
187 xDisp->dispatch( aURL, aProps );
191 //--------------------------------------------------------------------
192 // This constructor for an invalid cache that is updated in the first request.
194 SfxStateCache::SfxStateCache( sal_uInt16 nFuncId ):
195 pDispatch( 0 ),
196 nId(nFuncId),
197 pInternalController(0),
198 pController(0),
199 pLastItem( 0 ),
200 eLastState( 0 ),
201 bItemVisible( sal_True )
203 DBG_CTOR(SfxStateCache, 0);
204 bCtrlDirty = sal_True;
205 bSlotDirty = sal_True;
206 bItemDirty = sal_True;
209 //--------------------------------------------------------------------
210 // The Destructor checks by assertion, even if controllers are registered.
212 SfxStateCache::~SfxStateCache()
214 DBG_DTOR(SfxStateCache, 0);
215 DBG_ASSERT( pController == 0 && pInternalController == 0, "there are still Controllers registered" );
216 if ( !IsInvalidItem(pLastItem) )
217 delete pLastItem;
218 if ( pDispatch )
220 pDispatch->Release();
221 pDispatch = NULL;
225 //--------------------------------------------------------------------
226 // invalidates the cache (next request will force update)
227 void SfxStateCache::Invalidate( sal_Bool bWithMsg )
229 bCtrlDirty = sal_True;
230 if ( bWithMsg )
232 bSlotDirty = sal_True;
233 aSlotServ.SetSlot( 0 );
234 if ( pDispatch )
236 pDispatch->Release();
237 pDispatch = NULL;
242 //--------------------------------------------------------------------
243 // gets the corresponding function from the dispatcher or the cache
245 const SfxSlotServer* SfxStateCache::GetSlotServer( SfxDispatcher &rDispat , const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider > & xProv )
247 DBG_CHKTHIS(SfxStateCache, 0);
249 if ( bSlotDirty )
251 // get the SlotServer; we need it for internal controllers anyway, but also in most cases
252 rDispat._FindServer( nId, aSlotServ, sal_False );
254 DBG_ASSERT( !pDispatch, "Old Dispatch not removed!" );
256 // we don't need to check the dispatch provider if we only have an internal controller
257 if ( xProv.is() )
259 const SfxSlot* pSlot = aSlotServ.GetSlot();
260 if ( !pSlot )
261 // get the slot - even if it is disabled on the dispatcher
262 pSlot = SfxSlotPool::GetSlotPool( rDispat.GetFrame() ).GetSlot( nId );
264 if ( !pSlot || !pSlot->pUnoName )
266 bSlotDirty = sal_False;
267 bCtrlDirty = sal_True;
268 return aSlotServ.GetSlot()? &aSlotServ: 0;
271 // create the dispatch URL from the slot data
272 ::com::sun::star::util::URL aURL;
273 OUString aCmd = ".uno:";
274 aURL.Protocol = aCmd;
275 aURL.Path = OUString::createFromAscii( pSlot->GetUnoName() );
276 aCmd += aURL.Path;
277 aURL.Complete = aCmd;
278 aURL.Main = aCmd;
280 // try to get a dispatch object for this command
281 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
282 if ( xDisp.is() )
284 // test the dispatch object if it is just a wrapper for a SfxDispatcher
285 ::com::sun::star::uno::Reference< ::com::sun::star::lang::XUnoTunnel > xTunnel( xDisp, ::com::sun::star::uno::UNO_QUERY );
286 SfxOfficeDispatch* pDisp = NULL;
287 if ( xTunnel.is() )
289 sal_Int64 nImplementation = xTunnel->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier());
290 pDisp = reinterpret_cast< SfxOfficeDispatch* >(sal::static_int_cast< sal_IntPtr >( nImplementation ));
293 if ( pDisp )
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 SfxDispatcher *pDispatcher = pDisp->GetDispatcher_Impl();
299 if ( pDispatcher == &rDispat || pDispatcher == SFX_APP()->GetAppDispatcher_Impl() )
301 // so we can use it directly
302 bSlotDirty = sal_False;
303 bCtrlDirty = sal_True;
304 return aSlotServ.GetSlot()? &aSlotServ: 0;
308 // so the dispatch object isn't a SfxDispatcher wrapper or it is one, but it uses another dispatcher, but not rDispat
309 pDispatch = new BindDispatch_Impl( xDisp, aURL, this, pSlot );
310 pDispatch->acquire();
312 // flags must be set before adding StatusListener because the dispatch object will set the state
313 bSlotDirty = sal_False;
314 bCtrlDirty = sal_True;
315 xDisp->addStatusListener( pDispatch, aURL );
317 else if ( rDispat.GetFrame() )
319 ::com::sun::star::uno::Reference < ::com::sun::star::frame::XDispatchProvider > xFrameProv(
320 rDispat.GetFrame()->GetFrame().GetFrameInterface(), ::com::sun::star::uno::UNO_QUERY );
321 if ( xFrameProv != xProv )
322 return GetSlotServer( rDispat, xFrameProv );
326 bSlotDirty = sal_False;
327 bCtrlDirty = sal_True;
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 return aSlotServ.GetSlot()? &aSlotServ: 0;
336 //--------------------------------------------------------------------
338 // Set Status in all Controllers
340 void SfxStateCache::SetState
342 SfxItemState eState, // <SfxItemState> from 'pState'
343 const SfxPoolItem* pState, // Slot Status, 0 or -1
344 sal_Bool bMaybeDirty
347 /* [Description]
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.
355 SetState_Impl( eState, pState, bMaybeDirty );
358 //--------------------------------------------------------------------
360 void SfxStateCache::SetVisibleState( sal_Bool bShow )
362 SfxItemState eState( SFX_ITEM_AVAILABLE );
363 const SfxPoolItem* pState( NULL );
364 sal_Bool bDeleteItem( sal_False );
366 if ( bShow != bItemVisible )
368 bItemVisible = bShow;
369 if ( bShow )
371 if ( IsInvalidItem(pLastItem) || ( pLastItem == NULL ))
373 pState = new SfxVoidItem( nId );
374 bDeleteItem = sal_True;
376 else
377 pState = pLastItem;
379 eState = eLastState;
381 else
383 pState = new SfxVisibilityItem( nId, sal_False );
384 bDeleteItem = sal_True;
387 // Update Controller
388 if ( !pDispatch && pController )
390 for ( SfxControllerItem *pCtrl = pController;
391 pCtrl;
392 pCtrl = pCtrl->GetItemLink() )
393 pCtrl->StateChanged( nId, eState, pState );
396 if ( pInternalController )
397 pInternalController->StateChanged( nId, eState, pState );
399 if ( bDeleteItem )
400 delete pState;
404 //--------------------------------------------------------------------
406 void SfxStateCache::SetState_Impl
408 SfxItemState eState, // <SfxItemState> from 'pState'
409 const SfxPoolItem* pState, // Slot Status, 0 or -1
410 sal_Bool bMaybeDirty
413 (void)bMaybeDirty; //unused
414 DBG_CHKTHIS(SfxStateCache, 0);
416 // If a hard update occurs between enter- and leave-registrations is a
417 // can also intermediate Cached exist without controller.
418 if ( !pController && !pInternalController )
419 return;
421 DBG_ASSERT( bMaybeDirty || !bSlotDirty, "setting state of dirty message" );
422 DBG_ASSERT( SfxControllerItem::GetItemState(pState) == eState, "invalid SfxItemState" );
423 DBG_PROFSTART(SfxStateCacheSetState);
425 // does the controller have to be notified at all?
426 bool bNotify = bItemDirty;
427 if ( !bItemDirty )
429 bool bBothAvailable = pLastItem && pState &&
430 !IsInvalidItem(pState) && !IsInvalidItem(pLastItem);
431 DBG_ASSERT( !bBothAvailable || pState != pLastItem, "setting state with own item" );
432 if ( bBothAvailable )
433 bNotify = pState->Type() != pLastItem->Type() ||
434 *pState != *pLastItem;
435 else
436 bNotify = ( pState != pLastItem ) || ( eState != eLastState );
439 if ( bNotify )
441 // Update Controller
442 if ( !pDispatch && pController )
444 for ( SfxControllerItem *pCtrl = pController;
445 pCtrl;
446 pCtrl = pCtrl->GetItemLink() )
447 pCtrl->StateChanged( nId, eState, pState );
450 if ( pInternalController )
451 ((SfxDispatchController_Impl *)pInternalController)->StateChanged( nId, eState, pState, &aSlotServ );
453 // Remember new value
454 if ( !IsInvalidItem(pLastItem) )
455 DELETEZ(pLastItem);
456 if ( pState && !IsInvalidItem(pState) )
457 pLastItem = pState->Clone();
458 else
459 pLastItem = 0;
460 eLastState = eState;
461 bItemDirty = sal_False;
464 bCtrlDirty = sal_False;
465 DBG_PROFSTOP(SfxStateCacheSetState);
469 //--------------------------------------------------------------------
470 // Set old status again in all the controllers
472 void SfxStateCache::SetCachedState( sal_Bool bAlways )
474 DBG_CHKTHIS(SfxStateCache, 0);
475 DBG_ASSERT(pController==NULL||pController->GetId()==nId, "Cache with wrong ControllerItem" );
476 DBG_PROFSTART(SfxStateCacheSetState);
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 if ( bAlways || ( !bItemDirty && !bSlotDirty ) )
483 // Update Controller
484 if ( !pDispatch && pController )
486 for ( SfxControllerItem *pCtrl = pController;
487 pCtrl;
488 pCtrl = pCtrl->GetItemLink() )
489 pCtrl->StateChanged( nId, eLastState, pLastItem );
492 if ( pInternalController )
493 ((SfxDispatchController_Impl *)pInternalController)->StateChanged( nId, eLastState, pLastItem, &aSlotServ );
495 // Controller is now ok
496 bCtrlDirty = sal_True;
499 DBG_PROFSTOP(SfxStateCacheSetState);
503 //--------------------------------------------------------------------
504 // Destroy FloatingWindows in all Controls with this Id
506 void SfxStateCache::DeleteFloatingWindows()
508 DBG_CHKTHIS(SfxStateCache, 0);
510 SfxControllerItem *pNextCtrl=0;
511 for ( SfxControllerItem *pCtrl=pController; pCtrl; pCtrl=pNextCtrl )
513 pNextCtrl = pCtrl->GetItemLink();
514 pCtrl->DeleteFloatingWindow();
518 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > SfxStateCache::GetDispatch() const
520 if ( pDispatch )
521 return pDispatch->xDisp;
522 return ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
525 void SfxStateCache::Dispatch( const SfxItemSet* pSet, sal_Bool bForceSynchron )
527 // protect pDispatch against destruction in the call
528 ::com::sun::star::uno::Reference < ::com::sun::star::frame::XStatusListener > xKeepAlive( pDispatch );
529 if ( pDispatch )
531 uno::Sequence < beans::PropertyValue > aArgs;
532 if (pSet)
533 TransformItems( nId, *pSet, aArgs );
534 pDispatch->Dispatch( aArgs, bForceSynchron );
539 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */