bump product version to 5.0.4.1
[LibreOffice.git] / sfx2 / source / control / unoctitm.cxx
blobbba52c6f9722a98807cea77b45be93ed3e54dea8
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 .
20 #include <config_features.h>
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/Desktop.hpp>
34 #include <com/sun/star/frame/XController.hpp>
35 #include <com/sun/star/frame/XFrameActionListener.hpp>
36 #include <com/sun/star/frame/XComponentLoader.hpp>
37 #include <com/sun/star/frame/XFrame.hpp>
38 #include <com/sun/star/frame/FrameActionEvent.hpp>
39 #include <com/sun/star/frame/FrameAction.hpp>
40 #include <com/sun/star/frame/status/ItemStatus.hpp>
41 #include <com/sun/star/frame/status/ItemState.hpp>
42 #include <com/sun/star/frame/DispatchResultState.hpp>
43 #include <com/sun/star/frame/ModuleManager.hpp>
44 #include <com/sun/star/frame/status/Visibility.hpp>
45 #include <comphelper/processfactory.hxx>
46 #include <comphelper/sequence.hxx>
47 #include <officecfg/Office/Common.hxx>
48 #include <osl/mutex.hxx>
49 #include <uno/current_context.hxx>
50 #include <vcl/svapp.hxx>
52 #include <sfx2/app.hxx>
53 #include <sfx2/unoctitm.hxx>
54 #include <sfx2/viewfrm.hxx>
55 #include <sfx2/frame.hxx>
56 #include <sfx2/ctrlitem.hxx>
57 #include <sfx2/sfxuno.hxx>
58 #include <sfx2/bindings.hxx>
59 #include <sfx2/dispatch.hxx>
60 #include <sfx2/sfxsids.hrc>
61 #include <sfx2/request.hxx>
62 #include "statcach.hxx"
63 #include <sfx2/msgpool.hxx>
64 #include <sfx2/objsh.hxx>
66 #include <boost/scoped_ptr.hpp>
68 #include <iostream>
69 #include <map>
71 #include <sal/log.hxx>
72 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
74 using namespace ::com::sun::star;
75 using namespace ::com::sun::star::uno;
76 using namespace ::com::sun::star::util;
78 enum URLTypeId
80 URLType_BOOL,
81 URLType_BYTE,
82 URLType_SHORT,
83 URLType_LONG,
84 URLType_HYPER,
85 URLType_STRING,
86 URLType_FLOAT,
87 URLType_DOUBLE,
88 URLType_COUNT
91 const char* URLTypeNames[URLType_COUNT] =
93 "bool",
94 "byte",
95 "short",
96 "long",
97 "hyper",
98 "string",
99 "float",
100 "double"
103 SfxUnoControllerItem::SfxUnoControllerItem( SfxControllerItem *pItem, SfxBindings& rBind, const OUString& rCmd )
104 : pCtrlItem( pItem )
105 , pBindings( &rBind )
107 DBG_ASSERT( !pCtrlItem || !pCtrlItem->IsBound(), "ControllerItem is incorrect!" );
109 aCommand.Complete = rCmd;
110 Reference< XURLTransformer > xTrans( URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
111 xTrans->parseStrict( aCommand );
112 pBindings->RegisterUnoController_Impl( this );
115 SfxUnoControllerItem::~SfxUnoControllerItem()
117 // tell bindings to forget this controller ( if still connected )
118 if ( pBindings )
119 pBindings->ReleaseUnoController_Impl( this );
122 void SfxUnoControllerItem::UnBind()
124 // connection to SfxControllerItem is lost
125 pCtrlItem = NULL;
126 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > aRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY );
127 ReleaseDispatch();
130 void SAL_CALL SfxUnoControllerItem::statusChanged(const ::com::sun::star::frame::FeatureStateEvent& rEvent) throw ( ::com::sun::star::uno::RuntimeException, std::exception )
132 SolarMutexGuard aGuard;
133 DBG_ASSERT( pCtrlItem, "dispatch implementation didn't respect our previous removeStatusListener call!" );
135 if ( rEvent.Requery )
137 // Error can only happen if the old Dispatch is implemented incorrectly
138 // i.e. removeStatusListener did not work. But such things can happen...
139 // So protect before ReleaseDispatch from release!
140 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > aRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY );
141 ReleaseDispatch();
142 if ( pCtrlItem )
143 GetNewDispatch(); // asynchronous ??
145 else if ( pCtrlItem )
147 SfxItemState eState = SfxItemState::DISABLED;
148 SfxPoolItem* pItem = NULL;
149 if ( rEvent.IsEnabled )
151 eState = SfxItemState::DEFAULT;
152 ::com::sun::star::uno::Type pType = rEvent.State.getValueType();
154 if ( pType == cppu::UnoType< bool >::get() )
156 bool bTemp = false;
157 rEvent.State >>= bTemp ;
158 pItem = new SfxBoolItem( pCtrlItem->GetId(), bTemp );
160 else if ( pType == cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() )
162 sal_uInt16 nTemp = 0;
163 rEvent.State >>= nTemp ;
164 pItem = new SfxUInt16Item( pCtrlItem->GetId(), nTemp );
166 else if ( pType == cppu::UnoType<sal_uInt32>::get() )
168 sal_uInt32 nTemp = 0;
169 rEvent.State >>= nTemp ;
170 pItem = new SfxUInt32Item( pCtrlItem->GetId(), nTemp );
172 else if ( pType == cppu::UnoType<OUString>::get() )
174 OUString sTemp ;
175 rEvent.State >>= sTemp ;
176 pItem = new SfxStringItem( pCtrlItem->GetId(), sTemp );
178 else
179 pItem = new SfxVoidItem( pCtrlItem->GetId() );
182 pCtrlItem->StateChanged( pCtrlItem->GetId(), eState, pItem );
183 delete pItem;
187 void SAL_CALL SfxUnoControllerItem::disposing( const ::com::sun::star::lang::EventObject& ) throw ( ::com::sun::star::uno::RuntimeException, std::exception )
189 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > aRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY );
190 ReleaseDispatch();
193 void SfxUnoControllerItem::ReleaseDispatch()
195 if ( xDispatch.is() )
197 xDispatch->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aCommand );
198 xDispatch = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
202 void SfxUnoControllerItem::GetNewDispatch()
204 if ( !pBindings )
206 // Bindings released
207 OSL_FAIL( "Tried to get dispatch, but no Bindings!" );
208 return;
211 // forget old dispatch
212 xDispatch = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
214 // no arms, no cookies !
215 if ( !pBindings->GetDispatcher_Impl() || !pBindings->GetDispatcher_Impl()->GetFrame() )
216 return;
218 SfxFrame& rFrame = pBindings->GetDispatcher_Impl()->GetFrame()->GetFrame();
219 SfxFrame *pParent = rFrame.GetParentFrame();
220 if ( pParent )
221 // parent may intercept
222 xDispatch = TryGetDispatch( pParent );
224 if ( !xDispatch.is() )
226 // no interception
227 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame > xFrame = rFrame.GetFrameInterface();
228 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider > xProv( xFrame, ::com::sun::star::uno::UNO_QUERY );
229 if ( xProv.is() )
230 xDispatch = xProv->queryDispatch( aCommand, OUString(), 0 );
233 if ( xDispatch.is() )
234 xDispatch->addStatusListener( (::com::sun::star::frame::XStatusListener*) this, aCommand );
235 else if ( pCtrlItem )
236 pCtrlItem->StateChanged( pCtrlItem->GetId(), SfxItemState::DISABLED, NULL );
239 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > SfxUnoControllerItem::TryGetDispatch( SfxFrame *pFrame )
241 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > xDisp;
242 SfxFrame *pParent = pFrame->GetParentFrame();
243 if ( pParent )
244 // parent may intercept
245 xDisp = TryGetDispatch( pParent );
247 return xDisp;
250 void SfxUnoControllerItem::ReleaseBindings()
252 // connection to binding is lost; so forget the binding and the dispatch
253 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > aRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY );
254 ReleaseDispatch();
255 if ( pBindings )
256 pBindings->ReleaseUnoController_Impl( this );
257 pBindings = NULL;
260 void SfxStatusDispatcher::ReleaseAll()
262 ::com::sun::star::lang::EventObject aObject;
263 aObject.Source = (::cppu::OWeakObject*) this;
264 aListeners.disposeAndClear( aObject );
267 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 )
271 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 )
278 SfxStatusDispatcher::SfxStatusDispatcher()
279 : aListeners( aMutex )
283 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 )
285 aListeners.addInterface( aURL.Complete, aListener );
286 if ( aURL.Complete == ".uno:LifeTime" )
288 ::com::sun::star::frame::FeatureStateEvent aEvent;
289 aEvent.FeatureURL = aURL;
290 aEvent.Source = (::com::sun::star::frame::XDispatch*) this;
291 aEvent.IsEnabled = sal_True;
292 aEvent.Requery = sal_False;
293 aListener->statusChanged( aEvent );
297 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 )
299 aListeners.removeInterface( aURL.Complete, aListener );
303 // XUnoTunnel
304 sal_Int64 SAL_CALL SfxOfficeDispatch::getSomething( const ::com::sun::star::uno::Sequence< sal_Int8 >& aIdentifier ) throw(::com::sun::star::uno::RuntimeException, std::exception)
306 if ( aIdentifier == impl_getStaticIdentifier() )
307 return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this ));
308 else
309 return 0;
312 SfxOfficeDispatch::SfxOfficeDispatch( SfxBindings& rBindings, SfxDispatcher* pDispat, const SfxSlot* pSlot, const ::com::sun::star::util::URL& rURL )
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 pControllerItem = new SfxDispatchController_Impl( this, &rBindings, pDispat, pSlot, rURL );
318 SfxOfficeDispatch::SfxOfficeDispatch( SfxDispatcher* pDispat, const SfxSlot* pSlot, const ::com::sun::star::util::URL& rURL )
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 pControllerItem = new SfxDispatchController_Impl( this, NULL, pDispat, pSlot, rURL );
324 SfxOfficeDispatch::~SfxOfficeDispatch()
326 if ( pControllerItem )
328 // when dispatch object is released, destroy its connection to this object and destroy it
329 pControllerItem->UnBindController();
330 delete pControllerItem;
334 const ::com::sun::star::uno::Sequence< sal_Int8 >& SfxOfficeDispatch::impl_getStaticIdentifier()
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 static ::com::sun::star::uno::Sequence< sal_Int8 > seqID(reinterpret_cast<const sal_Int8*>(pGUID), 16) ;
339 return seqID ;
343 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 )
345 // ControllerItem is the Impl class
346 if ( pControllerItem )
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 true) );
358 #endif
359 pControllerItem->dispatch( aURL, aArgs, ::com::sun::star::uno::Reference < ::com::sun::star::frame::XDispatchResultListener >() );
363 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 )
367 // ControllerItem is the Impl class
368 if ( pControllerItem )
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 true) );
375 #endif
376 pControllerItem->dispatch( aURL, aArgs, rListener );
380 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 )
382 GetListeners().addInterface( aURL.Complete, aListener );
383 if ( pControllerItem )
385 // ControllerItem is the Impl class
386 pControllerItem->addStatusListener( aListener, aURL );
390 SfxDispatcher* SfxOfficeDispatch::GetDispatcher_Impl()
392 return pControllerItem->GetDispatcher();
395 void SfxOfficeDispatch::SetFrame(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& xFrame)
397 if ( pControllerItem )
398 pControllerItem->SetFrame( xFrame );
401 void SfxOfficeDispatch::SetMasterUnoCommand( bool bSet )
403 if ( pControllerItem )
404 pControllerItem->setMasterSlaveCommand( bSet );
407 // Determine if URL contains a master/slave command which must be handled a little bit different
408 bool SfxOfficeDispatch::IsMasterUnoCommand( const ::com::sun::star::util::URL& aURL )
410 return aURL.Protocol == ".uno:" && ( aURL.Path.indexOf( '.' ) > 0 );
413 OUString SfxOfficeDispatch::GetMasterUnoCommand( const ::com::sun::star::util::URL& aURL )
415 OUString aMasterCommand;
416 if ( IsMasterUnoCommand( aURL ))
418 sal_Int32 nIndex = aURL.Path.indexOf( '.' );
419 if ( nIndex > 0 )
420 aMasterCommand = aURL.Path.copy( 0, nIndex );
423 return aMasterCommand;
426 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 , nSlot( pSlot->GetSlotId() )
437 , pDispatch( pDisp )
438 , bMasterSlave( false )
439 , bVisible( true )
440 , pUnoName( pSlot->pUnoName )
442 if ( aDispatchURL.Protocol == "slot:" && pUnoName )
444 OStringBuffer aTmp(".uno:");
445 aTmp.append(pUnoName);
446 aDispatchURL.Complete = OStringToOUString(aTmp.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US);
447 Reference< XURLTransformer > xTrans( URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
448 xTrans->parseStrict( aDispatchURL );
451 SetId( nSlot );
452 if ( pBindings )
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 pBindings->ENTERREGISTRATIONS();
457 BindInternal_Impl( nSlot, pBindings );
458 pBindings->LEAVEREGISTRATIONS();
462 SfxDispatchController_Impl::~SfxDispatchController_Impl()
464 if ( pLastState && !IsInvalidItem( pLastState ) )
465 delete pLastState;
467 if ( pDispatch )
469 // disconnect
470 pDispatch->pControllerItem = NULL;
472 // force all listeners to release the dispatch object
473 ::com::sun::star::lang::EventObject aObject;
474 aObject.Source = (::cppu::OWeakObject*) pDispatch;
475 pDispatch->GetListeners().disposeAndClear( aObject );
479 void SfxDispatchController_Impl::SetFrame(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& _xFrame)
481 xFrame = _xFrame;
484 void SfxDispatchController_Impl::setMasterSlaveCommand( bool bSet )
486 bMasterSlave = bSet;
489 void SfxDispatchController_Impl::UnBindController()
491 pDispatch = NULL;
492 if ( IsBound() )
494 GetBindings().ENTERREGISTRATIONS();
495 SfxControllerItem::UnBind();
496 GetBindings().LEAVEREGISTRATIONS();
500 void SfxDispatchController_Impl::addParametersToArgs( const com::sun::star::util::URL& aURL, ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& rArgs )
502 // Extract the parameter from the URL and put them into the property value sequence
503 sal_Int32 nQueryIndex = aURL.Complete.indexOf( '?' );
504 if ( nQueryIndex > 0 )
506 OUString aParamString( aURL.Complete.copy( nQueryIndex+1 ));
507 sal_Int32 nIndex = 0;
510 OUString aToken = aParamString.getToken( 0, '&', nIndex );
512 sal_Int32 nParmIndex = 0;
513 OUString aParamType;
514 OUString aParamName = aToken.getToken( 0, '=', nParmIndex );
515 OUString aValue = (nParmIndex!=-1) ? aToken.getToken( 0, '=', nParmIndex ) : OUString();
517 if ( !aParamName.isEmpty() )
519 nParmIndex = 0;
520 aToken = aParamName;
521 aParamName = aToken.getToken( 0, ':', nParmIndex );
522 aParamType = (nParmIndex!=-1) ? aToken.getToken( 0, ':', nParmIndex ) : OUString();
525 sal_Int32 nLen = rArgs.getLength();
526 rArgs.realloc( nLen+1 );
527 rArgs[nLen].Name = aParamName;
529 if ( aParamType.isEmpty() )
531 // Default: LONG
532 rArgs[nLen].Value <<= aValue.toInt32();
534 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_BOOL], 4 ))
536 // sal_Bool support
537 rArgs[nLen].Value <<= aValue.toBoolean();
539 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_BYTE], 4 ))
541 // sal_uInt8 support
542 rArgs[nLen].Value <<= sal_Int8( aValue.toInt32() );
544 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_LONG], 4 ))
546 // LONG support
547 rArgs[nLen].Value <<= aValue.toInt32();
549 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_SHORT], 5 ))
551 // SHORT support
552 rArgs[nLen].Value <<= sal_Int8( aValue.toInt32() );
554 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_HYPER], 5 ))
556 // HYPER support
557 rArgs[nLen].Value <<= aValue.toInt64();
559 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_FLOAT], 5 ))
561 // FLOAT support
562 rArgs[nLen].Value <<= aValue.toFloat();
564 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_STRING], 6 ))
566 // STRING support
567 rArgs[nLen].Value <<= OUString( INetURLObject::decode( aValue, INetURLObject::DECODE_WITH_CHARSET ));
569 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_DOUBLE], 6))
571 // DOUBLE support
572 rArgs[nLen].Value <<= aValue.toDouble();
575 while ( nIndex >= 0 );
579 SfxMapUnit SfxDispatchController_Impl::GetCoreMetric( SfxItemPool& rPool, sal_uInt16 nSlotId )
581 sal_uInt16 nWhich = rPool.GetWhich( nSlotId );
582 return rPool.GetMetric( nWhich );
585 OUString SfxDispatchController_Impl::getSlaveCommand( const ::com::sun::star::util::URL& rURL )
587 OUString aSlaveCommand;
588 sal_Int32 nIndex = rURL.Path.indexOf( '.' );
589 if (( nIndex > 0 ) && ( nIndex < rURL.Path.getLength() ))
590 aSlaveCommand = rURL.Path.copy( nIndex+1 );
591 return aSlaveCommand;
594 namespace {
596 /// Class that collects the usage information - how many times what .uno: command was used.
597 class UsageInfo {
599 typedef std::map<OUString, int> UsageMap;
601 /// Are we collecting the info? We cache the value because the call to save can happen very late.
602 bool mbIsCollecting;
604 /// Command vs. how many times it was used
605 UsageMap maUsage;
607 public:
608 UsageInfo() : mbIsCollecting(false)
612 ~UsageInfo()
614 save();
617 /// Increment command's use.
618 void increment(const OUString &rCommand);
620 /// Save the usage data for the next session.
621 void save();
623 /// Modify the flag whether we are collecting.
624 void setCollecting(bool bIsCollecting) { mbIsCollecting = bIsCollecting; }
627 void UsageInfo::increment(const OUString &rCommand)
629 UsageMap::iterator it = maUsage.find(rCommand);
631 if (it != maUsage.end())
632 ++(it->second);
633 else
634 maUsage[rCommand] = 1;
637 void UsageInfo::save()
639 if (!mbIsCollecting)
640 return;
642 // TODO - do a real saving here, not only dump to the screen
643 std::cerr << "Usage information:" << std::endl;
644 for (UsageMap::const_iterator it = maUsage.begin(); it != maUsage.end(); ++it)
646 std::cerr << it->first << ';' << it->second << std::endl;
648 std::cerr << "Usage information end" << std::endl;
651 class theUsageInfo : public rtl::Static<UsageInfo, theUsageInfo> {};
653 /// Extracts information about the command + args, and stores that.
654 void collectUsageInformation(const util::URL& rURL, const uno::Sequence<beans::PropertyValue>& rArgs)
656 bool bCollecting = officecfg::Office::Common::Misc::CollectUsageInformation::get();
657 theUsageInfo::get().setCollecting(bCollecting);
658 if (!bCollecting)
659 return;
661 OUStringBuffer aBuffer;
663 // app identification [uh, several UNO calls :-(]
664 uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
665 uno::Reference<frame::XModuleManager2> xModuleManager(frame::ModuleManager::create(xContext));
666 uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(xContext);
667 uno::Reference<frame::XFrame> xFrame = xDesktop->getCurrentFrame();
669 OUString aModule(xModuleManager->identify(xFrame));
670 sal_Int32 nLastDot = aModule.lastIndexOf('.');
671 if (nLastDot >= 0)
672 aModule = aModule.copy(nLastDot + 1);
674 aBuffer.append(aModule);
675 aBuffer.append(';');
677 // command
678 aBuffer.append(rURL.Protocol);
679 aBuffer.append(rURL.Path);
680 sal_Int32 nCount = rArgs.getLength();
682 // parameters - only their names, not the values (could be sensitive!)
683 if (nCount > 0)
685 aBuffer.append('(');
686 for (sal_Int32 n = 0; n < nCount; n++)
688 const ::com::sun::star::beans::PropertyValue& rProp = rArgs[n];
689 if (n > 0)
690 aBuffer.append(',');
691 aBuffer.append(rProp.Name);
693 aBuffer.append(')');
696 OUString aCommand(aBuffer.makeStringAndClear());
698 // store
699 theUsageInfo::get().increment(aCommand);
704 void SAL_CALL SfxDispatchController_Impl::dispatch( const ::com::sun::star::util::URL& aURL,
705 const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& aArgs,
706 const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchResultListener >& rListener )
707 throw (css::uno::RuntimeException, std::exception)
709 collectUsageInformation(aURL, aArgs);
711 SolarMutexGuard aGuard;
712 if (
713 pDispatch &&
715 (aURL.Protocol == ".uno:" && aURL.Path == aDispatchURL.Path) ||
716 (aURL.Protocol == "slot:" && aURL.Path.toInt32() == GetId())
720 if ( !pDispatcher && pBindings )
721 pDispatcher = GetBindings().GetDispatcher_Impl();
723 ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue > lNewArgs;
724 sal_Int32 nCount = aArgs.getLength();
726 // Support for URL based arguments
727 INetURLObject aURLObj( aURL.Complete );
728 if ( aURLObj.HasParam() )
729 addParametersToArgs( aURL, lNewArgs );
731 // Try to find call mode and frame name inside given arguments...
732 SfxCallMode nCall = SfxCallMode::RECORD;
733 sal_Int32 nMarkArg = -1;
735 // Filter arguments which shouldn't be part of the sequence property value
736 sal_uInt16 nModifier(0);
737 std::vector< ::com::sun::star::beans::PropertyValue > aAddArgs;
738 for( sal_Int32 n=0; n<nCount; n++ )
740 const ::com::sun::star::beans::PropertyValue& rProp = aArgs[n];
741 if( rProp.Name == "SynchronMode" )
743 bool bTemp;
744 if( rProp.Value >>= bTemp )
745 nCall = bTemp ? SfxCallMode::SYNCHRON : SfxCallMode::ASYNCHRON;
747 else if( rProp.Name == "Bookmark" )
749 nMarkArg = n;
750 aAddArgs.push_back( aArgs[n] );
752 else if( rProp.Name == "KeyModifier" )
753 rProp.Value >>= nModifier;
754 else
755 aAddArgs.push_back( aArgs[n] );
758 // Add needed arguments to sequence property value
759 sal_uInt32 nAddArgs = aAddArgs.size();
760 if ( nAddArgs > 0 )
762 sal_uInt32 nIndex( lNewArgs.getLength() );
764 lNewArgs.realloc( lNewArgs.getLength()+aAddArgs.size() );
765 for ( sal_uInt32 i = 0; i < nAddArgs; i++ )
766 lNewArgs[nIndex++] = aAddArgs[i];
769 // Overwrite possible detected sychron argument, if real listener exists (currently no other way)
770 if ( rListener.is() )
771 nCall = SfxCallMode::SYNCHRON;
773 if( GetId() == SID_JUMPTOMARK && nMarkArg == - 1 )
775 // we offer dispatches for SID_JUMPTOMARK if the URL points to a bookmark inside the document
776 // so we must retrieve this as an argument from the parsed URL
777 lNewArgs.realloc( lNewArgs.getLength()+1 );
778 nMarkArg = lNewArgs.getLength()-1;
779 lNewArgs[nMarkArg].Name = "Bookmark";
780 lNewArgs[nMarkArg].Value <<= aURL.Mark;
783 css::uno::Reference< css::frame::XFrame > xFrameRef(xFrame.get(), css::uno::UNO_QUERY);
784 if (! xFrameRef.is() && pDispatcher)
786 SfxViewFrame* pViewFrame = pDispatcher->GetFrame();
787 if (pViewFrame)
788 xFrameRef = pViewFrame->GetFrame().GetFrameInterface();
791 bool bSuccess = false;
792 const SfxPoolItem* pItem = NULL;
793 SfxMapUnit eMapUnit( SFX_MAPUNIT_100TH_MM );
795 // Extra scope so that aInternalSet is destroyed before
796 // rListener->dispatchFinished potentially calls
797 // framework::Desktop::terminate -> SfxApplication::Deinitialize ->
798 // ~CntItemPool:
799 if (pDispatcher)
801 SfxAllItemSet aInternalSet( SfxGetpApp()->GetPool() );
802 if (xFrameRef.is()) // an empty set is no problem ... but an empty frame reference can be a problem !
803 aInternalSet.Put( SfxUnoFrameItem( SID_FILLFRAME, xFrameRef ) );
805 SfxShell* pShell( 0 );
806 // #i102619# Retrieve metric from shell before execution - the shell could be destroyed after execution
807 if ( pDispatcher->GetBindings() )
809 if ( !pDispatcher->IsLocked( GetId() ) )
811 const SfxSlot *pSlot = 0;
812 if ( pDispatcher->GetShellAndSlot_Impl( GetId(), &pShell, &pSlot, false,
813 SfxCallMode::MODAL==(nCall&SfxCallMode::MODAL), false ) )
815 if ( bMasterSlave )
817 // Extract slave command and add argument to the args list. Master slot MUST
818 // have a argument that has the same name as the master slot and type is SfxStringItem.
819 sal_Int32 nIndex = lNewArgs.getLength();
820 lNewArgs.realloc( nIndex+1 );
821 lNewArgs[nIndex].Name = OUString::createFromAscii( pSlot->pUnoName );
822 lNewArgs[nIndex].Value = makeAny( SfxDispatchController_Impl::getSlaveCommand( aDispatchURL ));
825 eMapUnit = GetCoreMetric( pShell->GetPool(), GetId() );
826 boost::scoped_ptr<SfxAllItemSet> xSet(new SfxAllItemSet(pShell->GetPool()));
827 TransformParameters(GetId(), lNewArgs, *xSet, pSlot);
828 if (xSet->Count())
830 // execute with arguments - call directly
831 pItem = pDispatcher->Execute(GetId(), nCall, xSet.get(), &aInternalSet, nModifier);
832 bSuccess = (pItem != NULL);
834 else
836 // Be sure to delete this before we send a dispatch
837 // request, which will destroy the current shell.
838 xSet.reset();
840 // execute using bindings, enables support for toggle/enum etc.
841 SfxRequest aReq( GetId(), nCall, pShell->GetPool() );
842 aReq.SetModifier( nModifier );
843 aReq.SetInternalArgs_Impl(aInternalSet);
844 pDispatcher->GetBindings()->Execute_Impl( aReq, pSlot, pShell );
845 pItem = aReq.GetReturnValue();
846 bSuccess = aReq.IsDone() || pItem != NULL;
849 #ifdef DBG_UTIL
850 else
851 DBG_WARNING("MacroPlayer: Unknown slot dispatched!");
852 #endif
855 else
857 eMapUnit = GetCoreMetric( SfxGetpApp()->GetPool(), GetId() );
858 // AppDispatcher
859 SfxAllItemSet aSet( SfxGetpApp()->GetPool() );
860 TransformParameters( GetId(), lNewArgs, aSet );
862 if ( aSet.Count() )
863 pItem = pDispatcher->Execute( GetId(), nCall, &aSet, &aInternalSet, nModifier );
864 else
865 // SfxRequests take empty sets as argument sets, GetArgs() returning non-zero!
866 pItem = pDispatcher->Execute( GetId(), nCall, 0, &aInternalSet, nModifier );
868 // no bindings, no invalidate ( usually done in SfxDispatcher::Call_Impl()! )
869 if ( SfxApplication::Get() )
871 SfxDispatcher* pAppDispat = SfxGetpApp()->GetAppDispatcher_Impl();
872 if ( pAppDispat )
874 const SfxPoolItem* pState=0;
875 SfxItemState eState = pDispatcher->QueryState( GetId(), pState );
876 StateChanged( GetId(), eState, pState );
880 bSuccess = (pItem != NULL);
884 if ( rListener.is() )
886 ::com::sun::star::frame::DispatchResultEvent aEvent;
887 if ( bSuccess )
888 aEvent.State = com::sun::star::frame::DispatchResultState::SUCCESS;
889 else
890 aEvent.State = com::sun::star::frame::DispatchResultState::FAILURE;
892 aEvent.Source = (::com::sun::star::frame::XDispatch*) pDispatch;
893 if ( bSuccess && pItem && !pItem->ISA(SfxVoidItem) )
895 sal_uInt16 nSubId( 0 );
896 if ( eMapUnit == SFX_MAPUNIT_TWIP )
897 nSubId |= CONVERT_TWIPS;
898 pItem->QueryValue( aEvent.Result, (sal_uInt8)nSubId );
901 rListener->dispatchFinished( aEvent );
906 SfxDispatcher* SfxDispatchController_Impl::GetDispatcher()
908 if ( !pDispatcher && pBindings )
909 pDispatcher = GetBindings().GetDispatcher_Impl();
910 return pDispatcher;
913 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 )
915 SolarMutexGuard aGuard;
916 if ( !pDispatch )
917 return;
919 // Use alternative QueryState call to have a valid UNO representation of the state.
920 ::com::sun::star::uno::Any aState;
921 if ( !pDispatcher && pBindings )
922 pDispatcher = GetBindings().GetDispatcher_Impl();
923 SfxItemState eState = pDispatcher ? pDispatcher->QueryState( GetId(), aState ) : SfxItemState::DONTCARE;
925 if ( eState == SfxItemState::DONTCARE )
927 // Use special uno struct to transport don't care state
928 ::com::sun::star::frame::status::ItemStatus aItemStatus;
929 aItemStatus.State = ::com::sun::star::frame::status::ItemState::DONT_CARE;
930 aState = makeAny( aItemStatus );
933 ::com::sun::star::frame::FeatureStateEvent aEvent;
934 aEvent.FeatureURL = aURL;
935 aEvent.Source = (::com::sun::star::frame::XDispatch*) pDispatch;
936 aEvent.Requery = sal_False;
937 if ( bVisible )
939 aEvent.IsEnabled = eState != SfxItemState::DISABLED;
940 aEvent.State = aState;
942 else
944 ::com::sun::star::frame::status::Visibility aVisibilityStatus;
945 aVisibilityStatus.bVisible = sal_False;
947 // MBA: we might decide to *not* disable "invisible" slots, but this would be
948 // a change that needs to adjust at least the testtool
949 aEvent.IsEnabled = sal_False;
950 aEvent.State = makeAny( aVisibilityStatus );
953 aListener->statusChanged( aEvent );
956 void SfxDispatchController_Impl::StateChanged( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState, SfxSlotServer* pSlotServ )
958 if ( !pDispatch )
959 return;
961 // Bindings instance notifies controller about a state change, listeners must be notified also
962 // Don't cache visibility state changes as they are volatile. We need our real state to send it
963 // to our controllers after visibility is set to true.
964 bool bNotify = true;
965 if ( pState && !IsInvalidItem( pState ) )
967 if ( !pState->ISA( SfxVisibilityItem ) )
969 if (pLastState && !IsInvalidItem(pLastState))
971 bNotify = pState->Type() != pLastState->Type() || *pState != *pLastState;
972 delete pLastState;
974 pLastState = !IsInvalidItem(pState) ? pState->Clone() : pState;
975 bVisible = true;
977 else
978 bVisible = static_cast<const SfxVisibilityItem *>(pState)->GetValue();
980 else
982 if ( pLastState && !IsInvalidItem( pLastState ) )
983 delete pLastState;
984 pLastState = pState;
987 if (bNotify)
989 ::com::sun::star::uno::Any aState;
990 if ( ( eState >= SfxItemState::DEFAULT ) && pState && !IsInvalidItem( pState ) && !pState->ISA(SfxVoidItem) )
992 // Retrieve metric from pool to have correct sub ID when calling QueryValue
993 sal_uInt16 nSubId( 0 );
994 SfxMapUnit eMapUnit( SFX_MAPUNIT_100TH_MM );
996 // retrieve the core metric
997 // it's enough to check the objectshell, the only shell that does not use the pool of the document
998 // is SfxViewFrame, but it hasn't any metric parameters
999 // TODO/LATER: what about the FormShell? Does it use any metric data?! Perhaps it should use the Pool of the document!
1000 if ( pSlotServ && pDispatcher )
1002 SfxShell* pShell = pDispatcher->GetShell( pSlotServ->GetShellLevel() );
1003 DBG_ASSERT( pShell, "Can't get core metric without shell!" );
1004 if ( pShell )
1005 eMapUnit = GetCoreMetric( pShell->GetPool(), nSID );
1008 if ( eMapUnit == SFX_MAPUNIT_TWIP )
1009 nSubId |= CONVERT_TWIPS;
1011 pState->QueryValue( aState, (sal_uInt8)nSubId );
1013 else if ( eState == SfxItemState::DONTCARE )
1015 // Use special uno struct to transport don't care state
1016 ::com::sun::star::frame::status::ItemStatus aItemStatus;
1017 aItemStatus.State = ::com::sun::star::frame::status::ItemState::DONT_CARE;
1018 aState = makeAny( aItemStatus );
1021 ::com::sun::star::frame::FeatureStateEvent aEvent;
1022 aEvent.FeatureURL = aDispatchURL;
1023 aEvent.Source = (::com::sun::star::frame::XDispatch*) pDispatch;
1024 aEvent.IsEnabled = eState != SfxItemState::DISABLED;
1025 aEvent.Requery = sal_False;
1026 aEvent.State = aState;
1028 if (pDispatcher && pDispatcher->GetFrame())
1030 InterceptLOKStateChangeEvent(
1031 pDispatcher->GetFrame()->GetObjectShell(), aEvent);
1034 ::cppu::OInterfaceContainerHelper* pContnr = pDispatch->GetListeners().getContainer ( aDispatchURL.Complete );
1035 if (pContnr) {
1036 ::cppu::OInterfaceIteratorHelper aIt( *pContnr );
1037 while( aIt.hasMoreElements() )
1041 static_cast< ::com::sun::star::frame::XStatusListener *>(aIt.next())->statusChanged( aEvent );
1043 catch (const ::com::sun::star::uno::RuntimeException&)
1045 aIt.remove();
1052 void SfxDispatchController_Impl::StateChanged( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
1054 StateChanged( nSID, eState, pState, 0 );
1057 void SfxDispatchController_Impl::InterceptLOKStateChangeEvent(const SfxObjectShell* objSh, const ::com::sun::star::frame::FeatureStateEvent& aEvent)
1059 if (!objSh || !objSh->isTiledRendering())
1060 return;
1062 if (aEvent.FeatureURL.Path == "Bold" ||
1063 aEvent.FeatureURL.Path == "Italic" ||
1064 aEvent.FeatureURL.Path == "Underline" ||
1065 aEvent.FeatureURL.Path == "Strikeout")
1068 OUStringBuffer aBuffer;
1069 aBuffer.append(aEvent.FeatureURL.Complete);
1070 aBuffer.append("=");
1071 bool bTemp = false;
1072 aEvent.State >>= bTemp;
1073 aBuffer.append(bTemp);
1075 OUString payload = aBuffer.makeStringAndClear();
1076 objSh->libreOfficeKitCallback(LOK_CALLBACK_STATE_CHANGED, payload.toUtf8().getStr());
1080 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */