1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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
)
69 , pCache( pStateCache
)
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
)
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
)
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
);
96 SfxPoolItem
*pItem
=NULL
;
97 sal_uInt16 nId
= pCache
->GetId();
98 SfxItemState eState
= SFX_ITEM_DISABLED
;
99 if ( !aStatus
.IsEnabled
)
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;
113 pItem
= new SfxBoolItem( nId
, bTemp
);
115 else if ( pType
== ::getCppuType((const sal_uInt16
*)0) )
117 sal_uInt16 nTemp
= 0;
119 pItem
= new SfxUInt16Item( nId
, nTemp
);
121 else if ( pType
== ::getCppuType((const sal_uInt32
*)0) )
123 sal_uInt32 nTemp
= 0;
125 pItem
= new SfxUInt32Item( nId
, nTemp
);
127 else if ( pType
== ::getCppuType((const OUString
*)0) )
131 pItem
= new SfxStringItem( nId
, sTemp
);
136 pItem
= pSlot
->GetType()->CreateItem();
139 pItem
->SetWhich( nId
);
140 pItem
->PutValue( aAny
);
143 pItem
= new SfxVoidItem( nId
);
149 pItem
= new SfxVoidItem(0);
150 eState
= SFX_ITEM_UNKNOWN
;
153 for ( SfxControllerItem
*pCtrl
= pCache
->GetItemLink();
155 pCtrl
= pCtrl
->GetItemLink() )
156 pCtrl
->StateChanged( nId
, eState
, pItem
);
162 void BindDispatch_Impl::Release()
166 xDisp
->removeStatusListener( (::com::sun::star::frame::XStatusListener
*) this, aURL
);
167 xDisp
= ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatch
> ();
174 const ::com::sun::star::frame::FeatureStateEvent
& BindDispatch_Impl::GetStatus() const
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
):
197 pInternalController(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
) )
220 pDispatch
->Release();
225 //--------------------------------------------------------------------
226 // invalidates the cache (next request will force update)
227 void SfxStateCache::Invalidate( sal_Bool bWithMsg
)
229 bCtrlDirty
= sal_True
;
232 bSlotDirty
= sal_True
;
233 aSlotServ
.SetSlot( 0 );
236 pDispatch
->Release();
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);
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
259 const SfxSlot
* pSlot
= aSlotServ
.GetSlot();
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() );
277 aURL
.Complete
= 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 );
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
;
289 sal_Int64 nImplementation
= xTunnel
->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier());
290 pDisp
= reinterpret_cast< SfxOfficeDispatch
* >(sal::static_int_cast
< sal_IntPtr
>( nImplementation
));
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
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
;
371 if ( IsInvalidItem(pLastItem
) || ( pLastItem
== NULL
))
373 pState
= new SfxVoidItem( nId
);
374 bDeleteItem
= sal_True
;
383 pState
= new SfxVisibilityItem( nId
, sal_False
);
384 bDeleteItem
= sal_True
;
388 if ( !pDispatch
&& pController
)
390 for ( SfxControllerItem
*pCtrl
= pController
;
392 pCtrl
= pCtrl
->GetItemLink() )
393 pCtrl
->StateChanged( nId
, eState
, pState
);
396 if ( pInternalController
)
397 pInternalController
->StateChanged( nId
, eState
, pState
);
404 //--------------------------------------------------------------------
406 void SfxStateCache::SetState_Impl
408 SfxItemState eState
, // <SfxItemState> from 'pState'
409 const SfxPoolItem
* pState
, // Slot Status, 0 or -1
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
)
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
;
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
;
436 bNotify
= ( pState
!= pLastItem
) || ( eState
!= eLastState
);
442 if ( !pDispatch
&& pController
)
444 for ( SfxControllerItem
*pCtrl
= pController
;
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
) )
456 if ( pState
&& !IsInvalidItem(pState
) )
457 pLastItem
= pState
->Clone();
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
) )
484 if ( !pDispatch
&& pController
)
486 for ( SfxControllerItem
*pCtrl
= pController
;
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
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
);
531 uno::Sequence
< beans::PropertyValue
> aArgs
;
533 TransformItems( nId
, *pSet
, aArgs
);
534 pDispatch
->Dispatch( aArgs
, bForceSynchron
);
539 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */