Bump version to 24.04.3.4
[LibreOffice.git] / sfx2 / source / control / statcach.cxx
blob2346333fd06a859ee6d23b744a311e28c5a79ae3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
21 #ifdef __sun
22 #include <ctime>
23 #endif
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>
45 #include <utility>
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 )
56 , pSlot( pS )
58 DBG_ASSERT( pCache && pSlot, "Invalid BindDispatch!");
59 aStatus.IsEnabled = true;
62 void SAL_CALL BindDispatch_Impl::disposing( const css::lang::EventObject& )
64 if ( xDisp.is() )
66 xDisp->removeStatusListener( static_cast<css::frame::XStatusListener*>(this), aURL );
67 xDisp.clear();
71 void SAL_CALL BindDispatch_Impl::statusChanged( const css::frame::FeatureStateEvent& rEvent )
73 aStatus = rEvent;
74 if ( !pCache )
75 return;
77 css::uno::Reference< css::frame::XStatusListener > xKeepAlive(this);
78 if ( aStatus.Requery )
79 pCache->Invalidate( true );
80 else
82 std::unique_ptr<SfxPoolItem> pItem;
83 sal_uInt16 nId = pCache->GetId();
84 SfxItemState eState = SfxItemState::DISABLED;
85 if ( !aStatus.IsEnabled )
87 // default
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() )
97 bool bTemp = false;
98 aAny >>= bTemp ;
99 pItem.reset( new SfxBoolItem( nId, bTemp ) );
101 else if ( aType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() )
103 sal_uInt16 nTemp = 0;
104 aAny >>= nTemp ;
105 pItem.reset( new SfxUInt16Item( nId, nTemp ) );
107 else if ( aType == cppu::UnoType<sal_uInt32>::get() )
109 sal_uInt32 nTemp = 0;
110 aAny >>= nTemp ;
111 pItem.reset( new SfxUInt32Item( nId, nTemp ) );
113 else if ( aType == cppu::UnoType<OUString>::get() )
115 OUString sTemp ;
116 aAny >>= sTemp ;
117 pItem.reset( new SfxStringItem( nId, sTemp ) );
119 else
121 if ( pSlot )
122 pItem = pSlot->GetType()->CreateItem();
123 if ( pItem )
125 pItem->SetWhich( nId );
126 pItem->PutValue( aAny, 0 );
128 else
129 pItem.reset( new SfxVoidItem( nId ) );
132 else
134 // DONTCARE status
135 pItem.reset( new SfxVoidItem(0) );
136 eState = SfxItemState::UNKNOWN;
139 for ( SfxControllerItem *pCtrl = pCache->GetItemLink();
140 pCtrl;
141 pCtrl = pCtrl->GetItemLink() )
142 pCtrl->StateChangedAtToolBoxControl( nId, eState, pItem.get() );
146 void BindDispatch_Impl::Release()
148 if ( xDisp.is() )
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: ");
158 xDisp.clear();
160 pCache = nullptr;
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;
174 aResult >>= aEvent;
176 eRet = aEvent.State;
179 return eRet;
183 // This constructor for an invalid cache that is updated in the first request.
185 SfxStateCache::SfxStateCache( sal_uInt16 nFuncId ):
186 nId(nFuncId),
187 pInternalController(nullptr),
188 pController(nullptr),
189 pLastItem( nullptr ),
190 eLastState( SfxItemState::UNKNOWN ),
191 bItemVisible( true )
193 bCtrlDirty = true;
194 bSlotDirty = true;
195 bItemDirty = true;
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) )
205 delete pLastItem;
206 if ( mxDispatch.is() )
207 mxDispatch->Release();
211 // invalidates the cache (next request will force update)
212 void SfxStateCache::Invalidate( bool bWithMsg )
214 bCtrlDirty = true;
215 if ( bWithMsg )
217 bSlotDirty = true;
218 aSlotServ.SetSlot( nullptr );
219 if ( mxDispatch.is() )
220 mxDispatch->Release();
221 mxDispatch.clear();
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 )
231 if ( bSlotDirty )
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
239 if ( xProv.is() )
241 const SfxSlot* pSlot = aSlotServ.GetSlot();
242 if ( !pSlot )
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() )
248 bSlotDirty = false;
249 bCtrlDirty = true;
250 return aSlotServ.GetSlot()? &aSlotServ: nullptr;
253 // create the dispatch URL from the slot data
254 css::util::URL aURL;
255 OUString aCmd = ".uno:";
256 aURL.Protocol = aCmd;
257 aURL.Path = pSlot->GetUnoName();
258 aCmd += aURL.Path;
259 aURL.Complete = aCmd;
260 aURL.Main = aCmd;
262 // try to get a dispatch object for this command
263 css::uno::Reference< css::frame::XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
264 if ( xDisp.is() )
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
276 bSlotDirty = false;
277 bCtrlDirty = true;
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
286 bSlotDirty = false;
287 bCtrlDirty = true;
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 );
299 bSlotDirty = false;
300 bCtrlDirty = true;
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
315 bool bMaybeDirty
318 /* [Description]
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;
337 pCtrl;
338 pCtrl = pCtrl->GetItemLink() )
339 pCtrl->GetControlState( nId, rState );
343 void SfxStateCache::SetVisibleState( bool bShow )
345 if ( bShow == bItemVisible )
346 return;
348 SfxItemState eState( SfxItemState::DEFAULT );
349 const SfxPoolItem* pState( nullptr );
350 bool bDeleteItem( false );
352 bItemVisible = bShow;
353 if ( bShow )
355 if ( IsInvalidItem(pLastItem) || ( pLastItem == nullptr ))
357 pState = new SfxVoidItem( nId );
358 bDeleteItem = true;
360 else
361 pState = pLastItem;
363 eState = eLastState;
365 else
367 pState = new SfxVisibilityItem( nId, false );
368 bDeleteItem = true;
371 // Update Controller
372 if ( !mxDispatch.is() && pController )
374 for ( SfxControllerItem *pCtrl = pController;
375 pCtrl;
376 pCtrl = pCtrl->GetItemLink() )
377 pCtrl->StateChangedAtToolBoxControl( nId, eState, pState );
380 if ( pInternalController )
381 pInternalController->StateChangedAtToolBoxControl( nId, eState, pState );
383 if ( bDeleteItem )
384 delete pState;
388 void SfxStateCache::SetState_Impl
390 SfxItemState eState, // <SfxItemState> from 'pState'
391 const SfxPoolItem* pState, // Slot Status, 0 or -1
392 bool bMaybeDirty
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 )
398 return;
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;
405 if ( !bItemDirty )
407 bNotify = !SfxPoolItem::areSame(pLastItem, pState) || (eState != eLastState);
410 if ( bNotify )
412 // Update Controller
413 if ( !mxDispatch.is() && pController )
415 for ( SfxControllerItem *pCtrl = pController;
416 pCtrl;
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) )
427 delete pLastItem;
428 pLastItem = nullptr;
430 if ( pState && !IsInvalidItem(pState) )
431 pLastItem = pState->Clone();
432 else
433 pLastItem = nullptr;
434 eLastState = eState;
435 bItemDirty = false;
438 bCtrlDirty = false;
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 )) )
452 return;
454 // Update Controller
455 if ( !mxDispatch.is() && pController )
457 for ( SfxControllerItem *pCtrl = pController;
458 pCtrl;
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
467 bCtrlDirty = true;
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;
487 if (pSet)
488 TransformItems( nId, *pSet, aArgs );
490 eRet = mxDispatch->Dispatch( aArgs, bForceSynchron );
493 return eRet;
497 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */