LibreOffice Module sfx2 (master) 1
statcach.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
21#ifdef __sun
22#include <ctime>
23#endif
24
26#include <com/sun/star/frame/DispatchResultState.hpp>
27#include <com/sun/star/frame/XFrame.hpp>
28#include <com/sun/star/beans/PropertyValue.hpp>
30#include <svl/eitem.hxx>
31#include <svl/intitem.hxx>
32#include <svl/stritem.hxx>
33#include <svl/visitem.hxx>
34
35#include <sfx2/app.hxx>
36#include <statcach.hxx>
37#include <sfx2/msg.hxx>
38#include <sfx2/ctrlitem.hxx>
39#include <sfx2/dispatch.hxx>
40#include <sfx2/sfxuno.hxx>
41#include <unoctitm.hxx>
42#include <sfx2/msgpool.hxx>
43#include <sfx2/viewfrm.hxx>
44#include <utility>
46
47using namespace ::com::sun::star;
48using namespace ::com::sun::star::uno;
49using namespace ::com::sun::star::util;
50
51BindDispatch_Impl::BindDispatch_Impl( css::uno::Reference< css::frame::XDispatch > _xDisp, css::util::URL _aURL, SfxStateCache *pStateCache, const SfxSlot* pS )
52 : xDisp(std::move( _xDisp ))
53 , aURL(std::move( _aURL ))
54 , pCache( pStateCache )
55 , pSlot( pS )
56{
57 DBG_ASSERT( pCache && pSlot, "Invalid BindDispatch!");
58 aStatus.IsEnabled = true;
59}
60
61void SAL_CALL BindDispatch_Impl::disposing( const css::lang::EventObject& )
62{
63 if ( xDisp.is() )
64 {
65 xDisp->removeStatusListener( static_cast<css::frame::XStatusListener*>(this), aURL );
66 xDisp.clear();
67 }
68}
69
70void SAL_CALL BindDispatch_Impl::statusChanged( const css::frame::FeatureStateEvent& rEvent )
71{
72 aStatus = rEvent;
73 if ( !pCache )
74 return;
75
76 css::uno::Reference< css::frame::XStatusListener > xKeepAlive(this);
77 if ( aStatus.Requery )
78 pCache->Invalidate( true );
79 else
80 {
81 std::unique_ptr<SfxPoolItem> pItem;
82 sal_uInt16 nId = pCache->GetId();
83 SfxItemState eState = SfxItemState::DISABLED;
84 if ( !aStatus.IsEnabled )
85 {
86 // default
87 }
88 else if (aStatus.State.hasValue())
89 {
90 eState = SfxItemState::DEFAULT;
91 css::uno::Any aAny = aStatus.State;
92
93 const css::uno::Type& aType = aAny.getValueType();
94 if ( aType == cppu::UnoType< bool >::get() )
95 {
96 bool bTemp = false;
97 aAny >>= bTemp ;
98 pItem.reset( new SfxBoolItem( nId, bTemp ) );
99 }
101 {
102 sal_uInt16 nTemp = 0;
103 aAny >>= nTemp ;
104 pItem.reset( new SfxUInt16Item( nId, nTemp ) );
105 }
106 else if ( aType == cppu::UnoType<sal_uInt32>::get() )
107 {
108 sal_uInt32 nTemp = 0;
109 aAny >>= nTemp ;
110 pItem.reset( new SfxUInt32Item( nId, nTemp ) );
111 }
112 else if ( aType == cppu::UnoType<OUString>::get() )
113 {
114 OUString sTemp ;
115 aAny >>= sTemp ;
116 pItem.reset( new SfxStringItem( nId, sTemp ) );
117 }
118 else
119 {
120 if ( pSlot )
121 pItem = pSlot->GetType()->CreateItem();
122 if ( pItem )
123 {
124 pItem->SetWhich( nId );
125 pItem->PutValue( aAny, 0 );
126 }
127 else
128 pItem.reset( new SfxVoidItem( nId ) );
129 }
130 }
131 else
132 {
133 // DONTCARE status
134 pItem.reset( new SfxVoidItem(0) );
135 eState = SfxItemState::UNKNOWN;
136 }
137
138 for ( SfxControllerItem *pCtrl = pCache->GetItemLink();
139 pCtrl;
140 pCtrl = pCtrl->GetItemLink() )
141 pCtrl->StateChangedAtToolBoxControl( nId, eState, pItem.get() );
142 }
143}
144
146{
147 if ( xDisp.is() )
148 {
149 try
150 {
151 xDisp->removeStatusListener(static_cast<css::frame::XStatusListener*>(this), aURL);
152 }
153 catch (const lang::DisposedException&)
154 {
155 TOOLS_WARN_EXCEPTION("sfx", "BindDispatch_Impl::Release: xDisp is disposed: ");
156 }
157 xDisp.clear();
158 }
159 pCache = nullptr;
160}
161
162
163sal_Int16 BindDispatch_Impl::Dispatch( const css::uno::Sequence < css::beans::PropertyValue >& aProps, bool bForceSynchron )
164{
165 sal_Int16 eRet = css::frame::DispatchResultState::DONTKNOW;
166
167 if ( xDisp.is() && aStatus.IsEnabled )
168 {
169 ::rtl::Reference< ::framework::DispatchHelper > xHelper( new ::framework::DispatchHelper(nullptr));
170 css::uno::Any aResult = xHelper->executeDispatch(xDisp, aURL, bForceSynchron, aProps);
171
172 css::frame::DispatchResultEvent aEvent;
173 aResult >>= aEvent;
174
175 eRet = aEvent.State;
176 }
177
178 return eRet;
179}
180
181
182// This constructor for an invalid cache that is updated in the first request.
183
184SfxStateCache::SfxStateCache( sal_uInt16 nFuncId ):
185 nId(nFuncId),
186 pInternalController(nullptr),
187 pController(nullptr),
188 pLastItem( nullptr ),
189 eLastState( SfxItemState::UNKNOWN ),
190 bItemVisible( true )
191{
192 bCtrlDirty = true;
193 bSlotDirty = true;
194 bItemDirty = true;
195}
196
197
198// The Destructor checks by assertion, even if controllers are registered.
199
201{
202 DBG_ASSERT( pController == nullptr && pInternalController == nullptr, "there are still Controllers registered" );
203 if ( !IsInvalidItem(pLastItem) )
204 delete pLastItem;
205 if ( mxDispatch.is() )
206 mxDispatch->Release();
207}
208
209
210// invalidates the cache (next request will force update)
211void SfxStateCache::Invalidate( bool bWithMsg )
212{
213 bCtrlDirty = true;
214 if ( bWithMsg )
215 {
216 bSlotDirty = true;
217 aSlotServ.SetSlot( nullptr );
218 if ( mxDispatch.is() )
219 mxDispatch->Release();
220 mxDispatch.clear();
221 }
222}
223
224
225// gets the corresponding function from the dispatcher or the cache
226
227const SfxSlotServer* SfxStateCache::GetSlotServer( SfxDispatcher &rDispat , const css::uno::Reference< css::frame::XDispatchProvider > & xProv )
228{
229
230 if ( bSlotDirty )
231 {
232 // get the SlotServer; we need it for internal controllers anyway, but also in most cases
233 rDispat.FindServer_( nId, aSlotServ );
234
235 DBG_ASSERT( !mxDispatch.is(), "Old Dispatch not removed!" );
236
237 // we don't need to check the dispatch provider if we only have an internal controller
238 if ( xProv.is() )
239 {
240 const SfxSlot* pSlot = aSlotServ.GetSlot();
241 if ( !pSlot )
242 // get the slot - even if it is disabled on the dispatcher
243 pSlot = SfxSlotPool::GetSlotPool( rDispat.GetFrame() ).GetSlot( nId );
244
245 if ( !pSlot || pSlot->pUnoName.isEmpty() )
246 {
247 bSlotDirty = false;
248 bCtrlDirty = true;
249 return aSlotServ.GetSlot()? &aSlotServ: nullptr;
250 }
251
252 // create the dispatch URL from the slot data
253 css::util::URL aURL;
254 OUString aCmd = ".uno:";
255 aURL.Protocol = aCmd;
256 aURL.Path = pSlot->GetUnoName();
257 aCmd += aURL.Path;
258 aURL.Complete = aCmd;
259 aURL.Main = aCmd;
260
261 // try to get a dispatch object for this command
262 css::uno::Reference< css::frame::XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
263 if ( xDisp.is() )
264 {
265 // test the dispatch object if it is just a wrapper for a SfxDispatcher
266 if (auto pDisp = dynamic_cast<SfxOfficeDispatch*>(xDisp.get()))
267 {
268 // The intercepting object is an SFX component
269 // If this dispatch object does not use the wanted dispatcher or the AppDispatcher, it's treated like any other UNO component
270 // (intercepting by internal dispatches)
271 SfxDispatcher *pDispatcher = pDisp->GetDispatcher_Impl();
272 if ( pDispatcher == &rDispat || pDispatcher == SfxGetpApp()->GetAppDispatcher_Impl() )
273 {
274 // so we can use it directly
275 bSlotDirty = false;
276 bCtrlDirty = true;
277 return aSlotServ.GetSlot()? &aSlotServ: nullptr;
278 }
279 }
280
281 // so the dispatch object isn't a SfxDispatcher wrapper or it is one, but it uses another dispatcher, but not rDispat
282 mxDispatch = new BindDispatch_Impl( xDisp, aURL, this, pSlot );
283
284 // flags must be set before adding StatusListener because the dispatch object will set the state
285 bSlotDirty = false;
286 bCtrlDirty = true;
287 xDisp->addStatusListener( mxDispatch, aURL );
288 }
289 else if ( rDispat.GetFrame() )
290 {
291 css::uno::Reference < css::frame::XDispatchProvider > xFrameProv(
292 rDispat.GetFrame()->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY );
293 if ( xFrameProv != xProv )
294 return GetSlotServer( rDispat, xFrameProv );
295 }
296 }
297
298 bSlotDirty = false;
299 bCtrlDirty = true;
300 }
301
302 // we *always* return a SlotServer (if there is one); but in case of an external dispatch we might not use it
303 // for the "real" (non internal) controllers
304 return aSlotServ.GetSlot()? &aSlotServ: nullptr;
305}
306
307
308// Set Status in all Controllers
309
311(
312 SfxItemState eState, // <SfxItemState> from 'pState'
313 const SfxPoolItem* pState, // Slot Status, 0 or -1
314 bool bMaybeDirty
315)
316
317/* [Description]
318
319 This method distributes the status of all of this SID bound
320 <SfxControllerItem>s. If the value is the same as before, and if neither
321 controller was registered nor invalidated inbetween, then no value is
322 passed. This way the flickering is for example avoided in ListBoxes.
323*/
324{
325 SetState_Impl( eState, pState, bMaybeDirty );
326}
327
329(
330 boost::property_tree::ptree& rState
331)
332{
333 if ( !mxDispatch.is() && pController )
334 {
335 for ( SfxControllerItem *pCtrl = pController;
336 pCtrl;
337 pCtrl = pCtrl->GetItemLink() )
338 pCtrl->GetControlState( nId, rState );
339 }
340}
341
343{
344 if ( bShow == bItemVisible )
345 return;
346
347 SfxItemState eState( SfxItemState::DEFAULT );
348 const SfxPoolItem* pState( nullptr );
349 bool bDeleteItem( false );
350
351 bItemVisible = bShow;
352 if ( bShow )
353 {
354 if ( IsInvalidItem(pLastItem) || ( pLastItem == nullptr ))
355 {
356 pState = new SfxVoidItem( nId );
357 bDeleteItem = true;
358 }
359 else
360 pState = pLastItem;
361
362 eState = eLastState;
363 }
364 else
365 {
366 pState = new SfxVisibilityItem( nId, false );
367 bDeleteItem = true;
368 }
369
370 // Update Controller
371 if ( !mxDispatch.is() && pController )
372 {
373 for ( SfxControllerItem *pCtrl = pController;
374 pCtrl;
375 pCtrl = pCtrl->GetItemLink() )
376 pCtrl->StateChangedAtToolBoxControl( nId, eState, pState );
377 }
378
381
382 if ( bDeleteItem )
383 delete pState;
384}
385
386
388(
389 SfxItemState eState, // <SfxItemState> from 'pState'
390 const SfxPoolItem* pState, // Slot Status, 0 or -1
391 bool bMaybeDirty
392)
393{
394 // If a hard update occurs between enter- and leave-registrations is a
395 // can also intermediate Cached exist without controller.
397 return;
398
399 DBG_ASSERT( bMaybeDirty || !bSlotDirty, "setting state of dirty message" );
400 DBG_ASSERT( SfxControllerItem::GetItemState(pState) == eState, "invalid SfxItemState" );
401
402 // does the controller have to be notified at all?
403 bool bNotify = bItemDirty;
404 if ( !bItemDirty )
405 {
406 bool bBothAvailable = pLastItem && pState &&
408 DBG_ASSERT( !bBothAvailable || pState != pLastItem, "setting state with own item" );
409 if ( bBothAvailable )
410 bNotify = typeid(*pState) != typeid(*pLastItem) ||
411 *pState != *pLastItem;
412 else
413 bNotify = ( pState != pLastItem ) || ( eState != eLastState );
414 }
415
416 if ( bNotify )
417 {
418 // Update Controller
419 if ( !mxDispatch.is() && pController )
420 {
421 for ( SfxControllerItem *pCtrl = pController;
422 pCtrl;
423 pCtrl = pCtrl->GetItemLink() )
424 pCtrl->StateChangedAtToolBoxControl( nId, eState, pState );
425 }
426
428 static_cast<SfxDispatchController_Impl *>(pInternalController)->StateChanged( nId, eState, pState, &aSlotServ );
429
430 // Remember new value
431 if ( !IsInvalidItem(pLastItem) )
432 {
433 delete pLastItem;
434 pLastItem = nullptr;
435 }
436 if ( pState && !IsInvalidItem(pState) )
437 pLastItem = pState->Clone();
438 else
439 pLastItem = nullptr;
440 eLastState = eState;
441 bItemDirty = false;
442 }
443
444 bCtrlDirty = false;
445}
446
447
448// Set old status again in all the controllers
449
451{
452 DBG_ASSERT(pController==nullptr||pController->GetId()==nId, "Cache with wrong ControllerItem" );
453
454 // Only update if cached item exists and also able to process.
455 // (If the State is sent, it must be ensured that a SlotServer is present,
456 // see SfxControllerItem:: GetCoreMetric())
457 if ( !(bAlways || ( !bItemDirty && !bSlotDirty )) )
458 return;
459
460 // Update Controller
461 if ( !mxDispatch.is() && pController )
462 {
463 for ( SfxControllerItem *pCtrl = pController;
464 pCtrl;
465 pCtrl = pCtrl->GetItemLink() )
466 pCtrl->StateChangedAtToolBoxControl( nId, eLastState, pLastItem );
467 }
468
471
472 // Controller is now ok
473 bCtrlDirty = true;
474}
475
476
477css::uno::Reference< css::frame::XDispatch > SfxStateCache::GetDispatch() const
478{
479 if ( mxDispatch.is() )
480 return mxDispatch->xDisp;
481 return css::uno::Reference< css::frame::XDispatch > ();
482}
483
484sal_Int16 SfxStateCache::Dispatch( const SfxItemSet* pSet, bool bForceSynchron )
485{
486 // protect pDispatch against destruction in the call
488 sal_Int16 eRet = css::frame::DispatchResultState::DONTKNOW;
489
490 if ( mxDispatch.is() )
491 {
492 uno::Sequence < beans::PropertyValue > aArgs;
493 if (pSet)
494 TransformItems( nId, *pSet, aArgs );
495
496 eRet = mxDispatch->Dispatch( aArgs, bForceSynchron );
497 }
498
499 return eRet;
500}
501
502
503/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SfxApplication * SfxGetpApp()
Definition: app.hxx:231
void TransformItems(sal_uInt16 nSlotId, const SfxItemSet &rSet, uno::Sequence< beans::PropertyValue > &rArgs, const SfxSlot *pSlot)
Definition: appuno.cxx:908
AnyEventRef aEvent
css::uno::Reference< css::frame::XDispatch > xDisp
Definition: statcach.hxx:40
virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent &Event) override
Definition: statcach.cxx:70
BindDispatch_Impl(css::uno::Reference< css::frame::XDispatch > xDisp, css::util::URL aURL, SfxStateCache *pStateCache, const SfxSlot *pSlot)
Definition: statcach.cxx:51
css::frame::FeatureStateEvent aStatus
Definition: statcach.hxx:42
const SfxSlot * pSlot
Definition: statcach.hxx:44
SfxStateCache * pCache
Definition: statcach.hxx:43
sal_Int16 Dispatch(const css::uno::Sequence< css::beans::PropertyValue > &aProps, bool bForceSynchron)
Definition: statcach.cxx:163
virtual void SAL_CALL disposing(const css::lang::EventObject &Source) override
Definition: statcach.cxx:61
css::util::URL aURL
Definition: statcach.hxx:41
virtual void StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem *pState)
Definition: ctrlitem.cxx:203
static SfxItemState GetItemState(const SfxPoolItem *pState)
Definition: ctrlitem.cxx:259
sal_uInt16 GetId() const
Definition: ctrlitem.hxx:63
SfxControllerItem * GetItemLink()
Definition: ctrlitem.cxx:31
bool FindServer_(sal_uInt16 nId, SfxSlotServer &rServer)
This helper method searches for the <Slot-Server> which currently serves the nSlot.
Definition: dispatch.cxx:1535
SfxViewFrame * GetFrame() const
Returns a pointer to the <SfxViewFrame> instance, which belongs to this SfxDispatcher.
Definition: dispatch.cxx:557
const css::uno::Reference< css::frame::XFrame > & GetFrameInterface() const
Definition: frame.cxx:515
virtual SfxPoolItem * Clone(SfxItemPool *pPool=nullptr) const=0
static SfxSlotPool & GetSlotPool(SfxViewFrame *pFrame=nullptr)
Definition: msgpool.cxx:316
const SfxSlot * GetSlot(sal_uInt16 nId) const
Definition: msgpool.cxx:155
void SetSlot(const SfxSlot *pSlot)
Definition: slotserv.hxx:37
const SfxSlot * GetSlot() const
Definition: slotserv.hxx:52
Definition: msg.hxx:184
OUString pUnoName
Definition: msg.hxx:205
const SfxType * GetType() const
Definition: msg.hxx:236
const OUString & GetUnoName() const
Definition: msg.hxx:237
SfxStateCache(const SfxStateCache &rOrig)=delete
void Invalidate(bool bWithSlot)
Definition: statcach.cxx:211
sal_uInt16 nId
Definition: statcach.hxx:65
SfxControllerItem * GetItemLink() const
Definition: statcach.hxx:141
bool bItemDirty
Definition: statcach.hxx:75
sal_uInt16 GetId() const
Definition: statcach.hxx:147
css::uno::Reference< css::frame::XDispatch > GetDispatch() const
Definition: statcach.cxx:477
void SetState(SfxItemState, const SfxPoolItem *, bool bMaybeDirty=false)
Definition: statcach.cxx:311
void SetCachedState(bool bAlways)
Definition: statcach.cxx:450
const SfxSlotServer * GetSlotServer(SfxDispatcher &rDispat, const css::uno::Reference< css::frame::XDispatchProvider > &xProv)
Definition: statcach.cxx:227
SfxPoolItem * pLastItem
Definition: statcach.hxx:70
bool bSlotDirty
Definition: statcach.hxx:73
friend class BindDispatch_Impl
Definition: statcach.hxx:62
SfxControllerItem * pInternalController
Definition: statcach.hxx:66
SfxSlotServer aSlotServ
Definition: statcach.hxx:69
bool bItemVisible
Definition: statcach.hxx:74
void SetState_Impl(SfxItemState, const SfxPoolItem *, bool bMaybeDirty)
Definition: statcach.cxx:388
void SetVisibleState(bool bShow)
Definition: statcach.cxx:342
void GetState(boost::property_tree::ptree &)
Definition: statcach.cxx:329
sal_Int16 Dispatch(const SfxItemSet *pSet, bool bForceSynchron)
Definition: statcach.cxx:484
SfxItemState eLastState
Definition: statcach.hxx:71
bool bCtrlDirty
Definition: statcach.hxx:72
rtl::Reference< BindDispatch_Impl > mxDispatch
Definition: statcach.hxx:64
SfxControllerItem * pController
Definition: statcach.hxx:68
SfxFrame & GetFrame() const
Definition: viewfrm.cxx:2782
const char * pS
#define DBG_ASSERT(sCon, aError)
#define TOOLS_WARN_EXCEPTION(area, stream)
URL aURL
UNKNOWN
sal_Int16 nId
SfxItemState
bool IsInvalidItem(const SfxPoolItem *pItem)
std::unique_ptr< SfxPoolItem > CreateItem() const
Definition: msg.hxx:111