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
25 #include <string>
26 #include <com/sun/star/util/XURLTransformer.hpp>
27 #include <framework/dispatchhelper.hxx>
28 #include <com/sun/star/frame/DispatchResultState.hpp>
29 #include <com/sun/star/beans/PropertyValue.hpp>
30 #include <cppuhelper/weak.hxx>
31 #include <svl/eitem.hxx>
32 #include <svl/intitem.hxx>
33 #include <svl/stritem.hxx>
34 #include <svl/visitem.hxx>
35
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 <sfxtypes.hxx>
42 #include <sfx2/sfxuno.hxx>
43 #include <sfx2/unoctitm.hxx>
44 #include <sfx2/msgpool.hxx>
45 #include <sfx2/viewfrm.hxx>
46
47 using namespace ::com::sun::star;
48 using namespace ::com::sun::star::uno;
49 using namespace ::com::sun::star::util;
50
BindDispatch_Impl(const css::uno::Reference<css::frame::XDispatch> & rDisp,const css::util::URL & rURL,SfxStateCache * pStateCache,const SfxSlot * pS)51 BindDispatch_Impl::BindDispatch_Impl( const css::uno::Reference< css::frame::XDispatch > & rDisp, const css::util::URL& rURL, SfxStateCache *pStateCache, const SfxSlot* pS )
52 : xDisp( rDisp )
53 , aURL( rURL )
54 , pCache( pStateCache )
55 , pSlot( pS )
56 {
57 DBG_ASSERT( pCache && pSlot, "Invalid BindDispatch!");
58 aStatus.IsEnabled = true;
59 }
60
disposing(const css::lang::EventObject &)61 void 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
statusChanged(const css::frame::FeatureStateEvent & rEvent)70 void 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 > xRef( static_cast<cppu::OWeakObject*>(this), css::uno::UNO_QUERY );
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 }
100 else if ( aType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() )
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->StateChanged( nId, eState, pItem.get() );
142 }
143 }
144
Release()145 void BindDispatch_Impl::Release()
146 {
147 if ( xDisp.is() )
148 {
149 xDisp->removeStatusListener( static_cast<css::frame::XStatusListener*>(this), aURL );
150 xDisp.clear();
151 }
152 pCache = nullptr;
153 }
154
155
Dispatch(const css::uno::Sequence<css::beans::PropertyValue> & aProps,bool bForceSynchron)156 sal_Int16 BindDispatch_Impl::Dispatch( const css::uno::Sequence < css::beans::PropertyValue >& aProps, bool bForceSynchron )
157 {
158 sal_Int16 eRet = css::frame::DispatchResultState::DONTKNOW;
159
160 if ( xDisp.is() && aStatus.IsEnabled )
161 {
162 ::rtl::Reference< ::framework::DispatchHelper > xHelper( new ::framework::DispatchHelper(nullptr));
163 css::uno::Any aResult = xHelper->executeDispatch(xDisp, aURL, bForceSynchron, aProps);
164
165 css::frame::DispatchResultEvent aEvent;
166 aResult >>= aEvent;
167
168 eRet = aEvent.State;
169 }
170
171 return eRet;
172 }
173
174
175 // This constructor for an invalid cache that is updated in the first request.
176
SfxStateCache(sal_uInt16 nFuncId)177 SfxStateCache::SfxStateCache( sal_uInt16 nFuncId ):
178 nId(nFuncId),
179 pInternalController(nullptr),
180 pController(nullptr),
181 pLastItem( nullptr ),
182 eLastState( SfxItemState::UNKNOWN ),
183 bItemVisible( true )
184 {
185 bCtrlDirty = true;
186 bSlotDirty = true;
187 bItemDirty = true;
188 }
189
190
191 // The Destructor checks by assertion, even if controllers are registered.
192
~SfxStateCache()193 SfxStateCache::~SfxStateCache()
194 {
195 DBG_ASSERT( pController == nullptr && pInternalController == nullptr, "there are still Controllers registered" );
196 if ( !IsInvalidItem(pLastItem) )
197 delete pLastItem;
198 if ( mxDispatch.is() )
199 mxDispatch->Release();
200 }
201
202
203 // invalidates the cache (next request will force update)
Invalidate(bool bWithMsg)204 void SfxStateCache::Invalidate( bool bWithMsg )
205 {
206 bCtrlDirty = true;
207 if ( bWithMsg )
208 {
209 bSlotDirty = true;
210 aSlotServ.SetSlot( nullptr );
211 if ( mxDispatch.is() )
212 mxDispatch->Release();
213 mxDispatch.clear();
214 }
215 }
216
217
218 // gets the corresponding function from the dispatcher or the cache
219
GetSlotServer(SfxDispatcher & rDispat,const css::uno::Reference<css::frame::XDispatchProvider> & xProv)220 const SfxSlotServer* SfxStateCache::GetSlotServer( SfxDispatcher &rDispat , const css::uno::Reference< css::frame::XDispatchProvider > & xProv )
221 {
222
223 if ( bSlotDirty )
224 {
225 // get the SlotServer; we need it for internal controllers anyway, but also in most cases
226 rDispat.FindServer_( nId, aSlotServ );
227
228 DBG_ASSERT( !mxDispatch.is(), "Old Dispatch not removed!" );
229
230 // we don't need to check the dispatch provider if we only have an internal controller
231 if ( xProv.is() )
232 {
233 const SfxSlot* pSlot = aSlotServ.GetSlot();
234 if ( !pSlot )
235 // get the slot - even if it is disabled on the dispatcher
236 pSlot = SfxSlotPool::GetSlotPool( rDispat.GetFrame() ).GetSlot( nId );
237
238 if ( !pSlot || !pSlot->pUnoName )
239 {
240 bSlotDirty = false;
241 bCtrlDirty = true;
242 return aSlotServ.GetSlot()? &aSlotServ: nullptr;
243 }
244
245 // create the dispatch URL from the slot data
246 css::util::URL aURL;
247 OUString aCmd = ".uno:";
248 aURL.Protocol = aCmd;
249 aURL.Path = OUString::createFromAscii( pSlot->GetUnoName() );
250 aCmd += aURL.Path;
251 aURL.Complete = aCmd;
252 aURL.Main = aCmd;
253
254 // try to get a dispatch object for this command
255 css::uno::Reference< css::frame::XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
256 if ( xDisp.is() )
257 {
258 // test the dispatch object if it is just a wrapper for a SfxDispatcher
259 css::uno::Reference< css::lang::XUnoTunnel > xTunnel( xDisp, css::uno::UNO_QUERY );
260 SfxOfficeDispatch* pDisp = nullptr;
261 if ( xTunnel.is() )
262 {
263 sal_Int64 nImplementation = xTunnel->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier());
264 pDisp = reinterpret_cast< SfxOfficeDispatch* >(sal::static_int_cast< sal_IntPtr >( nImplementation ));
265 }
266
267 if ( pDisp )
268 {
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() )
274 {
275 // so we can use it directly
276 bSlotDirty = false;
277 bCtrlDirty = true;
278 return aSlotServ.GetSlot()? &aSlotServ: nullptr;
279 }
280 }
281
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 );
284
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.get(), aURL );
289 }
290 else if ( rDispat.GetFrame() )
291 {
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 );
296 }
297 }
298
299 bSlotDirty = false;
300 bCtrlDirty = true;
301 }
302
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;
306 }
307
308
309 // Set Status in all Controllers
310
SetState(SfxItemState eState,const SfxPoolItem * pState,bool bMaybeDirty)311 void SfxStateCache::SetState
312 (
313 SfxItemState eState, // <SfxItemState> from 'pState'
314 const SfxPoolItem* pState, // Slot Status, 0 or -1
315 bool bMaybeDirty
316 )
317
318 /* [Description]
319
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.
324 */
325 {
326 SetState_Impl( eState, pState, bMaybeDirty );
327 }
328
329
SetVisibleState(bool bShow)330 void SfxStateCache::SetVisibleState( bool bShow )
331 {
332 if ( bShow == bItemVisible )
333 return;
334
335 SfxItemState eState( SfxItemState::DEFAULT );
336 const SfxPoolItem* pState( nullptr );
337 bool bDeleteItem( false );
338
339 bItemVisible = bShow;
340 if ( bShow )
341 {
342 if ( IsInvalidItem(pLastItem) || ( pLastItem == nullptr ))
343 {
344 pState = new SfxVoidItem( nId );
345 bDeleteItem = true;
346 }
347 else
348 pState = pLastItem;
349
350 eState = eLastState;
351 }
352 else
353 {
354 pState = new SfxVisibilityItem( nId, false );
355 bDeleteItem = true;
356 }
357
358 // Update Controller
359 if ( !mxDispatch.is() && pController )
360 {
361 for ( SfxControllerItem *pCtrl = pController;
362 pCtrl;
363 pCtrl = pCtrl->GetItemLink() )
364 pCtrl->StateChanged( nId, eState, pState );
365 }
366
367 if ( pInternalController )
368 pInternalController->StateChanged( nId, eState, pState );
369
370 if ( bDeleteItem )
371 delete pState;
372 }
373
374
SetState_Impl(SfxItemState eState,const SfxPoolItem * pState,bool bMaybeDirty)375 void SfxStateCache::SetState_Impl
376 (
377 SfxItemState eState, // <SfxItemState> from 'pState'
378 const SfxPoolItem* pState, // Slot Status, 0 or -1
379 bool bMaybeDirty
380 )
381 {
382 // If a hard update occurs between enter- and leave-registrations is a
383 // can also intermediate Cached exist without controller.
384 if ( !pController && !pInternalController )
385 return;
386
387 DBG_ASSERT( bMaybeDirty || !bSlotDirty, "setting state of dirty message" );
388 DBG_ASSERT( SfxControllerItem::GetItemState(pState) == eState, "invalid SfxItemState" );
389
390 // does the controller have to be notified at all?
391 bool bNotify = bItemDirty;
392 if ( !bItemDirty )
393 {
394 bool bBothAvailable = pLastItem && pState &&
395 !IsInvalidItem(pState) && !IsInvalidItem(pLastItem);
396 DBG_ASSERT( !bBothAvailable || pState != pLastItem, "setting state with own item" );
397 if ( bBothAvailable )
398 bNotify = typeid(*pState) != typeid(*pLastItem) ||
399 *pState != *pLastItem;
400 else
401 bNotify = ( pState != pLastItem ) || ( eState != eLastState );
402 }
403
404 if ( bNotify )
405 {
406 // Update Controller
407 if ( !mxDispatch.is() && pController )
408 {
409 for ( SfxControllerItem *pCtrl = pController;
410 pCtrl;
411 pCtrl = pCtrl->GetItemLink() )
412 pCtrl->StateChanged( nId, eState, pState );
413 }
414
415 if ( pInternalController )
416 static_cast<SfxDispatchController_Impl *>(pInternalController)->StateChanged( nId, eState, pState, &aSlotServ );
417
418 // Remember new value
419 if ( !IsInvalidItem(pLastItem) )
420 DELETEZ(pLastItem);
421 if ( pState && !IsInvalidItem(pState) )
422 pLastItem = pState->Clone();
423 else
424 pLastItem = nullptr;
425 eLastState = eState;
426 bItemDirty = false;
427 }
428
429 bCtrlDirty = false;
430 }
431
432
433 // Set old status again in all the controllers
434
SetCachedState(bool bAlways)435 void SfxStateCache::SetCachedState( bool bAlways )
436 {
437 DBG_ASSERT(pController==nullptr||pController->GetId()==nId, "Cache with wrong ControllerItem" );
438
439 // Only update if cached item exists and also able to process.
440 // (If the State is sent, it must be ensured that a SlotServer is present,
441 // see SfxControllerItem:: GetCoreMetric())
442 if ( !(bAlways || ( !bItemDirty && !bSlotDirty )) )
443 return;
444
445 // Update Controller
446 if ( !mxDispatch.is() && pController )
447 {
448 for ( SfxControllerItem *pCtrl = pController;
449 pCtrl;
450 pCtrl = pCtrl->GetItemLink() )
451 pCtrl->StateChanged( nId, eLastState, pLastItem );
452 }
453
454 if ( pInternalController )
455 static_cast<SfxDispatchController_Impl *>(pInternalController)->StateChanged( nId, eLastState, pLastItem, &aSlotServ );
456
457 // Controller is now ok
458 bCtrlDirty = true;
459 }
460
461
GetDispatch() const462 css::uno::Reference< css::frame::XDispatch > SfxStateCache::GetDispatch() const
463 {
464 if ( mxDispatch.is() )
465 return mxDispatch->xDisp;
466 return css::uno::Reference< css::frame::XDispatch > ();
467 }
468
Dispatch(const SfxItemSet * pSet,bool bForceSynchron)469 sal_Int16 SfxStateCache::Dispatch( const SfxItemSet* pSet, bool bForceSynchron )
470 {
471 // protect pDispatch against destruction in the call
472 rtl::Reference<BindDispatch_Impl> xKeepAlive( mxDispatch );
473 sal_Int16 eRet = css::frame::DispatchResultState::DONTKNOW;
474
475 if ( mxDispatch.is() )
476 {
477 uno::Sequence < beans::PropertyValue > aArgs;
478 if (pSet)
479 TransformItems( nId, *pSet, aArgs );
480
481 eRet = mxDispatch->Dispatch( aArgs, bForceSynchron );
482 }
483
484 return eRet;
485 }
486
487
488 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
489