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 .
25 #include <framework/dispatchhelper.hxx>
26 #include <com/sun/star/frame/DispatchResultState.hpp>
27 #include <com/sun/star/frame/XFrame.hpp>
28 #include <com/sun/star/beans/PropertyValue.hpp>
29 #include <comphelper/servicehelper.hxx>
30 #include <svl/eitem.hxx>
31 #include <svl/intitem.hxx>
32 #include <svl/stritem.hxx>
33 #include <svl/visitem.hxx>
34 #include <svl/voiditem.hxx>
36 #include <sfx2/app.hxx>
37 #include <statcach.hxx>
38 #include <sfx2/msg.hxx>
39 #include <sfx2/ctrlitem.hxx>
40 #include <sfx2/dispatch.hxx>
41 #include <sfx2/sfxuno.hxx>
42 #include <unoctitm.hxx>
43 #include <sfx2/msgpool.hxx>
44 #include <sfx2/viewfrm.hxx>
46 #include <comphelper/diagnose_ex.hxx>
48 using namespace ::com::sun::star
;
49 using namespace ::com::sun::star::uno
;
50 using namespace ::com::sun::star::util
;
52 BindDispatch_Impl::BindDispatch_Impl( css::uno::Reference
< css::frame::XDispatch
> _xDisp
, css::util::URL _aURL
, SfxStateCache
*pStateCache
, const SfxSlot
* pS
)
53 : xDisp(std::move( _xDisp
))
54 , aURL(std::move( _aURL
))
55 , pCache( pStateCache
)
58 DBG_ASSERT( pCache
&& pSlot
, "Invalid BindDispatch!");
59 aStatus
.IsEnabled
= true;
62 void SAL_CALL
BindDispatch_Impl::disposing( const css::lang::EventObject
& )
66 xDisp
->removeStatusListener( static_cast<css::frame::XStatusListener
*>(this), aURL
);
71 void SAL_CALL
BindDispatch_Impl::statusChanged( const css::frame::FeatureStateEvent
& rEvent
)
77 css::uno::Reference
< css::frame::XStatusListener
> xKeepAlive(this);
78 if ( aStatus
.Requery
)
79 pCache
->Invalidate( true );
82 std::unique_ptr
<SfxPoolItem
> pItem
;
83 sal_uInt16 nId
= pCache
->GetId();
84 SfxItemState eState
= SfxItemState::DISABLED
;
85 if ( !aStatus
.IsEnabled
)
89 else if (aStatus
.State
.hasValue())
91 eState
= SfxItemState::DEFAULT
;
92 css::uno::Any aAny
= aStatus
.State
;
94 const css::uno::Type
& aType
= aAny
.getValueType();
95 if ( aType
== cppu::UnoType
< bool >::get() )
99 pItem
.reset( new SfxBoolItem( nId
, bTemp
) );
101 else if ( aType
== ::cppu::UnoType
< ::cppu::UnoUnsignedShortType
>::get() )
103 sal_uInt16 nTemp
= 0;
105 pItem
.reset( new SfxUInt16Item( nId
, nTemp
) );
107 else if ( aType
== cppu::UnoType
<sal_uInt32
>::get() )
109 sal_uInt32 nTemp
= 0;
111 pItem
.reset( new SfxUInt32Item( nId
, nTemp
) );
113 else if ( aType
== cppu::UnoType
<OUString
>::get() )
117 pItem
.reset( new SfxStringItem( nId
, sTemp
) );
122 pItem
= pSlot
->GetType()->CreateItem();
125 pItem
->SetWhich( nId
);
126 pItem
->PutValue( aAny
, 0 );
129 pItem
.reset( new SfxVoidItem( nId
) );
135 pItem
.reset( new SfxVoidItem(0) );
136 eState
= SfxItemState::UNKNOWN
;
139 for ( SfxControllerItem
*pCtrl
= pCache
->GetItemLink();
141 pCtrl
= pCtrl
->GetItemLink() )
142 pCtrl
->StateChangedAtToolBoxControl( nId
, eState
, pItem
.get() );
146 void BindDispatch_Impl::Release()
152 xDisp
->removeStatusListener(static_cast<css::frame::XStatusListener
*>(this), aURL
);
154 catch (const lang::DisposedException
&)
156 TOOLS_WARN_EXCEPTION("sfx", "BindDispatch_Impl::Release: xDisp is disposed: ");
164 sal_Int16
BindDispatch_Impl::Dispatch( const css::uno::Sequence
< css::beans::PropertyValue
>& aProps
, bool bForceSynchron
)
166 sal_Int16 eRet
= css::frame::DispatchResultState::DONTKNOW
;
168 if ( xDisp
.is() && aStatus
.IsEnabled
)
170 ::rtl::Reference
< ::framework::DispatchHelper
> xHelper( new ::framework::DispatchHelper(nullptr));
171 css::uno::Any aResult
= xHelper
->executeDispatch(xDisp
, aURL
, bForceSynchron
, aProps
);
173 css::frame::DispatchResultEvent aEvent
;
183 // This constructor for an invalid cache that is updated in the first request.
185 SfxStateCache::SfxStateCache( sal_uInt16 nFuncId
):
187 pInternalController(nullptr),
188 pController(nullptr),
189 pLastItem( nullptr ),
190 eLastState( SfxItemState::UNKNOWN
),
199 // The Destructor checks by assertion, even if controllers are registered.
201 SfxStateCache::~SfxStateCache()
203 DBG_ASSERT( pController
== nullptr && pInternalController
== nullptr, "there are still Controllers registered" );
204 if ( !IsInvalidItem(pLastItem
) )
206 if ( mxDispatch
.is() )
207 mxDispatch
->Release();
211 // invalidates the cache (next request will force update)
212 void SfxStateCache::Invalidate( bool bWithMsg
)
218 aSlotServ
.SetSlot( nullptr );
219 if ( mxDispatch
.is() )
220 mxDispatch
->Release();
226 // gets the corresponding function from the dispatcher or the cache
228 const SfxSlotServer
* SfxStateCache::GetSlotServer( SfxDispatcher
&rDispat
, const css::uno::Reference
< css::frame::XDispatchProvider
> & xProv
)
233 // get the SlotServer; we need it for internal controllers anyway, but also in most cases
234 rDispat
.FindServer_( nId
, aSlotServ
);
236 DBG_ASSERT( !mxDispatch
.is(), "Old Dispatch not removed!" );
238 // we don't need to check the dispatch provider if we only have an internal controller
241 const SfxSlot
* pSlot
= aSlotServ
.GetSlot();
243 // get the slot - even if it is disabled on the dispatcher
244 pSlot
= SfxSlotPool::GetSlotPool( rDispat
.GetFrame() ).GetSlot( nId
);
246 if ( !pSlot
|| pSlot
->pUnoName
.isEmpty() )
250 return aSlotServ
.GetSlot()? &aSlotServ
: nullptr;
253 // create the dispatch URL from the slot data
255 OUString aCmd
= ".uno:";
256 aURL
.Protocol
= aCmd
;
257 aURL
.Path
= pSlot
->GetUnoName();
259 aURL
.Complete
= aCmd
;
262 // try to get a dispatch object for this command
263 css::uno::Reference
< css::frame::XDispatch
> xDisp
= xProv
->queryDispatch( aURL
, OUString(), 0 );
266 // test the dispatch object if it is just a wrapper for a SfxDispatcher
267 if (auto pDisp
= dynamic_cast<SfxOfficeDispatch
*>(xDisp
.get()))
269 // The intercepting object is an SFX component
270 // If this dispatch object does not use the wanted dispatcher or the AppDispatcher, it's treated like any other UNO component
271 // (intercepting by internal dispatches)
272 SfxDispatcher
*pDispatcher
= pDisp
->GetDispatcher_Impl();
273 if ( pDispatcher
== &rDispat
|| pDispatcher
== SfxGetpApp()->GetAppDispatcher_Impl() )
275 // so we can use it directly
278 return aSlotServ
.GetSlot()? &aSlotServ
: nullptr;
282 // so the dispatch object isn't a SfxDispatcher wrapper or it is one, but it uses another dispatcher, but not rDispat
283 mxDispatch
= new BindDispatch_Impl( xDisp
, aURL
, this, pSlot
);
285 // flags must be set before adding StatusListener because the dispatch object will set the state
288 xDisp
->addStatusListener( mxDispatch
, aURL
);
290 else if ( rDispat
.GetFrame() )
292 css::uno::Reference
< css::frame::XDispatchProvider
> xFrameProv(
293 rDispat
.GetFrame()->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY
);
294 if ( xFrameProv
!= xProv
)
295 return GetSlotServer( rDispat
, xFrameProv
);
303 // we *always* return a SlotServer (if there is one); but in case of an external dispatch we might not use it
304 // for the "real" (non internal) controllers
305 return aSlotServ
.GetSlot()? &aSlotServ
: nullptr;
309 // Set Status in all Controllers
311 void SfxStateCache::SetState
313 SfxItemState eState
, // <SfxItemState> from 'pState'
314 const SfxPoolItem
* pState
, // Slot Status, 0 or -1
320 This method distributes the status of all of this SID bound
321 <SfxControllerItem>s. If the value is the same as before, and if neither
322 controller was registered nor invalidated inbetween, then no value is
323 passed. This way the flickering is for example avoided in ListBoxes.
326 SetState_Impl( eState
, pState
, bMaybeDirty
);
329 void SfxStateCache::GetState
331 boost::property_tree::ptree
& rState
334 if ( !mxDispatch
.is() && pController
)
336 for ( SfxControllerItem
*pCtrl
= pController
;
338 pCtrl
= pCtrl
->GetItemLink() )
339 pCtrl
->GetControlState( nId
, rState
);
343 void SfxStateCache::SetVisibleState( bool bShow
)
345 if ( bShow
== bItemVisible
)
348 SfxItemState
eState( SfxItemState::DEFAULT
);
349 const SfxPoolItem
* pState( nullptr );
350 bool bDeleteItem( false );
352 bItemVisible
= bShow
;
355 if ( IsInvalidItem(pLastItem
) || ( pLastItem
== nullptr ))
357 pState
= new SfxVoidItem( nId
);
367 pState
= new SfxVisibilityItem( nId
, false );
372 if ( !mxDispatch
.is() && pController
)
374 for ( SfxControllerItem
*pCtrl
= pController
;
376 pCtrl
= pCtrl
->GetItemLink() )
377 pCtrl
->StateChangedAtToolBoxControl( nId
, eState
, pState
);
380 if ( pInternalController
)
381 pInternalController
->StateChangedAtToolBoxControl( nId
, eState
, pState
);
388 void SfxStateCache::SetState_Impl
390 SfxItemState eState
, // <SfxItemState> from 'pState'
391 const SfxPoolItem
* pState
, // Slot Status, 0 or -1
395 // If a hard update occurs between enter- and leave-registrations is a
396 // can also intermediate Cached exist without controller.
397 if ( !pController
&& !pInternalController
)
400 DBG_ASSERT( bMaybeDirty
|| !bSlotDirty
, "setting state of dirty message" );
401 DBG_ASSERT( SfxControllerItem::GetItemState(pState
) == eState
, "invalid SfxItemState" );
403 // does the controller have to be notified at all?
404 bool bNotify
= bItemDirty
;
407 bNotify
= !SfxPoolItem::areSame(pLastItem
, pState
) || (eState
!= eLastState
);
413 if ( !mxDispatch
.is() && pController
)
415 for ( SfxControllerItem
*pCtrl
= pController
;
417 pCtrl
= pCtrl
->GetItemLink() )
418 pCtrl
->StateChangedAtToolBoxControl( nId
, eState
, pState
);
421 if ( pInternalController
)
422 static_cast<SfxDispatchController_Impl
*>(pInternalController
)->StateChanged( nId
, eState
, pState
, &aSlotServ
);
424 // Remember new value
425 if ( !IsInvalidItem(pLastItem
) )
430 if ( pState
&& !IsInvalidItem(pState
) )
431 pLastItem
= pState
->Clone();
442 // Set old status again in all the controllers
444 void SfxStateCache::SetCachedState( bool bAlways
)
446 DBG_ASSERT(pController
==nullptr||pController
->GetId()==nId
, "Cache with wrong ControllerItem" );
448 // Only update if cached item exists and also able to process.
449 // (If the State is sent, it must be ensured that a SlotServer is present,
450 // see SfxControllerItem:: GetCoreMetric())
451 if ( !(bAlways
|| ( !bItemDirty
&& !bSlotDirty
)) )
455 if ( !mxDispatch
.is() && pController
)
457 for ( SfxControllerItem
*pCtrl
= pController
;
459 pCtrl
= pCtrl
->GetItemLink() )
460 pCtrl
->StateChangedAtToolBoxControl( nId
, eLastState
, pLastItem
);
463 if ( pInternalController
)
464 static_cast<SfxDispatchController_Impl
*>(pInternalController
)->StateChanged( nId
, eLastState
, pLastItem
, &aSlotServ
);
466 // Controller is now ok
471 css::uno::Reference
< css::frame::XDispatch
> SfxStateCache::GetDispatch() const
473 if ( mxDispatch
.is() )
474 return mxDispatch
->xDisp
;
475 return css::uno::Reference
< css::frame::XDispatch
> ();
478 sal_Int16
SfxStateCache::Dispatch( const SfxItemSet
* pSet
, bool bForceSynchron
)
480 // protect pDispatch against destruction in the call
481 rtl::Reference
<BindDispatch_Impl
> xKeepAlive( mxDispatch
);
482 sal_Int16 eRet
= css::frame::DispatchResultState::DONTKNOW
;
484 if ( mxDispatch
.is() )
486 uno::Sequence
< beans::PropertyValue
> aArgs
;
488 TransformItems( nId
, *pSet
, aArgs
);
490 eRet
= mxDispatch
->Dispatch( aArgs
, bForceSynchron
);
497 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */