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 "statcach.hxx"
43 #include <sfx2/msg.hxx>
44 #include <sfx2/ctrlitem.hxx>
45 #include <sfx2/dispatch.hxx>
46 #include "sfxtypes.hxx"
47 #include <sfx2/sfxuno.hxx>
48 #include <sfx2/unoctitm.hxx>
49 #include <sfx2/msgpool.hxx>
50 #include <sfx2/viewfrm.hxx>
52 using namespace ::com::sun::star
;
53 using namespace ::com::sun::star::uno
;
54 using namespace ::com::sun::star::util
;
56 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
)
59 , pCache( pStateCache
)
62 DBG_ASSERT( pCache
&& pSlot
, "Invalid BindDispatch!");
63 aStatus
.IsEnabled
= sal_True
;
66 void SAL_CALL
BindDispatch_Impl::disposing( const ::com::sun::star::lang::EventObject
& ) throw( ::com::sun::star::uno::RuntimeException
, std::exception
)
70 xDisp
->removeStatusListener( (::com::sun::star::frame::XStatusListener
*) this, aURL
);
71 xDisp
= ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatch
> ();
75 void SAL_CALL
BindDispatch_Impl::statusChanged( const ::com::sun::star::frame::FeatureStateEvent
& rEvent
) throw( ::com::sun::star::uno::RuntimeException
, std::exception
)
81 ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XStatusListener
> xRef( (::cppu::OWeakObject
*)this, ::com::sun::star::uno::UNO_QUERY
);
82 if ( aStatus
.Requery
)
83 pCache
->Invalidate( true );
86 SfxPoolItem
*pItem
=NULL
;
87 sal_uInt16 nId
= pCache
->GetId();
88 SfxItemState eState
= SfxItemState::DISABLED
;
89 if ( !aStatus
.IsEnabled
)
93 else if (aStatus
.State
.hasValue())
95 eState
= SfxItemState::DEFAULT
;
96 ::com::sun::star::uno::Any aAny
= aStatus
.State
;
98 ::com::sun::star::uno::Type pType
= aAny
.getValueType();
99 if ( pType
== cppu::UnoType
< bool >::get() )
103 pItem
= new SfxBoolItem( nId
, bTemp
);
105 else if ( pType
== ::cppu::UnoType
< ::cppu::UnoUnsignedShortType
>::get() )
107 sal_uInt16 nTemp
= 0;
109 pItem
= new SfxUInt16Item( nId
, nTemp
);
111 else if ( pType
== cppu::UnoType
<sal_uInt32
>::get() )
113 sal_uInt32 nTemp
= 0;
115 pItem
= new SfxUInt32Item( nId
, nTemp
);
117 else if ( pType
== cppu::UnoType
<OUString
>::get() )
121 pItem
= new SfxStringItem( nId
, sTemp
);
126 pItem
= pSlot
->GetType()->CreateItem();
129 pItem
->SetWhich( nId
);
130 pItem
->PutValue( aAny
);
133 pItem
= new SfxVoidItem( nId
);
139 pItem
= new SfxVoidItem(0);
140 eState
= SfxItemState::UNKNOWN
;
143 for ( SfxControllerItem
*pCtrl
= pCache
->GetItemLink();
145 pCtrl
= pCtrl
->GetItemLink() )
146 pCtrl
->StateChanged( nId
, eState
, pItem
);
152 void BindDispatch_Impl::Release()
156 xDisp
->removeStatusListener( (::com::sun::star::frame::XStatusListener
*) this, aURL
);
157 xDisp
= ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatch
> ();
165 void BindDispatch_Impl::Dispatch( const uno::Sequence
< beans::PropertyValue
>& aProps
, bool bForceSynchron
)
167 if ( xDisp
.is() && aStatus
.IsEnabled
)
169 sal_Int32 nLength
= aProps
.getLength();
170 uno::Sequence
< beans::PropertyValue
> aProps2
= aProps
;
171 aProps2
.realloc(nLength
+1);
172 aProps2
[nLength
].Name
= "SynchronMode";
173 aProps2
[nLength
].Value
<<= bForceSynchron
;
174 xDisp
->dispatch( aURL
, aProps2
);
179 // This constructor for an invalid cache that is updated in the first request.
181 SfxStateCache::SfxStateCache( sal_uInt16 nFuncId
):
184 pInternalController(0),
187 eLastState( SfxItemState::UNKNOWN
),
196 // The Destructor checks by assertion, even if controllers are registered.
198 SfxStateCache::~SfxStateCache()
200 DBG_ASSERT( pController
== 0 && pInternalController
== 0, "there are still Controllers registered" );
201 if ( !IsInvalidItem(pLastItem
) )
205 pDispatch
->Release();
211 // invalidates the cache (next request will force update)
212 void SfxStateCache::Invalidate( bool bWithMsg
)
218 aSlotServ
.SetSlot( 0 );
221 pDispatch
->Release();
228 // gets the corresponding function from the dispatcher or the cache
230 const SfxSlotServer
* SfxStateCache::GetSlotServer( SfxDispatcher
&rDispat
, const ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatchProvider
> & xProv
)
235 // get the SlotServer; we need it for internal controllers anyway, but also in most cases
236 rDispat
._FindServer( nId
, aSlotServ
, false );
238 DBG_ASSERT( !pDispatch
, "Old Dispatch not removed!" );
240 // we don't need to check the dispatch provider if we only have an internal controller
243 const SfxSlot
* pSlot
= aSlotServ
.GetSlot();
245 // get the slot - even if it is disabled on the dispatcher
246 pSlot
= SfxSlotPool::GetSlotPool( rDispat
.GetFrame() ).GetSlot( nId
);
248 if ( !pSlot
|| !pSlot
->pUnoName
)
252 return aSlotServ
.GetSlot()? &aSlotServ
: 0;
255 // create the dispatch URL from the slot data
256 ::com::sun::star::util::URL aURL
;
257 OUString aCmd
= ".uno:";
258 aURL
.Protocol
= aCmd
;
259 aURL
.Path
= OUString::createFromAscii( pSlot
->GetUnoName() );
261 aURL
.Complete
= aCmd
;
264 // try to get a dispatch object for this command
265 ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatch
> xDisp
= xProv
->queryDispatch( aURL
, OUString(), 0 );
268 // test the dispatch object if it is just a wrapper for a SfxDispatcher
269 ::com::sun::star::uno::Reference
< ::com::sun::star::lang::XUnoTunnel
> xTunnel( xDisp
, ::com::sun::star::uno::UNO_QUERY
);
270 SfxOfficeDispatch
* pDisp
= NULL
;
273 sal_Int64 nImplementation
= xTunnel
->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier());
274 pDisp
= reinterpret_cast< SfxOfficeDispatch
* >(sal::static_int_cast
< sal_IntPtr
>( nImplementation
));
279 // The intercepting object is an SFX component
280 // If this dispatch object does not use the wanted dispatcher or the AppDispatcher, it's treated like any other UNO component
281 // (intercepting by internal dispatches)
282 SfxDispatcher
*pDispatcher
= pDisp
->GetDispatcher_Impl();
283 if ( pDispatcher
== &rDispat
|| pDispatcher
== SfxGetpApp()->GetAppDispatcher_Impl() )
285 // so we can use it directly
288 return aSlotServ
.GetSlot()? &aSlotServ
: 0;
292 // so the dispatch object isn't a SfxDispatcher wrapper or it is one, but it uses another dispatcher, but not rDispat
293 pDispatch
= new BindDispatch_Impl( xDisp
, aURL
, this, pSlot
);
294 pDispatch
->acquire();
296 // flags must be set before adding StatusListener because the dispatch object will set the state
299 xDisp
->addStatusListener( pDispatch
, aURL
);
301 else if ( rDispat
.GetFrame() )
303 ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatchProvider
> xFrameProv(
304 rDispat
.GetFrame()->GetFrame().GetFrameInterface(), ::com::sun::star::uno::UNO_QUERY
);
305 if ( xFrameProv
!= xProv
)
306 return GetSlotServer( rDispat
, xFrameProv
);
314 // we *always* return a SlotServer (if there is one); but in case of an external dispatch we might not use it
315 // for the "real" (non internal) controllers
316 return aSlotServ
.GetSlot()? &aSlotServ
: 0;
322 // Set Status in all Controllers
324 void SfxStateCache::SetState
326 SfxItemState eState
, // <SfxItemState> from 'pState'
327 const SfxPoolItem
* pState
, // Slot Status, 0 or -1
333 This method distributes the status of all of this SID bound
334 <SfxControllerItem>s. If the value is the same as before, and if neither
335 controller was registered nor invalidated inbetween, then no value is
336 passed. This way the flickering is for example avoided in ListBoxes.
339 SetState_Impl( eState
, pState
, bMaybeDirty
);
344 void SfxStateCache::SetVisibleState( bool bShow
)
346 if ( bShow
!= bItemVisible
)
348 SfxItemState
eState( SfxItemState::DEFAULT
);
349 const SfxPoolItem
* pState( NULL
);
350 bool bDeleteItem( false );
352 bItemVisible
= bShow
;
355 if ( IsInvalidItem(pLastItem
) || ( pLastItem
== NULL
))
357 pState
= new SfxVoidItem( nId
);
367 pState
= new SfxVisibilityItem( nId
, false );
372 if ( !pDispatch
&& pController
)
374 for ( SfxControllerItem
*pCtrl
= pController
;
376 pCtrl
= pCtrl
->GetItemLink() )
377 pCtrl
->StateChanged( nId
, eState
, pState
);
380 if ( pInternalController
)
381 pInternalController
->StateChanged( nId
, eState
, pState
);
390 void SfxStateCache::SetState_Impl
392 SfxItemState eState
, // <SfxItemState> from 'pState'
393 const SfxPoolItem
* pState
, // Slot Status, 0 or -1
397 (void)bMaybeDirty
; //unused
399 // If a hard update occurs between enter- and leave-registrations is a
400 // can also intermediate Cached exist without controller.
401 if ( !pController
&& !pInternalController
)
404 DBG_ASSERT( bMaybeDirty
|| !bSlotDirty
, "setting state of dirty message" );
405 DBG_ASSERT( SfxControllerItem::GetItemState(pState
) == eState
, "invalid SfxItemState" );
407 // does the controller have to be notified at all?
408 bool bNotify
= bItemDirty
;
411 bool bBothAvailable
= pLastItem
&& pState
&&
412 !IsInvalidItem(pState
) && !IsInvalidItem(pLastItem
);
413 DBG_ASSERT( !bBothAvailable
|| pState
!= pLastItem
, "setting state with own item" );
414 if ( bBothAvailable
)
415 bNotify
= pState
->Type() != pLastItem
->Type() ||
416 *pState
!= *pLastItem
;
418 bNotify
= ( pState
!= pLastItem
) || ( eState
!= eLastState
);
424 if ( !pDispatch
&& pController
)
426 for ( SfxControllerItem
*pCtrl
= pController
;
428 pCtrl
= pCtrl
->GetItemLink() )
429 pCtrl
->StateChanged( nId
, eState
, pState
);
432 if ( pInternalController
)
433 static_cast<SfxDispatchController_Impl
*>(pInternalController
)->StateChanged( nId
, eState
, pState
, &aSlotServ
);
435 // Remember new value
436 if ( !IsInvalidItem(pLastItem
) )
438 if ( pState
&& !IsInvalidItem(pState
) )
439 pLastItem
= pState
->Clone();
451 // Set old status again in all the controllers
453 void SfxStateCache::SetCachedState( bool bAlways
)
455 DBG_ASSERT(pController
==NULL
||pController
->GetId()==nId
, "Cache with wrong ControllerItem" );
457 // Only update if cached item exists and also able to process.
458 // (If the State is sent, it must be ensured that a SlotServer is present,
459 // see SfxControllerItem:: GetCoreMetric())
460 if ( bAlways
|| ( !bItemDirty
&& !bSlotDirty
) )
463 if ( !pDispatch
&& pController
)
465 for ( SfxControllerItem
*pCtrl
= pController
;
467 pCtrl
= pCtrl
->GetItemLink() )
468 pCtrl
->StateChanged( nId
, eLastState
, pLastItem
);
471 if ( pInternalController
)
472 static_cast<SfxDispatchController_Impl
*>(pInternalController
)->StateChanged( nId
, eLastState
, pLastItem
, &aSlotServ
);
474 // Controller is now ok
481 ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatch
> SfxStateCache::GetDispatch() const
484 return pDispatch
->xDisp
;
485 return ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XDispatch
> ();
488 void SfxStateCache::Dispatch( const SfxItemSet
* pSet
, bool bForceSynchron
)
490 // protect pDispatch against destruction in the call
491 ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XStatusListener
> xKeepAlive( pDispatch
);
494 uno::Sequence
< beans::PropertyValue
> aArgs
;
496 TransformItems( nId
, *pSet
, aArgs
);
497 pDispatch
->Dispatch( aArgs
, bForceSynchron
);
502 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */