bump product version to 4.2.0.1
[LibreOffice.git] / sfx2 / source / control / statcach.cxx
blob400af6ced130e1ed50353981853a642527a6aeb9
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 //-----------------------------------------------------------------------------
63 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 )
64 : xDisp( rDisp )
65 , aURL( rURL )
66 , pCache( pStateCache )
67 , pSlot( pS )
69 DBG_ASSERT( pCache && pSlot, "Invalid BindDispatch!");
70 aStatus.IsEnabled = sal_True;
73 void SAL_CALL BindDispatch_Impl::disposing( const ::com::sun::star::lang::EventObject& ) throw( ::com::sun::star::uno::RuntimeException )
75 if ( xDisp.is() )
77 xDisp->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aURL );
78 xDisp = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
82 void SAL_CALL BindDispatch_Impl::statusChanged( const ::com::sun::star::frame::FeatureStateEvent& rEvent ) throw( ::com::sun::star::uno::RuntimeException )
84 aStatus = rEvent;
85 if ( !pCache )
86 return;
88 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > xRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY );
89 if ( aStatus.Requery )
90 pCache->Invalidate( sal_True );
91 else
93 SfxPoolItem *pItem=NULL;
94 sal_uInt16 nId = pCache->GetId();
95 SfxItemState eState = SFX_ITEM_DISABLED;
96 if ( !aStatus.IsEnabled )
98 // default
100 else if (aStatus.State.hasValue())
102 eState = SFX_ITEM_AVAILABLE;
103 ::com::sun::star::uno::Any aAny = aStatus.State;
105 ::com::sun::star::uno::Type pType = aAny.getValueType();
106 if ( pType == ::getBooleanCppuType() )
108 sal_Bool bTemp = false;
109 aAny >>= bTemp ;
110 pItem = new SfxBoolItem( nId, bTemp );
112 else if ( pType == ::getCppuType((const sal_uInt16*)0) )
114 sal_uInt16 nTemp = 0;
115 aAny >>= nTemp ;
116 pItem = new SfxUInt16Item( nId, nTemp );
118 else if ( pType == ::getCppuType((const sal_uInt32*)0) )
120 sal_uInt32 nTemp = 0;
121 aAny >>= nTemp ;
122 pItem = new SfxUInt32Item( nId, nTemp );
124 else if ( pType == ::getCppuType((const OUString*)0) )
126 OUString sTemp ;
127 aAny >>= sTemp ;
128 pItem = new SfxStringItem( nId, sTemp );
130 else
132 if ( pSlot )
133 pItem = pSlot->GetType()->CreateItem();
134 if ( pItem )
136 pItem->SetWhich( nId );
137 pItem->PutValue( aAny );
139 else
140 pItem = new SfxVoidItem( nId );
143 else
145 // DONTCARE status
146 pItem = new SfxVoidItem(0);
147 eState = SFX_ITEM_UNKNOWN;
150 for ( SfxControllerItem *pCtrl = pCache->GetItemLink();
151 pCtrl;
152 pCtrl = pCtrl->GetItemLink() )
153 pCtrl->StateChanged( nId, eState, pItem );
155 delete pItem;
159 void BindDispatch_Impl::Release()
161 if ( xDisp.is() )
163 xDisp->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aURL );
164 xDisp = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
167 pCache = NULL;
168 release();
171 const ::com::sun::star::frame::FeatureStateEvent& BindDispatch_Impl::GetStatus() const
173 return aStatus;
176 void BindDispatch_Impl::Dispatch( uno::Sequence < beans::PropertyValue > aProps, sal_Bool bForceSynchron )
178 if ( xDisp.is() && aStatus.IsEnabled )
180 sal_Int32 nLength = aProps.getLength();
181 aProps.realloc(nLength+1);
182 aProps[nLength].Name = "SynchronMode";
183 aProps[nLength].Value <<= bForceSynchron ;
184 xDisp->dispatch( aURL, aProps );
188 //--------------------------------------------------------------------
189 // This constructor for an invalid cache that is updated in the first request.
191 SfxStateCache::SfxStateCache( sal_uInt16 nFuncId ):
192 pDispatch( 0 ),
193 nId(nFuncId),
194 pInternalController(0),
195 pController(0),
196 pLastItem( 0 ),
197 eLastState( 0 ),
198 bItemVisible( sal_True )
200 DBG_CTOR(SfxStateCache, 0);
201 bCtrlDirty = sal_True;
202 bSlotDirty = sal_True;
203 bItemDirty = sal_True;
206 //--------------------------------------------------------------------
207 // The Destructor checks by assertion, even if controllers are registered.
209 SfxStateCache::~SfxStateCache()
211 DBG_DTOR(SfxStateCache, 0);
212 DBG_ASSERT( pController == 0 && pInternalController == 0, "there are still Controllers registered" );
213 if ( !IsInvalidItem(pLastItem) )
214 delete pLastItem;
215 if ( pDispatch )
217 pDispatch->Release();
218 pDispatch = NULL;
222 //--------------------------------------------------------------------
223 // invalidates the cache (next request will force update)
224 void SfxStateCache::Invalidate( sal_Bool bWithMsg )
226 bCtrlDirty = sal_True;
227 if ( bWithMsg )
229 bSlotDirty = sal_True;
230 aSlotServ.SetSlot( 0 );
231 if ( pDispatch )
233 pDispatch->Release();
234 pDispatch = NULL;
239 //--------------------------------------------------------------------
240 // gets the corresponding function from the dispatcher or the cache
242 const SfxSlotServer* SfxStateCache::GetSlotServer( SfxDispatcher &rDispat , const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider > & xProv )
244 DBG_CHKTHIS(SfxStateCache, 0);
246 if ( bSlotDirty )
248 // get the SlotServer; we need it for internal controllers anyway, but also in most cases
249 rDispat._FindServer( nId, aSlotServ, sal_False );
251 DBG_ASSERT( !pDispatch, "Old Dispatch not removed!" );
253 // we don't need to check the dispatch provider if we only have an internal controller
254 if ( xProv.is() )
256 const SfxSlot* pSlot = aSlotServ.GetSlot();
257 if ( !pSlot )
258 // get the slot - even if it is disabled on the dispatcher
259 pSlot = SfxSlotPool::GetSlotPool( rDispat.GetFrame() ).GetSlot( nId );
261 if ( !pSlot || !pSlot->pUnoName )
263 bSlotDirty = sal_False;
264 bCtrlDirty = sal_True;
265 return aSlotServ.GetSlot()? &aSlotServ: 0;
268 // create the dispatch URL from the slot data
269 ::com::sun::star::util::URL aURL;
270 OUString aCmd = ".uno:";
271 aURL.Protocol = aCmd;
272 aURL.Path = OUString::createFromAscii( pSlot->GetUnoName() );
273 aCmd += aURL.Path;
274 aURL.Complete = aCmd;
275 aURL.Main = aCmd;
277 // try to get a dispatch object for this command
278 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
279 if ( xDisp.is() )
281 // test the dispatch object if it is just a wrapper for a SfxDispatcher
282 ::com::sun::star::uno::Reference< ::com::sun::star::lang::XUnoTunnel > xTunnel( xDisp, ::com::sun::star::uno::UNO_QUERY );
283 SfxOfficeDispatch* pDisp = NULL;
284 if ( xTunnel.is() )
286 sal_Int64 nImplementation = xTunnel->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier());
287 pDisp = reinterpret_cast< SfxOfficeDispatch* >(sal::static_int_cast< sal_IntPtr >( nImplementation ));
290 if ( pDisp )
292 // The intercepting object is an SFX component
293 // If this dispatch object does not use the wanted dispatcher or the AppDispatcher, it's treated like any other UNO component
294 // (intercepting by internal dispatches)
295 SfxDispatcher *pDispatcher = pDisp->GetDispatcher_Impl();
296 if ( pDispatcher == &rDispat || pDispatcher == SFX_APP()->GetAppDispatcher_Impl() )
298 // so we can use it directly
299 bSlotDirty = sal_False;
300 bCtrlDirty = sal_True;
301 return aSlotServ.GetSlot()? &aSlotServ: 0;
305 // so the dispatch object isn't a SfxDispatcher wrapper or it is one, but it uses another dispatcher, but not rDispat
306 pDispatch = new BindDispatch_Impl( xDisp, aURL, this, pSlot );
307 pDispatch->acquire();
309 // flags must be set before adding StatusListener because the dispatch object will set the state
310 bSlotDirty = sal_False;
311 bCtrlDirty = sal_True;
312 xDisp->addStatusListener( pDispatch, aURL );
314 else if ( rDispat.GetFrame() )
316 ::com::sun::star::uno::Reference < ::com::sun::star::frame::XDispatchProvider > xFrameProv(
317 rDispat.GetFrame()->GetFrame().GetFrameInterface(), ::com::sun::star::uno::UNO_QUERY );
318 if ( xFrameProv != xProv )
319 return GetSlotServer( rDispat, xFrameProv );
323 bSlotDirty = sal_False;
324 bCtrlDirty = sal_True;
327 // we *always* return a SlotServer (if there is one); but in case of an external dispatch we might not use it
328 // for the "real" (non internal) controllers
329 return aSlotServ.GetSlot()? &aSlotServ: 0;
333 //--------------------------------------------------------------------
335 // Set Status in all Controllers
337 void SfxStateCache::SetState
339 SfxItemState eState, // <SfxItemState> from 'pState'
340 const SfxPoolItem* pState, // Slot Status, 0 or -1
341 sal_Bool bMaybeDirty
344 /* [Description]
346 This method distributes the status of all of this SID bound
347 <SfxControllerItem>s. If the value is the same as before, and if neither
348 controller was registered nor invalidated inbetween, then no value is
349 passed. This way the flickering is for example avoided in ListBoxes.
352 SetState_Impl( eState, pState, bMaybeDirty );
355 //--------------------------------------------------------------------
357 void SfxStateCache::SetVisibleState( sal_Bool bShow )
359 SfxItemState eState( SFX_ITEM_AVAILABLE );
360 const SfxPoolItem* pState( NULL );
361 sal_Bool bDeleteItem( sal_False );
363 if ( bShow != bItemVisible )
365 bItemVisible = bShow;
366 if ( bShow )
368 if ( IsInvalidItem(pLastItem) || ( pLastItem == NULL ))
370 pState = new SfxVoidItem( nId );
371 bDeleteItem = sal_True;
373 else
374 pState = pLastItem;
376 eState = eLastState;
378 else
380 pState = new SfxVisibilityItem( nId, sal_False );
381 bDeleteItem = sal_True;
384 // Update Controller
385 if ( !pDispatch && pController )
387 for ( SfxControllerItem *pCtrl = pController;
388 pCtrl;
389 pCtrl = pCtrl->GetItemLink() )
390 pCtrl->StateChanged( nId, eState, pState );
393 if ( pInternalController )
394 pInternalController->StateChanged( nId, eState, pState );
396 if ( bDeleteItem )
397 delete pState;
401 //--------------------------------------------------------------------
403 void SfxStateCache::SetState_Impl
405 SfxItemState eState, // <SfxItemState> from 'pState'
406 const SfxPoolItem* pState, // Slot Status, 0 or -1
407 sal_Bool bMaybeDirty
410 (void)bMaybeDirty; //unused
411 DBG_CHKTHIS(SfxStateCache, 0);
413 // If a hard update occurs between enter- and leave-registrations is a
414 // can also intermediate Cached exist without controller.
415 if ( !pController && !pInternalController )
416 return;
418 DBG_ASSERT( bMaybeDirty || !bSlotDirty, "setting state of dirty message" );
419 DBG_ASSERT( SfxControllerItem::GetItemState(pState) == eState, "invalid SfxItemState" );
420 DBG_PROFSTART(SfxStateCacheSetState);
422 // does the controller have to be notified at all?
423 bool bNotify = bItemDirty;
424 if ( !bItemDirty )
426 bool bBothAvailable = pLastItem && pState &&
427 !IsInvalidItem(pState) && !IsInvalidItem(pLastItem);
428 DBG_ASSERT( !bBothAvailable || pState != pLastItem, "setting state with own item" );
429 if ( bBothAvailable )
430 bNotify = pState->Type() != pLastItem->Type() ||
431 *pState != *pLastItem;
432 else
433 bNotify = ( pState != pLastItem ) || ( eState != eLastState );
436 if ( bNotify )
438 // Update Controller
439 if ( !pDispatch && pController )
441 for ( SfxControllerItem *pCtrl = pController;
442 pCtrl;
443 pCtrl = pCtrl->GetItemLink() )
444 pCtrl->StateChanged( nId, eState, pState );
447 if ( pInternalController )
448 ((SfxDispatchController_Impl *)pInternalController)->StateChanged( nId, eState, pState, &aSlotServ );
450 // Remember new value
451 if ( !IsInvalidItem(pLastItem) )
452 DELETEZ(pLastItem);
453 if ( pState && !IsInvalidItem(pState) )
454 pLastItem = pState->Clone();
455 else
456 pLastItem = 0;
457 eLastState = eState;
458 bItemDirty = sal_False;
461 bCtrlDirty = sal_False;
462 DBG_PROFSTOP(SfxStateCacheSetState);
466 //--------------------------------------------------------------------
467 // Set old status again in all the controllers
469 void SfxStateCache::SetCachedState( sal_Bool bAlways )
471 DBG_CHKTHIS(SfxStateCache, 0);
472 DBG_ASSERT(pController==NULL||pController->GetId()==nId, "Cache with wrong ControllerItem" );
473 DBG_PROFSTART(SfxStateCacheSetState);
475 // Only update if cached item exists and also able to process.
476 // (If the State is sent, it must be ensured that a SlotServer is present,
477 // see SfxControllerItem:: GetCoreMetric())
478 if ( bAlways || ( !bItemDirty && !bSlotDirty ) )
480 // Update Controller
481 if ( !pDispatch && pController )
483 for ( SfxControllerItem *pCtrl = pController;
484 pCtrl;
485 pCtrl = pCtrl->GetItemLink() )
486 pCtrl->StateChanged( nId, eLastState, pLastItem );
489 if ( pInternalController )
490 ((SfxDispatchController_Impl *)pInternalController)->StateChanged( nId, eLastState, pLastItem, &aSlotServ );
492 // Controller is now ok
493 bCtrlDirty = sal_True;
496 DBG_PROFSTOP(SfxStateCacheSetState);
500 //--------------------------------------------------------------------
501 // Destroy FloatingWindows in all Controls with this Id
503 void SfxStateCache::DeleteFloatingWindows()
505 DBG_CHKTHIS(SfxStateCache, 0);
507 SfxControllerItem *pNextCtrl=0;
508 for ( SfxControllerItem *pCtrl=pController; pCtrl; pCtrl=pNextCtrl )
510 pNextCtrl = pCtrl->GetItemLink();
511 pCtrl->DeleteFloatingWindow();
515 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > SfxStateCache::GetDispatch() const
517 if ( pDispatch )
518 return pDispatch->xDisp;
519 return ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
522 void SfxStateCache::Dispatch( const SfxItemSet* pSet, sal_Bool bForceSynchron )
524 // protect pDispatch against destruction in the call
525 ::com::sun::star::uno::Reference < ::com::sun::star::frame::XStatusListener > xKeepAlive( pDispatch );
526 if ( pDispatch )
528 uno::Sequence < beans::PropertyValue > aArgs;
529 if (pSet)
530 TransformItems( nId, *pSet, aArgs );
531 pDispatch->Dispatch( aArgs, bForceSynchron );
536 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */