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 //-----------------------------------------------------------------------------
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
)
66 , pCache( pStateCache
)
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
)
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
)
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
);
93 SfxPoolItem
*pItem
=NULL
;
94 sal_uInt16 nId
= pCache
->GetId();
95 SfxItemState eState
= SFX_ITEM_DISABLED
;
96 if ( !aStatus
.IsEnabled
)
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;
110 pItem
= new SfxBoolItem( nId
, bTemp
);
112 else if ( pType
== ::getCppuType((const sal_uInt16
*)0) )
114 sal_uInt16 nTemp
= 0;
116 pItem
= new SfxUInt16Item( nId
, nTemp
);
118 else if ( pType
== ::getCppuType((const sal_uInt32
*)0) )
120 sal_uInt32 nTemp
= 0;
122 pItem
= new SfxUInt32Item( nId
, nTemp
);
124 else if ( pType
== ::getCppuType((const OUString
*)0) )
128 pItem
= new SfxStringItem( nId
, sTemp
);
133 pItem
= pSlot
->GetType()->CreateItem();
136 pItem
->SetWhich( nId
);
137 pItem
->PutValue( aAny
);
140 pItem
= new SfxVoidItem( nId
);
146 pItem
= new SfxVoidItem(0);
147 eState
= SFX_ITEM_UNKNOWN
;
150 for ( SfxControllerItem
*pCtrl
= pCache
->GetItemLink();
152 pCtrl
= pCtrl
->GetItemLink() )
153 pCtrl
->StateChanged( nId
, eState
, pItem
);
159 void BindDispatch_Impl::Release()
163 xDisp
->removeStatusListener( (::com::sun::star::frame::XStatusListener
*) this, aURL
);
164 xDisp
= ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatch
> ();
171 const ::com::sun::star::frame::FeatureStateEvent
& BindDispatch_Impl::GetStatus() const
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
):
194 pInternalController(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
) )
217 pDispatch
->Release();
222 //--------------------------------------------------------------------
223 // invalidates the cache (next request will force update)
224 void SfxStateCache::Invalidate( sal_Bool bWithMsg
)
226 bCtrlDirty
= sal_True
;
229 bSlotDirty
= sal_True
;
230 aSlotServ
.SetSlot( 0 );
233 pDispatch
->Release();
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);
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
256 const SfxSlot
* pSlot
= aSlotServ
.GetSlot();
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() );
274 aURL
.Complete
= 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 );
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
;
286 sal_Int64 nImplementation
= xTunnel
->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier());
287 pDisp
= reinterpret_cast< SfxOfficeDispatch
* >(sal::static_int_cast
< sal_IntPtr
>( nImplementation
));
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
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
;
368 if ( IsInvalidItem(pLastItem
) || ( pLastItem
== NULL
))
370 pState
= new SfxVoidItem( nId
);
371 bDeleteItem
= sal_True
;
380 pState
= new SfxVisibilityItem( nId
, sal_False
);
381 bDeleteItem
= sal_True
;
385 if ( !pDispatch
&& pController
)
387 for ( SfxControllerItem
*pCtrl
= pController
;
389 pCtrl
= pCtrl
->GetItemLink() )
390 pCtrl
->StateChanged( nId
, eState
, pState
);
393 if ( pInternalController
)
394 pInternalController
->StateChanged( nId
, eState
, pState
);
401 //--------------------------------------------------------------------
403 void SfxStateCache::SetState_Impl
405 SfxItemState eState
, // <SfxItemState> from 'pState'
406 const SfxPoolItem
* pState
, // Slot Status, 0 or -1
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
)
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
;
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
;
433 bNotify
= ( pState
!= pLastItem
) || ( eState
!= eLastState
);
439 if ( !pDispatch
&& pController
)
441 for ( SfxControllerItem
*pCtrl
= pController
;
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
) )
453 if ( pState
&& !IsInvalidItem(pState
) )
454 pLastItem
= pState
->Clone();
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
) )
481 if ( !pDispatch
&& pController
)
483 for ( SfxControllerItem
*pCtrl
= pController
;
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
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
);
528 uno::Sequence
< beans::PropertyValue
> aArgs
;
530 TransformItems( nId
, *pSet
, aArgs
);
531 pDispatch
->Dispatch( aArgs
, bForceSynchron
);
536 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */