Line data Source code
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 : #include <tools/debug.hxx>
22 : #include <svl/eitem.hxx>
23 : #include <svl/stritem.hxx>
24 : #include <svl/intitem.hxx>
25 : #include <svl/itemset.hxx>
26 : #include <svl/visitem.hxx>
27 : #include <svtools/javacontext.hxx>
28 : #include <svl/itempool.hxx>
29 : #include <tools/urlobj.hxx>
30 : #include <com/sun/star/util/URLTransformer.hpp>
31 : #include <com/sun/star/util/XURLTransformer.hpp>
32 : #include <com/sun/star/frame/XController.hpp>
33 : #include <com/sun/star/frame/XFrameActionListener.hpp>
34 : #include <com/sun/star/frame/XComponentLoader.hpp>
35 : #include <com/sun/star/frame/XFrame.hpp>
36 : #include <com/sun/star/frame/FrameActionEvent.hpp>
37 : #include <com/sun/star/frame/FrameAction.hpp>
38 : #include <com/sun/star/frame/status/ItemStatus.hpp>
39 : #include <com/sun/star/frame/status/ItemState.hpp>
40 : #include <com/sun/star/frame/DispatchResultState.hpp>
41 : #include <com/sun/star/frame/status/Visibility.hpp>
42 : #include <comphelper/processfactory.hxx>
43 : #include <comphelper/sequence.hxx>
44 : #include <osl/mutex.hxx>
45 : #include <uno/current_context.hxx>
46 : #include <vcl/svapp.hxx>
47 :
48 : #include <sfx2/app.hxx>
49 : #include <sfx2/unoctitm.hxx>
50 : #include <sfx2/viewfrm.hxx>
51 : #include <sfx2/frame.hxx>
52 : #include <sfx2/ctrlitem.hxx>
53 : #include <sfx2/sfxuno.hxx>
54 : #include <sfx2/bindings.hxx>
55 : #include <sfx2/dispatch.hxx>
56 : #include <sfx2/sfxsids.hrc>
57 : #include <sfx2/request.hxx>
58 : #include "statcach.hxx"
59 : #include <sfx2/msgpool.hxx>
60 : #include <sfx2/objsh.hxx>
61 :
62 : #include <boost/scoped_ptr.hpp>
63 :
64 : using namespace ::com::sun::star::uno;
65 : using namespace ::com::sun::star::util;
66 :
67 : enum URLTypeId
68 : {
69 : URLType_BOOL,
70 : URLType_BYTE,
71 : URLType_SHORT,
72 : URLType_LONG,
73 : URLType_HYPER,
74 : URLType_STRING,
75 : URLType_FLOAT,
76 : URLType_DOUBLE,
77 : URLType_COUNT
78 : };
79 :
80 : const char* URLTypeNames[URLType_COUNT] =
81 : {
82 : "bool",
83 : "byte",
84 : "short",
85 : "long",
86 : "hyper",
87 : "string",
88 : "float",
89 : "double"
90 : };
91 :
92 0 : SFX_IMPL_XINTERFACE_2( SfxUnoControllerItem, OWeakObject, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener )
93 0 : SFX_IMPL_XTYPEPROVIDER_2( SfxUnoControllerItem, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener )
94 :
95 0 : SfxUnoControllerItem::SfxUnoControllerItem( SfxControllerItem *pItem, SfxBindings& rBind, const String& rCmd )
96 : : pCtrlItem( pItem )
97 0 : , pBindings( &rBind )
98 : {
99 : DBG_ASSERT( !pCtrlItem || !pCtrlItem->IsBound(), "ControllerItem is incorrect!" );
100 :
101 0 : aCommand.Complete = rCmd;
102 0 : Reference< XURLTransformer > xTrans( URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
103 0 : xTrans->parseStrict( aCommand );
104 0 : pBindings->RegisterUnoController_Impl( this );
105 0 : }
106 :
107 0 : SfxUnoControllerItem::~SfxUnoControllerItem()
108 : {
109 : // tell bindings to forget this controller ( if still connected )
110 0 : if ( pBindings )
111 0 : pBindings->ReleaseUnoController_Impl( this );
112 0 : }
113 :
114 0 : void SfxUnoControllerItem::UnBind()
115 : {
116 : // connection to SfxControllerItem is lost
117 0 : pCtrlItem = NULL;
118 0 : ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > aRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY );
119 0 : ReleaseDispatch();
120 0 : }
121 :
122 0 : void SAL_CALL SfxUnoControllerItem::statusChanged(const ::com::sun::star::frame::FeatureStateEvent& rEvent) throw ( ::com::sun::star::uno::RuntimeException )
123 : {
124 0 : SolarMutexGuard aGuard;
125 : DBG_ASSERT( pCtrlItem, "dispatch implementation didn't respect our previous removeStatusListener call!" );
126 :
127 0 : if ( rEvent.Requery )
128 : {
129 : // Error can only happen if the old Dispatch is implemented incorrectly
130 : // i.e. removeStatusListener did not work. But such things can happen...
131 : // So protect before ReleaseDispatch from release!
132 0 : ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > aRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY );
133 0 : ReleaseDispatch();
134 0 : if ( pCtrlItem )
135 0 : GetNewDispatch(); // asynchronous ??
136 : }
137 0 : else if ( pCtrlItem )
138 : {
139 0 : SfxItemState eState = SFX_ITEM_DISABLED;
140 0 : SfxPoolItem* pItem = NULL;
141 0 : if ( rEvent.IsEnabled )
142 : {
143 0 : eState = SFX_ITEM_AVAILABLE;
144 0 : ::com::sun::star::uno::Type pType = rEvent.State.getValueType();
145 :
146 0 : if ( pType == ::getBooleanCppuType() )
147 : {
148 0 : sal_Bool bTemp = false;
149 0 : rEvent.State >>= bTemp ;
150 0 : pItem = new SfxBoolItem( pCtrlItem->GetId(), bTemp );
151 : }
152 0 : else if ( pType == ::getCppuType((const sal_uInt16*)0) )
153 : {
154 0 : sal_uInt16 nTemp = 0;
155 0 : rEvent.State >>= nTemp ;
156 0 : pItem = new SfxUInt16Item( pCtrlItem->GetId(), nTemp );
157 : }
158 0 : else if ( pType == ::getCppuType((const sal_uInt32*)0) )
159 : {
160 0 : sal_uInt32 nTemp = 0;
161 0 : rEvent.State >>= nTemp ;
162 0 : pItem = new SfxUInt32Item( pCtrlItem->GetId(), nTemp );
163 : }
164 0 : else if ( pType == ::getCppuType((const ::rtl::OUString*)0) )
165 : {
166 0 : ::rtl::OUString sTemp ;
167 0 : rEvent.State >>= sTemp ;
168 0 : pItem = new SfxStringItem( pCtrlItem->GetId(), sTemp );
169 : }
170 : else
171 0 : pItem = new SfxVoidItem( pCtrlItem->GetId() );
172 : }
173 :
174 0 : pCtrlItem->StateChanged( pCtrlItem->GetId(), eState, pItem );
175 0 : delete pItem;
176 0 : }
177 0 : }
178 :
179 0 : void SAL_CALL SfxUnoControllerItem::disposing( const ::com::sun::star::lang::EventObject& ) throw ( ::com::sun::star::uno::RuntimeException )
180 : {
181 0 : ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > aRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY );
182 0 : ReleaseDispatch();
183 0 : }
184 :
185 0 : void SfxUnoControllerItem::ReleaseDispatch()
186 : {
187 0 : if ( xDispatch.is() )
188 : {
189 0 : xDispatch->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aCommand );
190 0 : xDispatch = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
191 : }
192 0 : }
193 :
194 0 : void SfxUnoControllerItem::GetNewDispatch()
195 : {
196 0 : if ( !pBindings )
197 : {
198 : // Bindings released
199 : OSL_FAIL( "Tried to get dispatch, but no Bindings!" );
200 0 : return;
201 : }
202 :
203 : // forget old dispatch
204 0 : xDispatch = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
205 :
206 : // no arms, no cookies !
207 0 : if ( !pBindings->GetDispatcher_Impl() || !pBindings->GetDispatcher_Impl()->GetFrame() )
208 0 : return;
209 :
210 0 : SfxFrame& rFrame = pBindings->GetDispatcher_Impl()->GetFrame()->GetFrame();
211 0 : SfxFrame *pParent = rFrame.GetParentFrame();
212 0 : if ( pParent )
213 : // parent may intercept
214 0 : xDispatch = TryGetDispatch( pParent );
215 :
216 0 : if ( !xDispatch.is() )
217 : {
218 : // no interception
219 0 : ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame > xFrame = rFrame.GetFrameInterface();
220 0 : ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider > xProv( xFrame, ::com::sun::star::uno::UNO_QUERY );
221 0 : if ( xProv.is() )
222 0 : xDispatch = xProv->queryDispatch( aCommand, ::rtl::OUString(), 0 );
223 : }
224 :
225 0 : if ( xDispatch.is() )
226 0 : xDispatch->addStatusListener( (::com::sun::star::frame::XStatusListener*) this, aCommand );
227 0 : else if ( pCtrlItem )
228 0 : pCtrlItem->StateChanged( pCtrlItem->GetId(), SFX_ITEM_DISABLED, NULL );
229 : }
230 :
231 0 : ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > SfxUnoControllerItem::TryGetDispatch( SfxFrame *pFrame )
232 : {
233 0 : ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > xDisp;
234 0 : SfxFrame *pParent = pFrame->GetParentFrame();
235 0 : if ( pParent )
236 : // parent may intercept
237 0 : xDisp = TryGetDispatch( pParent );
238 :
239 : // only components may intercept
240 0 : if ( !xDisp.is() && pFrame->HasComponent() )
241 : {
242 : // no interception
243 0 : ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame > xFrame = pFrame->GetFrameInterface();
244 0 : ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider > xProv( xFrame, ::com::sun::star::uno::UNO_QUERY );
245 0 : if ( xProv.is() )
246 0 : xDisp = xProv->queryDispatch( aCommand, ::rtl::OUString(), 0 );
247 : }
248 :
249 0 : return xDisp;
250 : }
251 :
252 0 : void SfxUnoControllerItem::ReleaseBindings()
253 : {
254 : // connection to binding is lost; so forget the binding and the dispatch
255 0 : ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > aRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY );
256 0 : ReleaseDispatch();
257 0 : if ( pBindings )
258 0 : pBindings->ReleaseUnoController_Impl( this );
259 0 : pBindings = NULL;
260 0 : }
261 :
262 0 : void SfxStatusDispatcher::ReleaseAll()
263 : {
264 0 : ::com::sun::star::lang::EventObject aObject;
265 0 : aObject.Source = (::cppu::OWeakObject*) this;
266 0 : aListeners.disposeAndClear( aObject );
267 0 : }
268 :
269 0 : void SAL_CALL SfxStatusDispatcher::dispatch( const ::com::sun::star::util::URL&, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& ) throw ( ::com::sun::star::uno::RuntimeException )
270 : {
271 0 : }
272 :
273 0 : void SAL_CALL SfxStatusDispatcher::dispatchWithNotification(
274 : const ::com::sun::star::util::URL&,
275 : const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >&,
276 : const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchResultListener >& ) throw( ::com::sun::star::uno::RuntimeException )
277 : {
278 0 : }
279 :
280 27889 : SFX_IMPL_XINTERFACE_2( SfxStatusDispatcher, OWeakObject, ::com::sun::star::frame::XNotifyingDispatch, ::com::sun::star::frame::XDispatch )
281 0 : SFX_IMPL_XTYPEPROVIDER_2( SfxStatusDispatcher, ::com::sun::star::frame::XNotifyingDispatch, ::com::sun::star::frame::XDispatch )
282 : //IMPLNAME "com.sun.star.comp.sfx2.StatusDispatcher",
283 :
284 1654 : SfxStatusDispatcher::SfxStatusDispatcher()
285 1654 : : aListeners( aMutex )
286 : {
287 1654 : }
288 :
289 0 : void SAL_CALL SfxStatusDispatcher::addStatusListener(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > & aListener, const ::com::sun::star::util::URL& aURL) throw ( ::com::sun::star::uno::RuntimeException )
290 : {
291 0 : aListeners.addInterface( aURL.Complete, aListener );
292 0 : if ( aURL.Complete.compareToAscii(".uno:LifeTime")==0 )
293 : {
294 0 : ::com::sun::star::frame::FeatureStateEvent aEvent;
295 0 : aEvent.FeatureURL = aURL;
296 0 : aEvent.Source = (::com::sun::star::frame::XDispatch*) this;
297 0 : aEvent.IsEnabled = sal_True;
298 0 : aEvent.Requery = sal_False;
299 0 : aListener->statusChanged( aEvent );
300 : }
301 0 : }
302 :
303 0 : void SAL_CALL SfxStatusDispatcher::removeStatusListener( const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > & aListener, const ::com::sun::star::util::URL& aURL ) throw ( ::com::sun::star::uno::RuntimeException )
304 : {
305 0 : aListeners.removeInterface( aURL.Complete, aListener );
306 0 : }
307 :
308 29505 : SFX_IMPL_XINTERFACE_1( SfxOfficeDispatch, SfxStatusDispatcher, ::com::sun::star::lang::XUnoTunnel )
309 0 : SFX_IMPL_XTYPEPROVIDER_2( SfxOfficeDispatch, ::com::sun::star::frame::XNotifyingDispatch, ::com::sun::star::lang::XUnoTunnel )
310 :
311 :
312 : //-------------------------------------------------------------------------
313 : // XUnoTunnel
314 1635 : sal_Int64 SAL_CALL SfxOfficeDispatch::getSomething( const ::com::sun::star::uno::Sequence< sal_Int8 >& aIdentifier ) throw(::com::sun::star::uno::RuntimeException)
315 : {
316 1635 : if ( aIdentifier == impl_getStaticIdentifier() )
317 1635 : return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this ));
318 : else
319 0 : return 0;
320 : }
321 :
322 1635 : SfxOfficeDispatch::SfxOfficeDispatch( SfxBindings& rBindings, SfxDispatcher* pDispat, const SfxSlot* pSlot, const ::com::sun::star::util::URL& rURL )
323 : {
324 : // this object is an adapter that shows a ::com::sun::star::frame::XDispatch-Interface to the outside and uses a SfxControllerItem to monitor a state
325 1635 : pControllerItem = new SfxDispatchController_Impl( this, &rBindings, pDispat, pSlot, rURL );
326 1635 : }
327 :
328 0 : SfxOfficeDispatch::SfxOfficeDispatch( SfxDispatcher* pDispat, const SfxSlot* pSlot, const ::com::sun::star::util::URL& rURL )
329 : {
330 : // this object is an adapter that shows a ::com::sun::star::frame::XDispatch-Interface to the outside and uses a SfxControllerItem to monitor a state
331 0 : pControllerItem = new SfxDispatchController_Impl( this, NULL, pDispat, pSlot, rURL );
332 0 : }
333 :
334 225 : SfxOfficeDispatch::~SfxOfficeDispatch()
335 : {
336 75 : if ( pControllerItem )
337 : {
338 : // when dispatch object is released, destroy its connection to this object and destroy it
339 75 : pControllerItem->UnBindController();
340 75 : delete pControllerItem;
341 : }
342 150 : }
343 :
344 3270 : const ::com::sun::star::uno::Sequence< sal_Int8 >& SfxOfficeDispatch::impl_getStaticIdentifier()
345 : {
346 : // {38 57 CA 80 09 36 11 d4 83 FE 00 50 04 52 6B 21}
347 : static sal_uInt8 pGUID[16] = { 0x38, 0x57, 0xCA, 0x80, 0x09, 0x36, 0x11, 0xd4, 0x83, 0xFE, 0x00, 0x50, 0x04, 0x52, 0x6B, 0x21 };
348 3270 : static ::com::sun::star::uno::Sequence< sal_Int8 > seqID((sal_Int8*)pGUID,16) ;
349 3270 : return seqID ;
350 : }
351 :
352 :
353 0 : void SAL_CALL SfxOfficeDispatch::dispatch( const ::com::sun::star::util::URL& aURL, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& aArgs ) throw ( ::com::sun::star::uno::RuntimeException )
354 : {
355 : // ControllerItem is the Impl class
356 0 : if ( pControllerItem )
357 : {
358 : // The JavaContext contains an interaction handler which is used when
359 : // the creation of a Java Virtual Machine fails. The second parameter
360 : // indicates, that there shall only be one user notification (message box)
361 : // even if the same error (interaction) reoccurs. The effect is, that if a
362 : // user selects a menu entry than they may get only one notification that
363 : // a JRE is not selected.
364 : com::sun::star::uno::ContextLayer layer(
365 : new svt::JavaContext( com::sun::star::uno::getCurrentContext(),
366 0 : true) );
367 :
368 0 : pControllerItem->dispatch( aURL, aArgs, ::com::sun::star::uno::Reference < ::com::sun::star::frame::XDispatchResultListener >() );
369 : }
370 0 : }
371 :
372 0 : void SAL_CALL SfxOfficeDispatch::dispatchWithNotification( const ::com::sun::star::util::URL& aURL,
373 : const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& aArgs,
374 : const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchResultListener >& rListener ) throw( ::com::sun::star::uno::RuntimeException )
375 : {
376 : // ControllerItem is the Impl class
377 0 : if ( pControllerItem )
378 : {
379 : // see comment for SfxOfficeDispatch::dispatch
380 : com::sun::star::uno::ContextLayer layer(
381 : new svt::JavaContext( com::sun::star::uno::getCurrentContext(),
382 0 : true) );
383 :
384 0 : pControllerItem->dispatch( aURL, aArgs, rListener );
385 : }
386 0 : }
387 :
388 0 : void SAL_CALL SfxOfficeDispatch::addStatusListener(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > & aListener, const ::com::sun::star::util::URL& aURL) throw ( ::com::sun::star::uno::RuntimeException )
389 : {
390 0 : GetListeners().addInterface( aURL.Complete, aListener );
391 0 : if ( pControllerItem )
392 : {
393 : // ControllerItem is the Impl class
394 0 : pControllerItem->addStatusListener( aListener, aURL );
395 : }
396 0 : }
397 :
398 1635 : SfxDispatcher* SfxOfficeDispatch::GetDispatcher_Impl()
399 : {
400 1635 : return pControllerItem->GetDispatcher();
401 : }
402 :
403 0 : void SfxOfficeDispatch::SetFrame(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& xFrame)
404 : {
405 0 : if ( pControllerItem )
406 0 : pControllerItem->SetFrame( xFrame );
407 0 : }
408 :
409 1635 : void SfxOfficeDispatch::SetMasterUnoCommand( sal_Bool bSet )
410 : {
411 1635 : if ( pControllerItem )
412 1635 : pControllerItem->setMasterSlaveCommand( bSet );
413 1635 : }
414 :
415 : // Determine if URL contains a master/slave command which must be handled a little bit different
416 1635 : sal_Bool SfxOfficeDispatch::IsMasterUnoCommand( const ::com::sun::star::util::URL& aURL )
417 : {
418 1635 : if ( aURL.Protocol == ".uno:" && ( aURL.Path.indexOf( '.' ) > 0 ))
419 0 : return sal_True;
420 :
421 1635 : return sal_False;
422 : }
423 :
424 1635 : rtl::OUString SfxOfficeDispatch::GetMasterUnoCommand( const ::com::sun::star::util::URL& aURL )
425 : {
426 1635 : rtl::OUString aMasterCommand;
427 1635 : if ( IsMasterUnoCommand( aURL ))
428 : {
429 0 : sal_Int32 nIndex = aURL.Path.indexOf( '.' );
430 0 : if ( nIndex > 0 )
431 0 : aMasterCommand = aURL.Path.copy( 0, nIndex );
432 : }
433 :
434 1635 : return aMasterCommand;
435 : }
436 :
437 1635 : SfxDispatchController_Impl::SfxDispatchController_Impl(
438 : SfxOfficeDispatch* pDisp,
439 : SfxBindings* pBind,
440 : SfxDispatcher* pDispat,
441 : const SfxSlot* pSlot,
442 : const ::com::sun::star::util::URL& rURL )
443 : : aDispatchURL( rURL )
444 : , pDispatcher( pDispat )
445 : , pBindings( pBind )
446 : , pLastState( 0 )
447 1635 : , nSlot( pSlot->GetSlotId() )
448 : , pDispatch( pDisp )
449 : , bMasterSlave( sal_False )
450 : , bVisible( sal_True )
451 3270 : , pUnoName( pSlot->pUnoName )
452 : {
453 1635 : if ( aDispatchURL.Protocol == "slot:" && pUnoName )
454 : {
455 0 : rtl::OStringBuffer aTmp(RTL_CONSTASCII_STRINGPARAM(".uno:"));
456 0 : aTmp.append(pUnoName);
457 0 : aDispatchURL.Complete = ::rtl::OStringToOUString(aTmp.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US);
458 0 : Reference< XURLTransformer > xTrans( URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
459 0 : xTrans->parseStrict( aDispatchURL );
460 : }
461 :
462 1635 : SetId( nSlot );
463 1635 : if ( pBindings )
464 : {
465 : // Bind immediately to enable the cache to recycle dispatches when asked for the same command
466 : // a command in "slot" or in ".uno" notation must be treated as identical commands!
467 1635 : pBindings->ENTERREGISTRATIONS();
468 1635 : BindInternal_Impl( nSlot, pBindings );
469 1635 : pBindings->LEAVEREGISTRATIONS();
470 : }
471 1635 : }
472 :
473 225 : SfxDispatchController_Impl::~SfxDispatchController_Impl()
474 : {
475 75 : if ( pLastState && !IsInvalidItem( pLastState ) )
476 0 : delete pLastState;
477 :
478 75 : if ( pDispatch )
479 : {
480 : // disconnect
481 0 : pDispatch->pControllerItem = NULL;
482 :
483 : // force all listeners to release the dispatch object
484 0 : ::com::sun::star::lang::EventObject aObject;
485 0 : aObject.Source = (::cppu::OWeakObject*) pDispatch;
486 0 : pDispatch->GetListeners().disposeAndClear( aObject );
487 : }
488 150 : }
489 :
490 0 : void SfxDispatchController_Impl::SetFrame(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& _xFrame)
491 : {
492 0 : xFrame = _xFrame;
493 0 : }
494 :
495 1635 : void SfxDispatchController_Impl::setMasterSlaveCommand( sal_Bool bSet )
496 : {
497 1635 : bMasterSlave = bSet;
498 1635 : }
499 :
500 75 : void SfxDispatchController_Impl::UnBindController()
501 : {
502 75 : pDispatch = NULL;
503 75 : if ( IsBound() )
504 : {
505 0 : GetBindings().ENTERREGISTRATIONS();
506 0 : SfxControllerItem::UnBind();
507 0 : GetBindings().LEAVEREGISTRATIONS();
508 : }
509 75 : }
510 :
511 0 : void SfxDispatchController_Impl::addParametersToArgs( const com::sun::star::util::URL& aURL, ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& rArgs ) const
512 : {
513 : // Extract the parameter from the URL and put them into the property value sequence
514 0 : sal_Int32 nQueryIndex = aURL.Complete.indexOf( '?' );
515 0 : if ( nQueryIndex > 0 )
516 : {
517 0 : rtl::OUString aParamString( aURL.Complete.copy( nQueryIndex+1 ));
518 0 : sal_Int32 nIndex = 0;
519 0 : do
520 : {
521 0 : rtl::OUString aToken = aParamString.getToken( 0, '&', nIndex );
522 :
523 0 : sal_Int32 nParmIndex = 0;
524 0 : rtl::OUString aParamType;
525 0 : rtl::OUString aParamName = aToken.getToken( 0, '=', nParmIndex );
526 0 : rtl::OUString aValue = (nParmIndex!=-1) ? aToken.getToken( 0, '=', nParmIndex ) : ::rtl::OUString();
527 :
528 0 : if ( !aParamName.isEmpty() )
529 : {
530 0 : nParmIndex = 0;
531 0 : aToken = aParamName;
532 0 : aParamName = (nParmIndex!=-1) ? aToken.getToken( 0, ':', nParmIndex ) : ::rtl::OUString();
533 0 : aParamType = (nParmIndex!=-1) ? aToken.getToken( 0, ':', nParmIndex ) : ::rtl::OUString();
534 : }
535 :
536 0 : sal_Int32 nLen = rArgs.getLength();
537 0 : rArgs.realloc( nLen+1 );
538 0 : rArgs[nLen].Name = aParamName;
539 :
540 0 : if ( aParamType.isEmpty() )
541 : {
542 : // Default: LONG
543 0 : rArgs[nLen].Value <<= aValue.toInt32();
544 : }
545 0 : else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_BOOL], 4 ))
546 : {
547 : // sal_Bool support
548 0 : rArgs[nLen].Value <<= aValue.toBoolean();
549 : }
550 0 : else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_BYTE], 4 ))
551 : {
552 : // sal_uInt8 support
553 0 : rArgs[nLen].Value <<= sal_Int8( aValue.toInt32() );
554 : }
555 0 : else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_LONG], 4 ))
556 : {
557 : // LONG support
558 0 : rArgs[nLen].Value <<= aValue.toInt32();
559 : }
560 0 : else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_SHORT], 5 ))
561 : {
562 : // SHORT support
563 0 : rArgs[nLen].Value <<= sal_Int8( aValue.toInt32() );
564 : }
565 0 : else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_HYPER], 5 ))
566 : {
567 : // HYPER support
568 0 : rArgs[nLen].Value <<= aValue.toInt64();
569 : }
570 0 : else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_FLOAT], 5 ))
571 : {
572 : // FLOAT support
573 0 : rArgs[nLen].Value <<= aValue.toFloat();
574 : }
575 0 : else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_STRING], 6 ))
576 : {
577 : // STRING support
578 0 : rArgs[nLen].Value <<= rtl::OUString( INetURLObject::decode( aValue, '%', INetURLObject::DECODE_WITH_CHARSET ));
579 : }
580 0 : else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_DOUBLE], 6))
581 : {
582 : // DOUBLE support
583 0 : rArgs[nLen].Value <<= aValue.toDouble();
584 0 : }
585 : }
586 0 : while ( nIndex >= 0 );
587 : }
588 0 : }
589 :
590 0 : SfxMapUnit SfxDispatchController_Impl::GetCoreMetric( SfxItemPool& rPool, sal_uInt16 nSlotId )
591 : {
592 0 : sal_uInt16 nWhich = rPool.GetWhich( nSlotId );
593 0 : return rPool.GetMetric( nWhich );
594 : }
595 :
596 0 : rtl::OUString SfxDispatchController_Impl::getSlaveCommand( const ::com::sun::star::util::URL& rURL )
597 : {
598 0 : rtl::OUString aSlaveCommand;
599 0 : sal_Int32 nIndex = rURL.Path.indexOf( '.' );
600 0 : if (( nIndex > 0 ) && ( nIndex < rURL.Path.getLength() ))
601 0 : aSlaveCommand = rURL.Path.copy( nIndex+1 );
602 0 : return aSlaveCommand;
603 : }
604 :
605 0 : void SAL_CALL SfxDispatchController_Impl::dispatch( const ::com::sun::star::util::URL& aURL,
606 : const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& aArgs,
607 : const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchResultListener >& rListener ) throw( ::com::sun::star::uno::RuntimeException )
608 : {
609 0 : SolarMutexGuard aGuard;
610 0 : if (
611 : pDispatch &&
612 : (
613 0 : (aURL.Protocol.equalsAsciiL( ".uno:", 5 ) && aURL.Path == aDispatchURL.Path) ||
614 0 : (aURL.Protocol.equalsAsciiL( "slot:", 5 ) && aURL.Path.toInt32() == GetId())
615 : )
616 : )
617 : {
618 0 : if ( !pDispatcher && pBindings )
619 0 : pDispatcher = GetBindings().GetDispatcher_Impl();
620 :
621 0 : ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue > lNewArgs;
622 0 : sal_Int32 nCount = aArgs.getLength();
623 :
624 : // Support for URL based arguments
625 0 : INetURLObject aURLObj( aURL.Complete );
626 0 : if ( aURLObj.HasParam() )
627 0 : addParametersToArgs( aURL, lNewArgs );
628 :
629 : // Try to find call mode and frame name inside given arguments...
630 0 : SfxCallMode nCall = SFX_CALLMODE_STANDARD;
631 0 : sal_Int32 nMarkArg = -1;
632 :
633 : // Filter arguments which shouldn't be part of the sequence property value
634 0 : sal_Bool bTemp = sal_Bool();
635 0 : sal_uInt16 nModifier(0);
636 0 : std::vector< ::com::sun::star::beans::PropertyValue > aAddArgs;
637 0 : for( sal_Int32 n=0; n<nCount; n++ )
638 : {
639 0 : const ::com::sun::star::beans::PropertyValue& rProp = aArgs[n];
640 0 : if( rProp.Name.equalsAsciiL("SynchronMode",12))
641 : {
642 0 : if( rProp.Value >>=bTemp )
643 0 : nCall = bTemp ? SFX_CALLMODE_SYNCHRON : SFX_CALLMODE_ASYNCHRON;
644 : }
645 0 : else if( rProp.Name.equalsAsciiL("Bookmark",8))
646 : {
647 0 : nMarkArg = n;
648 0 : aAddArgs.push_back( aArgs[n] );
649 : }
650 0 : else if( rProp.Name.equalsAsciiL("KeyModifier",11))
651 0 : rProp.Value >>= nModifier;
652 : else
653 0 : aAddArgs.push_back( aArgs[n] );
654 : }
655 :
656 : // Add needed arguments to sequence property value
657 0 : sal_uInt32 nAddArgs = aAddArgs.size();
658 0 : if ( nAddArgs > 0 )
659 : {
660 0 : sal_uInt32 nIndex( lNewArgs.getLength() );
661 :
662 0 : lNewArgs.realloc( lNewArgs.getLength()+aAddArgs.size() );
663 0 : for ( sal_uInt32 i = 0; i < nAddArgs; i++ )
664 0 : lNewArgs[nIndex++] = aAddArgs[i];
665 : }
666 :
667 : // Overwrite possible detected sychron argument, if real listener exists (currently no other way)
668 0 : if ( rListener.is() )
669 0 : nCall = SFX_CALLMODE_SYNCHRON;
670 :
671 0 : if( GetId() == SID_JUMPTOMARK && nMarkArg == - 1 )
672 : {
673 : // we offer dispatches for SID_JUMPTOMARK if the URL points to a bookmark inside the document
674 : // so we must retrieve this as an argument from the parsed URL
675 0 : lNewArgs.realloc( lNewArgs.getLength()+1 );
676 0 : nMarkArg = lNewArgs.getLength()-1;
677 0 : lNewArgs[nMarkArg].Name = ::rtl::OUString("Bookmark");
678 0 : lNewArgs[nMarkArg].Value <<= aURL.Mark;
679 : }
680 :
681 0 : css::uno::Reference< css::frame::XFrame > xFrameRef(xFrame.get(), css::uno::UNO_QUERY);
682 0 : if (! xFrameRef.is() && pDispatcher)
683 : {
684 0 : SfxViewFrame* pViewFrame = pDispatcher->GetFrame();
685 0 : if (pViewFrame)
686 0 : xFrameRef = pViewFrame->GetFrame().GetFrameInterface();
687 : }
688 0 : SfxAllItemSet aInternalSet( SFX_APP()->GetPool() );
689 0 : if (xFrameRef.is()) // an empty set is no problem ... but an empty frame reference can be a problem !
690 0 : aInternalSet.Put( SfxUnoFrameItem( SID_FILLFRAME, xFrameRef ) );
691 :
692 0 : sal_Bool bSuccess = sal_False;
693 0 : const SfxPoolItem* pItem = NULL;
694 0 : SfxShell* pShell( 0 );
695 : // #i102619# Retrieve metric from shell before execution - the shell could be destroyed after execution
696 0 : SfxMapUnit eMapUnit( SFX_MAPUNIT_100TH_MM );
697 0 : if ( pDispatcher->GetBindings() )
698 : {
699 0 : if ( !pDispatcher->IsLocked( GetId() ) )
700 : {
701 0 : const SfxSlot *pSlot = 0;
702 0 : if ( pDispatcher->GetShellAndSlot_Impl( GetId(), &pShell, &pSlot, sal_False,
703 0 : SFX_CALLMODE_MODAL==(nCall&SFX_CALLMODE_MODAL), sal_False ) )
704 : {
705 0 : if ( bMasterSlave )
706 : {
707 : // Extract slave command and add argument to the args list. Master slot MUST
708 : // have a argument that has the same name as the master slot and type is SfxStringItem.
709 0 : sal_Int32 nIndex = lNewArgs.getLength();
710 0 : lNewArgs.realloc( nIndex+1 );
711 0 : lNewArgs[nIndex].Name = rtl::OUString::createFromAscii( pSlot->pUnoName );
712 0 : lNewArgs[nIndex].Value = makeAny( SfxDispatchController_Impl::getSlaveCommand( aDispatchURL ));
713 : }
714 :
715 0 : eMapUnit = GetCoreMetric( pShell->GetPool(), GetId() );
716 0 : boost::scoped_ptr<SfxAllItemSet> xSet(new SfxAllItemSet(pShell->GetPool()));
717 0 : TransformParameters(GetId(), lNewArgs, *xSet, pSlot);
718 0 : if (xSet->Count())
719 : {
720 : // execute with arguments - call directly
721 0 : pItem = pDispatcher->Execute(GetId(), nCall, xSet.get(), &aInternalSet, nModifier);
722 0 : bSuccess = (pItem != NULL);
723 : }
724 : else
725 : {
726 : // Be sure to delete this before we send a dispatch
727 : // request, which will destroy the current shell.
728 0 : xSet.reset();
729 :
730 : // execute using bindings, enables support for toggle/enum etc.
731 0 : SfxRequest aReq( GetId(), nCall, pShell->GetPool() );
732 0 : aReq.SetModifier( nModifier );
733 0 : aReq.SetInternalArgs_Impl(aInternalSet);
734 0 : pDispatcher->GetBindings()->Execute_Impl( aReq, pSlot, pShell );
735 0 : pItem = aReq.GetReturnValue();
736 0 : bSuccess = aReq.IsDone() || pItem != NULL;
737 0 : }
738 : }
739 : #ifdef DBG_UTIL
740 : else
741 : DBG_WARNING("MacroPlayer: Unknown slot dispatched!");
742 : #endif
743 : }
744 : }
745 : else
746 : {
747 0 : eMapUnit = GetCoreMetric( SFX_APP()->GetPool(), GetId() );
748 : // AppDispatcher
749 0 : SfxAllItemSet aSet( SFX_APP()->GetPool() );
750 0 : TransformParameters( GetId(), lNewArgs, aSet );
751 :
752 0 : if ( aSet.Count() )
753 0 : pItem = pDispatcher->Execute( GetId(), nCall, &aSet, &aInternalSet, nModifier );
754 : else
755 : // SfxRequests take empty sets as argument sets, GetArgs() returning non-zero!
756 0 : pItem = pDispatcher->Execute( GetId(), nCall, 0, &aInternalSet, nModifier );
757 :
758 : // no bindings, no invalidate ( usually done in SfxDispatcher::Call_Impl()! )
759 0 : if ( SfxApplication::Get() )
760 : {
761 0 : SfxDispatcher* pAppDispat = SFX_APP()->GetAppDispatcher_Impl();
762 0 : if ( pAppDispat )
763 : {
764 0 : const SfxPoolItem* pState=0;
765 0 : SfxItemState eState = pDispatcher->QueryState( GetId(), pState );
766 0 : StateChanged( GetId(), eState, pState );
767 : }
768 : }
769 :
770 0 : bSuccess = (pItem != NULL);
771 : }
772 :
773 0 : if ( rListener.is() )
774 : {
775 0 : ::com::sun::star::frame::DispatchResultEvent aEvent;
776 0 : if ( bSuccess )
777 0 : aEvent.State = com::sun::star::frame::DispatchResultState::SUCCESS;
778 : else
779 0 : aEvent.State = com::sun::star::frame::DispatchResultState::FAILURE;
780 :
781 0 : aEvent.Source = (::com::sun::star::frame::XDispatch*) pDispatch;
782 0 : if ( bSuccess && pItem && !pItem->ISA(SfxVoidItem) )
783 : {
784 0 : sal_uInt16 nSubId( 0 );
785 0 : if ( eMapUnit == SFX_MAPUNIT_TWIP )
786 0 : nSubId |= CONVERT_TWIPS;
787 0 : pItem->QueryValue( aEvent.Result, (sal_uInt8)nSubId );
788 : }
789 :
790 0 : rListener->dispatchFinished( aEvent );
791 0 : }
792 0 : }
793 0 : }
794 :
795 1635 : SfxDispatcher* SfxDispatchController_Impl::GetDispatcher()
796 : {
797 1635 : if ( !pDispatcher && pBindings )
798 0 : pDispatcher = GetBindings().GetDispatcher_Impl();
799 1635 : return pDispatcher;
800 : }
801 :
802 0 : void SAL_CALL SfxDispatchController_Impl::addStatusListener(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > & aListener, const ::com::sun::star::util::URL& aURL) throw ( ::com::sun::star::uno::RuntimeException )
803 : {
804 0 : SolarMutexGuard aGuard;
805 0 : if ( !pDispatch )
806 0 : return;
807 :
808 : // Use alternative QueryState call to have a valid UNO representation of the state.
809 0 : ::com::sun::star::uno::Any aState;
810 0 : if ( !pDispatcher && pBindings )
811 0 : pDispatcher = GetBindings().GetDispatcher_Impl();
812 0 : SfxItemState eState = pDispatcher->QueryState( GetId(), aState );
813 :
814 0 : if ( eState == SFX_ITEM_DONTCARE )
815 : {
816 : // Use special uno struct to transport don't care state
817 0 : ::com::sun::star::frame::status::ItemStatus aItemStatus;
818 0 : aItemStatus.State = ::com::sun::star::frame::status::ItemState::dont_care;
819 0 : aState = makeAny( aItemStatus );
820 : }
821 :
822 0 : ::com::sun::star::frame::FeatureStateEvent aEvent;
823 0 : aEvent.FeatureURL = aURL;
824 0 : aEvent.Source = (::com::sun::star::frame::XDispatch*) pDispatch;
825 0 : aEvent.Requery = sal_False;
826 0 : if ( bVisible )
827 : {
828 0 : aEvent.IsEnabled = eState != SFX_ITEM_DISABLED;
829 0 : aEvent.State = aState;
830 : }
831 : else
832 : {
833 0 : ::com::sun::star::frame::status::Visibility aVisibilityStatus;
834 0 : aVisibilityStatus.bVisible = sal_False;
835 :
836 : // MBA: we might decide to *not* disable "invisible" slots, but this would be
837 : // a change that needs to adjust at least the testtool
838 0 : aEvent.IsEnabled = sal_False;
839 0 : aEvent.State = makeAny( aVisibilityStatus );
840 : }
841 :
842 0 : aListener->statusChanged( aEvent );
843 : }
844 :
845 1515 : void SfxDispatchController_Impl::StateChanged( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState, SfxSlotServer* pSlotServ )
846 : {
847 1515 : if ( !pDispatch )
848 1515 : return;
849 :
850 : // Bindings instance notifies controller about a state change, listeners must be notified also
851 : // Don't cache visibility state changes as they are volatile. We need our real state to send it
852 : // to our controllers after visibility is set to true.
853 1515 : sal_Bool bNotify = sal_True;
854 1515 : if ( pState && !IsInvalidItem( pState ) )
855 : {
856 922 : if ( !pState->ISA( SfxVisibilityItem ) )
857 : {
858 922 : sal_Bool bBothAvailable = pLastState && !IsInvalidItem(pLastState);
859 922 : if ( bBothAvailable )
860 0 : bNotify = pState->Type() != pLastState->Type() || *pState != *pLastState;
861 922 : if ( pLastState && !IsInvalidItem( pLastState ) )
862 0 : delete pLastState;
863 922 : pLastState = !IsInvalidItem(pState) ? pState->Clone() : pState;
864 922 : bVisible = sal_True;
865 : }
866 : else
867 0 : bVisible = ((SfxVisibilityItem *)pState)->GetValue();
868 : }
869 : else
870 : {
871 593 : if ( pLastState && !IsInvalidItem( pLastState ) )
872 0 : delete pLastState;
873 593 : pLastState = pState;
874 : }
875 :
876 1515 : ::cppu::OInterfaceContainerHelper* pContnr = pDispatch->GetListeners().getContainer ( aDispatchURL.Complete );
877 1515 : if ( bNotify && pContnr )
878 : {
879 0 : ::com::sun::star::uno::Any aState;
880 0 : if ( ( eState >= SFX_ITEM_AVAILABLE ) && pState && !IsInvalidItem( pState ) && !pState->ISA(SfxVoidItem) )
881 : {
882 : // Retrieve metric from pool to have correct sub ID when calling QueryValue
883 0 : sal_uInt16 nSubId( 0 );
884 0 : SfxMapUnit eMapUnit( SFX_MAPUNIT_100TH_MM );
885 :
886 : // retrieve the core metric
887 : // it's enough to check the objectshell, the only shell that does not use the pool of the document
888 : // is SfxViewFrame, but it hasn't any metric parameters
889 : // TODO/LATER: what about the FormShell? Does it use any metric data?! Perhaps it should use the Pool of the document!
890 0 : if ( pSlotServ && pDispatcher )
891 : {
892 0 : SfxShell* pShell = pDispatcher->GetShell( pSlotServ->GetShellLevel() );
893 : DBG_ASSERT( pShell, "Can't get core metric without shell!" );
894 0 : if ( pShell )
895 0 : eMapUnit = GetCoreMetric( pShell->GetPool(), nSID );
896 : }
897 :
898 0 : if ( eMapUnit == SFX_MAPUNIT_TWIP )
899 0 : nSubId |= CONVERT_TWIPS;
900 :
901 0 : pState->QueryValue( aState, (sal_uInt8)nSubId );
902 : }
903 0 : else if ( eState == SFX_ITEM_DONTCARE )
904 : {
905 : // Use special uno struct to transport don't care state
906 0 : ::com::sun::star::frame::status::ItemStatus aItemStatus;
907 0 : aItemStatus.State = ::com::sun::star::frame::status::ItemState::dont_care;
908 0 : aState = makeAny( aItemStatus );
909 : }
910 :
911 0 : ::com::sun::star::frame::FeatureStateEvent aEvent;
912 0 : aEvent.FeatureURL = aDispatchURL;
913 0 : aEvent.Source = (::com::sun::star::frame::XDispatch*) pDispatch;
914 0 : aEvent.IsEnabled = eState != SFX_ITEM_DISABLED;
915 0 : aEvent.Requery = sal_False;
916 0 : aEvent.State = aState;
917 :
918 0 : ::cppu::OInterfaceIteratorHelper aIt( *pContnr );
919 0 : while( aIt.hasMoreElements() )
920 : {
921 : try
922 : {
923 0 : ((::com::sun::star::frame::XStatusListener *)aIt.next())->statusChanged( aEvent );
924 : }
925 0 : catch (const ::com::sun::star::uno::RuntimeException&)
926 : {
927 0 : aIt.remove();
928 : }
929 0 : }
930 : }
931 : }
932 :
933 0 : void SfxDispatchController_Impl::StateChanged( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
934 : {
935 0 : StateChanged( nSID, eState, pState, 0 );
936 0 : }
937 :
938 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|