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