1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/awt/FontDescriptor.hpp>
32 #include <com/sun/star/util/URLTransformer.hpp>
33 #include <com/sun/star/util/XURLTransformer.hpp>
34 #include <com/sun/star/frame/Desktop.hpp>
35 #include <com/sun/star/frame/XController.hpp>
36 #include <com/sun/star/frame/XFrameActionListener.hpp>
37 #include <com/sun/star/frame/XComponentLoader.hpp>
38 #include <com/sun/star/frame/XFrame.hpp>
39 #include <com/sun/star/frame/FrameActionEvent.hpp>
40 #include <com/sun/star/frame/FrameAction.hpp>
41 #include <com/sun/star/frame/status/FontHeight.hpp>
42 #include <com/sun/star/frame/status/ItemStatus.hpp>
43 #include <com/sun/star/frame/status/ItemState.hpp>
44 #include <com/sun/star/frame/status/Template.hpp>
45 #include <com/sun/star/frame/DispatchResultState.hpp>
46 #include <com/sun/star/frame/ModuleManager.hpp>
47 #include <com/sun/star/frame/status/Visibility.hpp>
48 #include <comphelper/processfactory.hxx>
49 #include <comphelper/sequence.hxx>
50 #include <officecfg/Office/Common.hxx>
51 #include <osl/mutex.hxx>
52 #include <uno/current_context.hxx>
53 #include <vcl/svapp.hxx>
55 #include <sfx2/app.hxx>
56 #include <sfx2/unoctitm.hxx>
57 #include <sfx2/viewfrm.hxx>
58 #include <sfx2/frame.hxx>
59 #include <sfx2/ctrlitem.hxx>
60 #include <sfx2/sfxuno.hxx>
61 #include <sfx2/bindings.hxx>
62 #include <sfx2/dispatch.hxx>
63 #include <sfx2/sfxsids.hrc>
64 #include <sfx2/request.hxx>
65 #include "statcach.hxx"
66 #include <sfx2/msgpool.hxx>
67 #include <sfx2/objsh.hxx>
68 #include <sfx2/viewsh.hxx>
69 #include <osl/file.hxx>
70 #include <rtl/ustring.hxx>
71 #include <unotools/pathoptions.hxx>
78 #include <sal/log.hxx>
79 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
80 #include <comphelper/lok.hxx>
82 using namespace ::com::sun::star
;
83 using namespace ::com::sun::star::uno
;
84 using namespace ::com::sun::star::util
;
99 const char* URLTypeNames
[URLType_COUNT
] =
111 static void InterceptLOKStateChangeEvent(const SfxViewFrame
* pViewFrame
, const css::frame::FeatureStateEvent
& aEvent
);
113 void SfxStatusDispatcher::ReleaseAll()
115 css::lang::EventObject aObject
;
116 aObject
.Source
= static_cast<cppu::OWeakObject
*>(this);
117 aListeners
.disposeAndClear( aObject
);
120 void SAL_CALL
SfxStatusDispatcher::dispatch( const css::util::URL
&, const css::uno::Sequence
< css::beans::PropertyValue
>& ) throw ( css::uno::RuntimeException
, std::exception
)
124 void SAL_CALL
SfxStatusDispatcher::dispatchWithNotification(
125 const css::util::URL
&,
126 const css::uno::Sequence
< css::beans::PropertyValue
>&,
127 const css::uno::Reference
< css::frame::XDispatchResultListener
>& ) throw( css::uno::RuntimeException
, std::exception
)
131 SfxStatusDispatcher::SfxStatusDispatcher()
132 : aListeners( aMutex
)
136 void SAL_CALL
SfxStatusDispatcher::addStatusListener(const css::uno::Reference
< css::frame::XStatusListener
> & aListener
, const css::util::URL
& aURL
) throw ( css::uno::RuntimeException
, std::exception
)
138 aListeners
.addInterface( aURL
.Complete
, aListener
);
139 if ( aURL
.Complete
== ".uno:LifeTime" )
141 css::frame::FeatureStateEvent aEvent
;
142 aEvent
.FeatureURL
= aURL
;
143 aEvent
.Source
= static_cast<css::frame::XDispatch
*>(this);
144 aEvent
.IsEnabled
= true;
145 aEvent
.Requery
= false;
146 aListener
->statusChanged( aEvent
);
150 void SAL_CALL
SfxStatusDispatcher::removeStatusListener( const css::uno::Reference
< css::frame::XStatusListener
> & aListener
, const css::util::URL
& aURL
) throw ( css::uno::RuntimeException
, std::exception
)
152 aListeners
.removeInterface( aURL
.Complete
, aListener
);
157 sal_Int64 SAL_CALL
SfxOfficeDispatch::getSomething( const css::uno::Sequence
< sal_Int8
>& aIdentifier
) throw(css::uno::RuntimeException
, std::exception
)
159 if ( aIdentifier
== impl_getStaticIdentifier() )
160 return sal::static_int_cast
< sal_Int64
>( reinterpret_cast< sal_IntPtr
>( this ));
165 SfxOfficeDispatch::SfxOfficeDispatch( SfxBindings
& rBindings
, SfxDispatcher
* pDispat
, const SfxSlot
* pSlot
, const css::util::URL
& rURL
)
166 : pImpl( new SfxDispatchController_Impl( this, &rBindings
, pDispat
, pSlot
, rURL
))
168 // pImpl is an adapter that shows a css::frame::XDispatch-Interface to the outside and uses a SfxControllerItem to monitor a state
172 SfxOfficeDispatch::SfxOfficeDispatch( SfxDispatcher
* pDispat
, const SfxSlot
* pSlot
, const css::util::URL
& rURL
)
173 : pImpl( new SfxDispatchController_Impl( this, nullptr, pDispat
, pSlot
, rURL
))
175 // pImpl is an adapter that shows a css::frame::XDispatch-Interface to the outside and uses a SfxControllerItem to monitor a state
178 SfxOfficeDispatch::~SfxOfficeDispatch()
182 // when dispatch object is released, destroy its connection to this object and destroy it
183 pImpl
->UnBindController();
187 const css::uno::Sequence
< sal_Int8
>& SfxOfficeDispatch::impl_getStaticIdentifier()
189 // {38 57 CA 80 09 36 11 d4 83 FE 00 50 04 52 6B 21}
190 static const sal_uInt8 pGUID
[16] = { 0x38, 0x57, 0xCA, 0x80, 0x09, 0x36, 0x11, 0xd4, 0x83, 0xFE, 0x00, 0x50, 0x04, 0x52, 0x6B, 0x21 };
191 static css::uno::Sequence
< sal_Int8
> seqID(reinterpret_cast<const sal_Int8
*>(pGUID
), 16) ;
196 void SAL_CALL
SfxOfficeDispatch::dispatch( const css::util::URL
& aURL
, const css::uno::Sequence
< css::beans::PropertyValue
>& aArgs
) throw ( css::uno::RuntimeException
, std::exception
)
198 // ControllerItem is the Impl class
201 #if HAVE_FEATURE_JAVA
202 // The JavaContext contains an interaction handler which is used when
203 // the creation of a Java Virtual Machine fails. The second parameter
204 // indicates, that there shall only be one user notification (message box)
205 // even if the same error (interaction) reoccurs. The effect is, that if a
206 // user selects a menu entry than they may get only one notification that
207 // a JRE is not selected.
208 css::uno::ContextLayer
layer(
209 new svt::JavaContext( css::uno::getCurrentContext() ) );
211 pImpl
->dispatch( aURL
, aArgs
, css::uno::Reference
< css::frame::XDispatchResultListener
>() );
215 void SAL_CALL
SfxOfficeDispatch::dispatchWithNotification( const css::util::URL
& aURL
,
216 const css::uno::Sequence
< css::beans::PropertyValue
>& aArgs
,
217 const css::uno::Reference
< css::frame::XDispatchResultListener
>& rListener
) throw( css::uno::RuntimeException
, std::exception
)
219 // ControllerItem is the Impl class
222 #if HAVE_FEATURE_JAVA
223 // see comment for SfxOfficeDispatch::dispatch
224 css::uno::ContextLayer
layer( new svt::JavaContext( css::uno::getCurrentContext() ) );
226 pImpl
->dispatch( aURL
, aArgs
, rListener
);
230 void SAL_CALL
SfxOfficeDispatch::addStatusListener(const css::uno::Reference
< css::frame::XStatusListener
> & aListener
, const css::util::URL
& aURL
) throw ( css::uno::RuntimeException
, std::exception
)
232 GetListeners().addInterface( aURL
.Complete
, aListener
);
235 // ControllerItem is the Impl class
236 pImpl
->addStatusListener( aListener
, aURL
);
240 SfxDispatcher
* SfxOfficeDispatch::GetDispatcher_Impl()
242 return pImpl
->GetDispatcher();
245 void SfxOfficeDispatch::SetFrame(const css::uno::Reference
< css::frame::XFrame
>& xFrame
)
248 pImpl
->SetFrame( xFrame
);
251 void SfxOfficeDispatch::SetMasterUnoCommand( bool bSet
)
254 pImpl
->setMasterSlaveCommand( bSet
);
257 // Determine if URL contains a master/slave command which must be handled a little bit different
258 bool SfxOfficeDispatch::IsMasterUnoCommand( const css::util::URL
& aURL
)
260 return aURL
.Protocol
== ".uno:" && ( aURL
.Path
.indexOf( '.' ) > 0 );
263 OUString
SfxOfficeDispatch::GetMasterUnoCommand( const css::util::URL
& aURL
)
265 OUString aMasterCommand
;
266 if ( IsMasterUnoCommand( aURL
))
268 sal_Int32 nIndex
= aURL
.Path
.indexOf( '.' );
270 aMasterCommand
= aURL
.Path
.copy( 0, nIndex
);
273 return aMasterCommand
;
276 SfxDispatchController_Impl::SfxDispatchController_Impl(
277 SfxOfficeDispatch
* pDisp
,
279 SfxDispatcher
* pDispat
,
280 const SfxSlot
* pSlot
,
281 const css::util::URL
& rURL
)
282 : aDispatchURL( rURL
)
283 , pDispatcher( pDispat
)
285 , pLastState( nullptr )
286 , nSlot( pSlot
->GetSlotId() )
288 , bMasterSlave( false )
290 , pUnoName( pSlot
->pUnoName
)
292 if ( aDispatchURL
.Protocol
== "slot:" && pUnoName
)
294 OStringBuffer
aTmp(".uno:");
295 aTmp
.append(pUnoName
);
296 aDispatchURL
.Complete
= OStringToOUString(aTmp
.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US
);
297 Reference
< XURLTransformer
> xTrans( URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
298 xTrans
->parseStrict( aDispatchURL
);
304 // Bind immediately to enable the cache to recycle dispatches when asked for the same command
305 // a command in "slot" or in ".uno" notation must be treated as identical commands!
306 pBindings
->ENTERREGISTRATIONS();
307 BindInternal_Impl( nSlot
, pBindings
);
308 pBindings
->LEAVEREGISTRATIONS();
312 SfxDispatchController_Impl::~SfxDispatchController_Impl()
314 if ( pLastState
&& !IsInvalidItem( pLastState
) )
320 pDispatch
->pImpl
= nullptr;
322 // force all listeners to release the dispatch object
323 css::lang::EventObject aObject
;
324 aObject
.Source
= static_cast<cppu::OWeakObject
*>(pDispatch
);
325 pDispatch
->GetListeners().disposeAndClear( aObject
);
329 void SfxDispatchController_Impl::SetFrame(const css::uno::Reference
< css::frame::XFrame
>& _xFrame
)
334 void SfxDispatchController_Impl::setMasterSlaveCommand( bool bSet
)
339 void SfxDispatchController_Impl::UnBindController()
344 GetBindings().ENTERREGISTRATIONS();
345 SfxControllerItem::UnBind();
346 GetBindings().LEAVEREGISTRATIONS();
350 void SfxDispatchController_Impl::addParametersToArgs( const css::util::URL
& aURL
, css::uno::Sequence
< css::beans::PropertyValue
>& rArgs
)
352 // Extract the parameter from the URL and put them into the property value sequence
353 sal_Int32 nQueryIndex
= aURL
.Complete
.indexOf( '?' );
354 if ( nQueryIndex
> 0 )
356 OUString
aParamString( aURL
.Complete
.copy( nQueryIndex
+1 ));
357 sal_Int32 nIndex
= 0;
360 OUString aToken
= aParamString
.getToken( 0, '&', nIndex
);
362 sal_Int32 nParmIndex
= 0;
364 OUString aParamName
= aToken
.getToken( 0, '=', nParmIndex
);
365 OUString aValue
= (nParmIndex
!=-1) ? aToken
.getToken( 0, '=', nParmIndex
) : OUString();
367 if ( !aParamName
.isEmpty() )
371 aParamName
= aToken
.getToken( 0, ':', nParmIndex
);
372 aParamType
= (nParmIndex
!=-1) ? aToken
.getToken( 0, ':', nParmIndex
) : OUString();
375 sal_Int32 nLen
= rArgs
.getLength();
376 rArgs
.realloc( nLen
+1 );
377 rArgs
[nLen
].Name
= aParamName
;
379 if ( aParamType
.isEmpty() )
382 rArgs
[nLen
].Value
<<= aValue
.toInt32();
384 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_BOOL
], 4 ))
387 rArgs
[nLen
].Value
<<= aValue
.toBoolean();
389 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_BYTE
], 4 ))
392 rArgs
[nLen
].Value
<<= sal_Int8( aValue
.toInt32() );
394 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_LONG
], 4 ))
397 rArgs
[nLen
].Value
<<= aValue
.toInt32();
399 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_SHORT
], 5 ))
402 rArgs
[nLen
].Value
<<= sal_Int8( aValue
.toInt32() );
404 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_HYPER
], 5 ))
407 rArgs
[nLen
].Value
<<= aValue
.toInt64();
409 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_FLOAT
], 5 ))
412 rArgs
[nLen
].Value
<<= aValue
.toFloat();
414 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_STRING
], 6 ))
417 rArgs
[nLen
].Value
<<= OUString( INetURLObject::decode( aValue
, INetURLObject::DECODE_WITH_CHARSET
));
419 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_DOUBLE
], 6))
422 rArgs
[nLen
].Value
<<= aValue
.toDouble();
425 while ( nIndex
>= 0 );
429 SfxMapUnit
SfxDispatchController_Impl::GetCoreMetric( SfxItemPool
& rPool
, sal_uInt16 nSlotId
)
431 sal_uInt16 nWhich
= rPool
.GetWhich( nSlotId
);
432 return rPool
.GetMetric( nWhich
);
435 OUString
SfxDispatchController_Impl::getSlaveCommand( const css::util::URL
& rURL
)
437 OUString aSlaveCommand
;
438 sal_Int32 nIndex
= rURL
.Path
.indexOf( '.' );
439 if (( nIndex
> 0 ) && ( nIndex
< rURL
.Path
.getLength() ))
440 aSlaveCommand
= rURL
.Path
.copy( nIndex
+1 );
441 return aSlaveCommand
;
446 /// Class that collects the usage information - how many times what .uno: command was used.
449 typedef std::map
<OUString
, int> UsageMap
;
451 /// Are we collecting the info? We cache the value because the call to save can happen very late.
454 /// Command vs. how many times it was used
458 UsageInfo() : mbIsCollecting(false)
467 /// Increment command's use.
468 void increment(const OUString
&rCommand
);
470 /// Save the usage data for the next session.
473 /// Modify the flag whether we are collecting.
474 void setCollecting(bool bIsCollecting
) { mbIsCollecting
= bIsCollecting
; }
477 void UsageInfo::increment(const OUString
&rCommand
)
479 UsageMap::iterator it
= maUsage
.find(rCommand
);
481 if (it
!= maUsage
.end())
484 maUsage
[rCommand
] = 1;
487 void UsageInfo::save()
492 OUString
path(SvtPathOptions().GetConfigPath());
494 osl::Directory::createPath(path
);
496 //get system time information.
497 TimeValue systemTime
;
499 oslDateTime localDateTime
;
500 osl_getSystemTime( &systemTime
);
501 osl_getLocalTimeFromSystemTime( &systemTime
, &localTime
);
502 osl_getDateTimeFromTimeValue( &localTime
, &localDateTime
);
505 sprintf(time
,"%4i-%02i-%02iT%02i_%02i_%02i", localDateTime
.Year
, localDateTime
.Month
, localDateTime
.Day
, localDateTime
.Hours
, localDateTime
.Minutes
, localDateTime
.Seconds
);
507 //filename type: usage-YYYY-MM-DDTHH_MM_SS.csv
508 OUString filename
= "usage-" + OUString::createFromAscii(time
) + ".csv";
511 osl::File
file(path
);
513 if( file
.open(osl_File_OpenFlag_Read
| osl_File_OpenFlag_Write
| osl_File_OpenFlag_Create
) == osl::File::E_None
)
515 OString aUsageInfoMsg
= "Document Type;Command;Count";
517 for (UsageMap::const_iterator it
= maUsage
.begin(); it
!= maUsage
.end(); ++it
)
518 aUsageInfoMsg
+= "\n" + it
->first
.toUtf8() + ";" + OString::number(it
->second
);
520 sal_uInt64 written
= 0;
521 file
.write(aUsageInfoMsg
.pData
->buffer
, aUsageInfoMsg
.getLength(), written
);
526 class theUsageInfo
: public rtl::Static
<UsageInfo
, theUsageInfo
> {};
528 /// Extracts information about the command + args, and stores that.
529 void collectUsageInformation(const util::URL
& rURL
, const uno::Sequence
<beans::PropertyValue
>& rArgs
)
531 bool bCollecting
= getenv("LO_COLLECT_USAGE") || officecfg::Office::Common::Misc::CollectUsageInformation::get();
532 theUsageInfo::get().setCollecting(bCollecting
);
536 OUStringBuffer aBuffer
;
538 // app identification [uh, several UNO calls :-(]
539 uno::Reference
<uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
540 uno::Reference
<frame::XModuleManager2
> xModuleManager(frame::ModuleManager::create(xContext
));
541 uno::Reference
<frame::XDesktop2
> xDesktop
= frame::Desktop::create(xContext
);
542 uno::Reference
<frame::XFrame
> xFrame
= xDesktop
->getCurrentFrame();
544 OUString
aModule(xModuleManager
->identify(xFrame
));
545 sal_Int32 nLastDot
= aModule
.lastIndexOf('.');
547 aModule
= aModule
.copy(nLastDot
+ 1);
549 aBuffer
.append(aModule
);
553 aBuffer
.append(rURL
.Protocol
);
554 aBuffer
.append(rURL
.Path
);
555 sal_Int32 nCount
= rArgs
.getLength();
557 // parameters - only their names, not the values (could be sensitive!)
561 for (sal_Int32 n
= 0; n
< nCount
; n
++)
563 const css::beans::PropertyValue
& rProp
= rArgs
[n
];
566 aBuffer
.append(rProp
.Name
);
571 OUString
aCommand(aBuffer
.makeStringAndClear());
574 theUsageInfo::get().increment(aCommand
);
579 void SAL_CALL
SfxDispatchController_Impl::dispatch( const css::util::URL
& aURL
,
580 const css::uno::Sequence
< css::beans::PropertyValue
>& aArgs
,
581 const css::uno::Reference
< css::frame::XDispatchResultListener
>& rListener
)
582 throw (css::uno::RuntimeException
, std::exception
)
584 collectUsageInformation(aURL
, aArgs
);
586 SolarMutexGuard aGuard
;
590 (aURL
.Protocol
== ".uno:" && aURL
.Path
== aDispatchURL
.Path
) ||
591 (aURL
.Protocol
== "slot:" && aURL
.Path
.toInt32() == GetId())
595 if ( !pDispatcher
&& pBindings
)
596 pDispatcher
= GetBindings().GetDispatcher_Impl();
598 css::uno::Sequence
< css::beans::PropertyValue
> lNewArgs
;
599 sal_Int32 nCount
= aArgs
.getLength();
601 // Support for URL based arguments
602 INetURLObject
aURLObj( aURL
.Complete
);
603 if ( aURLObj
.HasParam() )
604 addParametersToArgs( aURL
, lNewArgs
);
606 // Try to find call mode and frame name inside given arguments...
607 SfxCallMode nCall
= SfxCallMode::RECORD
;
608 sal_Int32 nMarkArg
= -1;
610 // Filter arguments which shouldn't be part of the sequence property value
611 sal_uInt16
nModifier(0);
612 std::vector
< css::beans::PropertyValue
> aAddArgs
;
613 for( sal_Int32 n
=0; n
<nCount
; n
++ )
615 const css::beans::PropertyValue
& rProp
= aArgs
[n
];
616 if( rProp
.Name
== "SynchronMode" )
619 if( rProp
.Value
>>= bTemp
)
620 nCall
= bTemp
? SfxCallMode::SYNCHRON
: SfxCallMode::ASYNCHRON
;
622 else if( rProp
.Name
== "Bookmark" )
625 aAddArgs
.push_back( aArgs
[n
] );
627 else if( rProp
.Name
== "KeyModifier" )
628 rProp
.Value
>>= nModifier
;
630 aAddArgs
.push_back( aArgs
[n
] );
633 // Add needed arguments to sequence property value
634 sal_uInt32 nAddArgs
= aAddArgs
.size();
637 sal_uInt32
nIndex( lNewArgs
.getLength() );
639 lNewArgs
.realloc( lNewArgs
.getLength()+aAddArgs
.size() );
640 for ( sal_uInt32 i
= 0; i
< nAddArgs
; i
++ )
641 lNewArgs
[nIndex
++] = aAddArgs
[i
];
644 // Overwrite possible detected synchron argument, if real listener exists (currently no other way)
645 if ( rListener
.is() )
646 nCall
= SfxCallMode::SYNCHRON
;
648 if( GetId() == SID_JUMPTOMARK
&& nMarkArg
== - 1 )
650 // we offer dispatches for SID_JUMPTOMARK if the URL points to a bookmark inside the document
651 // so we must retrieve this as an argument from the parsed URL
652 lNewArgs
.realloc( lNewArgs
.getLength()+1 );
653 nMarkArg
= lNewArgs
.getLength()-1;
654 lNewArgs
[nMarkArg
].Name
= "Bookmark";
655 lNewArgs
[nMarkArg
].Value
<<= aURL
.Mark
;
658 css::uno::Reference
< css::frame::XFrame
> xFrameRef(xFrame
.get(), css::uno::UNO_QUERY
);
659 if (! xFrameRef
.is() && pDispatcher
)
661 SfxViewFrame
* pViewFrame
= pDispatcher
->GetFrame();
663 xFrameRef
= pViewFrame
->GetFrame().GetFrameInterface();
666 bool bSuccess
= false;
667 const SfxPoolItem
* pItem
= nullptr;
668 SfxMapUnit
eMapUnit( SFX_MAPUNIT_100TH_MM
);
670 // Extra scope so that aInternalSet is destroyed before
671 // rListener->dispatchFinished potentially calls
672 // framework::Desktop::terminate -> SfxApplication::Deinitialize ->
676 SfxAllItemSet
aInternalSet( SfxGetpApp()->GetPool() );
677 if (xFrameRef
.is()) // an empty set is no problem ... but an empty frame reference can be a problem !
678 aInternalSet
.Put( SfxUnoFrameItem( SID_FILLFRAME
, xFrameRef
) );
680 SfxShell
* pShell( nullptr );
681 // #i102619# Retrieve metric from shell before execution - the shell could be destroyed after execution
682 if ( pDispatcher
->GetBindings() )
684 if ( !pDispatcher
->IsLocked( GetId() ) )
686 const SfxSlot
*pSlot
= nullptr;
687 if ( pDispatcher
->GetShellAndSlot_Impl( GetId(), &pShell
, &pSlot
, false,
688 SfxCallMode::MODAL
==(nCall
&SfxCallMode::MODAL
), false ) )
692 // Extract slave command and add argument to the args list. Master slot MUST
693 // have a argument that has the same name as the master slot and type is SfxStringItem.
694 sal_Int32 nIndex
= lNewArgs
.getLength();
695 lNewArgs
.realloc( nIndex
+1 );
696 lNewArgs
[nIndex
].Name
= OUString::createFromAscii( pSlot
->pUnoName
);
697 lNewArgs
[nIndex
].Value
= makeAny( SfxDispatchController_Impl::getSlaveCommand( aDispatchURL
));
700 eMapUnit
= GetCoreMetric( pShell
->GetPool(), GetId() );
701 std::unique_ptr
<SfxAllItemSet
> xSet(new SfxAllItemSet(pShell
->GetPool()));
702 TransformParameters(GetId(), lNewArgs
, *xSet
, pSlot
);
705 // execute with arguments - call directly
706 pItem
= pDispatcher
->Execute(GetId(), nCall
, xSet
.get(), &aInternalSet
, nModifier
);
707 if ( pItem
!= nullptr )
709 if ( dynamic_cast< const SfxBoolItem
*>( pItem
) != nullptr )
710 bSuccess
= dynamic_cast< const SfxBoolItem
*>( pItem
)->GetValue();
711 else if ( dynamic_cast< const SfxVoidItem
*>( pItem
) == nullptr )
712 bSuccess
= true; // all other types are true
714 // else bSuccess = false look to line 664 it is false
718 // Be sure to delete this before we send a dispatch
719 // request, which will destroy the current shell.
722 // execute using bindings, enables support for toggle/enum etc.
723 SfxRequest
aReq( GetId(), nCall
, pShell
->GetPool() );
724 aReq
.SetModifier( nModifier
);
725 aReq
.SetInternalArgs_Impl(aInternalSet
);
726 pDispatcher
->GetBindings()->Execute_Impl( aReq
, pSlot
, pShell
);
727 pItem
= aReq
.GetReturnValue();
728 bSuccess
= aReq
.IsDone() || pItem
!= nullptr;
733 SAL_INFO("sfx.control", "MacroPlayer: Unknown slot dispatched!");
739 eMapUnit
= GetCoreMetric( SfxGetpApp()->GetPool(), GetId() );
741 SfxAllItemSet
aSet( SfxGetpApp()->GetPool() );
742 TransformParameters( GetId(), lNewArgs
, aSet
);
745 pItem
= pDispatcher
->Execute( GetId(), nCall
, &aSet
, &aInternalSet
, nModifier
);
747 // SfxRequests take empty sets as argument sets, GetArgs() returning non-zero!
748 pItem
= pDispatcher
->Execute( GetId(), nCall
, nullptr, &aInternalSet
, nModifier
);
750 // no bindings, no invalidate ( usually done in SfxDispatcher::Call_Impl()! )
751 if ( SfxApplication::Get() )
753 SfxDispatcher
* pAppDispat
= SfxGetpApp()->GetAppDispatcher_Impl();
756 const SfxPoolItem
* pState
=nullptr;
757 SfxItemState eState
= pDispatcher
->QueryState( GetId(), pState
);
758 StateChanged( GetId(), eState
, pState
);
762 bSuccess
= (pItem
!= nullptr);
766 if ( rListener
.is() )
768 css::frame::DispatchResultEvent aEvent
;
770 aEvent
.State
= css::frame::DispatchResultState::SUCCESS
;
772 aEvent
.State
= css::frame::DispatchResultState::FAILURE
;
774 aEvent
.Source
= static_cast<css::frame::XDispatch
*>(pDispatch
);
775 if ( bSuccess
&& pItem
&& dynamic_cast< const SfxVoidItem
*>( pItem
) == nullptr )
777 sal_uInt16
nSubId( 0 );
778 if ( eMapUnit
== SFX_MAPUNIT_TWIP
)
779 nSubId
|= CONVERT_TWIPS
;
780 pItem
->QueryValue( aEvent
.Result
, (sal_uInt8
)nSubId
);
783 rListener
->dispatchFinished( aEvent
);
788 SfxDispatcher
* SfxDispatchController_Impl::GetDispatcher()
790 if ( !pDispatcher
&& pBindings
)
791 pDispatcher
= GetBindings().GetDispatcher_Impl();
795 void SAL_CALL
SfxDispatchController_Impl::addStatusListener(const css::uno::Reference
< css::frame::XStatusListener
> & aListener
, const css::util::URL
& aURL
) throw ( css::uno::RuntimeException
)
797 SolarMutexGuard aGuard
;
801 // Use alternative QueryState call to have a valid UNO representation of the state.
802 css::uno::Any aState
;
803 if ( !pDispatcher
&& pBindings
)
804 pDispatcher
= GetBindings().GetDispatcher_Impl();
805 SfxItemState eState
= pDispatcher
? pDispatcher
->QueryState( GetId(), aState
) : SfxItemState::DONTCARE
;
807 if ( eState
== SfxItemState::DONTCARE
)
809 // Use special uno struct to transport don't care state
810 css::frame::status::ItemStatus aItemStatus
;
811 aItemStatus
.State
= css::frame::status::ItemState::DONT_CARE
;
812 aState
= makeAny( aItemStatus
);
815 css::frame::FeatureStateEvent aEvent
;
816 aEvent
.FeatureURL
= aURL
;
817 aEvent
.Source
= static_cast<css::frame::XDispatch
*>(pDispatch
);
818 aEvent
.Requery
= false;
821 aEvent
.IsEnabled
= eState
!= SfxItemState::DISABLED
;
822 aEvent
.State
= aState
;
826 css::frame::status::Visibility aVisibilityStatus
;
827 aVisibilityStatus
.bVisible
= false;
829 // MBA: we might decide to *not* disable "invisible" slots, but this would be
830 // a change that needs to adjust at least the testtool
831 aEvent
.IsEnabled
= false;
832 aEvent
.State
= makeAny( aVisibilityStatus
);
835 aListener
->statusChanged( aEvent
);
838 void SfxDispatchController_Impl::StateChanged( sal_uInt16 nSID
, SfxItemState eState
, const SfxPoolItem
* pState
, SfxSlotServer
* pSlotServ
)
843 // Bindings instance notifies controller about a state change, listeners must be notified also
844 // Don't cache visibility state changes as they are volatile. We need our real state to send it
845 // to our controllers after visibility is set to true.
847 if ( pState
&& !IsInvalidItem( pState
) )
849 if ( dynamic_cast< const SfxVisibilityItem
*>( pState
) == nullptr )
851 if (pLastState
&& !IsInvalidItem(pLastState
))
853 bNotify
= typeid(*pState
) != typeid(*pLastState
) || *pState
!= *pLastState
;
856 pLastState
= !IsInvalidItem(pState
) ? pState
->Clone() : pState
;
860 bVisible
= static_cast<const SfxVisibilityItem
*>(pState
)->GetValue();
864 if ( pLastState
&& !IsInvalidItem( pLastState
) )
871 css::uno::Any aState
;
872 if ( ( eState
>= SfxItemState::DEFAULT
) && pState
&& !IsInvalidItem( pState
) && dynamic_cast< const SfxVoidItem
*>( pState
) == nullptr )
874 // Retrieve metric from pool to have correct sub ID when calling QueryValue
875 sal_uInt16
nSubId( 0 );
876 SfxMapUnit
eMapUnit( SFX_MAPUNIT_100TH_MM
);
878 // retrieve the core metric
879 // it's enough to check the objectshell, the only shell that does not use the pool of the document
880 // is SfxViewFrame, but it hasn't any metric parameters
881 // TODO/LATER: what about the FormShell? Does it use any metric data?! Perhaps it should use the Pool of the document!
882 if ( pSlotServ
&& pDispatcher
)
884 SfxShell
* pShell
= pDispatcher
->GetShell( pSlotServ
->GetShellLevel() );
885 DBG_ASSERT( pShell
, "Can't get core metric without shell!" );
887 eMapUnit
= GetCoreMetric( pShell
->GetPool(), nSID
);
890 if ( eMapUnit
== SFX_MAPUNIT_TWIP
)
891 nSubId
|= CONVERT_TWIPS
;
893 pState
->QueryValue( aState
, (sal_uInt8
)nSubId
);
895 else if ( eState
== SfxItemState::DONTCARE
)
897 // Use special uno struct to transport don't care state
898 css::frame::status::ItemStatus aItemStatus
;
899 aItemStatus
.State
= css::frame::status::ItemState::DONT_CARE
;
900 aState
= makeAny( aItemStatus
);
903 css::frame::FeatureStateEvent aEvent
;
904 aEvent
.FeatureURL
= aDispatchURL
;
905 aEvent
.Source
= static_cast<css::frame::XDispatch
*>(pDispatch
);
906 aEvent
.IsEnabled
= eState
!= SfxItemState::DISABLED
;
907 aEvent
.Requery
= false;
908 aEvent
.State
= aState
;
910 if (pDispatcher
&& pDispatcher
->GetFrame())
912 InterceptLOKStateChangeEvent(pDispatcher
->GetFrame(), aEvent
);
915 ::cppu::OInterfaceContainerHelper
* pContnr
= pDispatch
->GetListeners().getContainer ( aDispatchURL
.Complete
);
917 ::cppu::OInterfaceIteratorHelper
aIt( *pContnr
);
918 while( aIt
.hasMoreElements() )
922 static_cast< css::frame::XStatusListener
*>(aIt
.next())->statusChanged( aEvent
);
924 catch (const css::uno::RuntimeException
&)
933 void SfxDispatchController_Impl::StateChanged( sal_uInt16 nSID
, SfxItemState eState
, const SfxPoolItem
* pState
)
935 StateChanged( nSID
, eState
, pState
, nullptr );
938 static void InterceptLOKStateChangeEvent(const SfxViewFrame
* pViewFrame
, const css::frame::FeatureStateEvent
& aEvent
)
940 if (!comphelper::LibreOfficeKit::isActive())
943 OUStringBuffer aBuffer
;
944 aBuffer
.append(aEvent
.FeatureURL
.Complete
);
945 aBuffer
.append(static_cast<sal_Unicode
>('='));
947 if (aEvent
.FeatureURL
.Path
== "Bold" ||
948 aEvent
.FeatureURL
.Path
== "CenterPara" ||
949 aEvent
.FeatureURL
.Path
== "CharBackgroundExt" ||
950 aEvent
.FeatureURL
.Path
== "DefaultBullet" ||
951 aEvent
.FeatureURL
.Path
== "DefaultNumbering" ||
952 aEvent
.FeatureURL
.Path
== "Italic" ||
953 aEvent
.FeatureURL
.Path
== "JustifyPara" ||
954 aEvent
.FeatureURL
.Path
== "LeftPara" ||
955 aEvent
.FeatureURL
.Path
== "OutlineFont" ||
956 aEvent
.FeatureURL
.Path
== "RightPara" ||
957 aEvent
.FeatureURL
.Path
== "Shadowed" ||
958 aEvent
.FeatureURL
.Path
== "SubScript" ||
959 aEvent
.FeatureURL
.Path
== "SuperScript" ||
960 aEvent
.FeatureURL
.Path
== "Strikeout" ||
961 aEvent
.FeatureURL
.Path
== "Underline" ||
962 aEvent
.FeatureURL
.Path
== "ModifiedStatus")
965 aEvent
.State
>>= bTemp
;
966 aBuffer
.append(bTemp
);
968 else if (aEvent
.FeatureURL
.Path
== "CharFontName")
970 css::awt::FontDescriptor aFontDesc
;
971 aEvent
.State
>>= aFontDesc
;
972 aBuffer
.append(aFontDesc
.Name
);
974 else if (aEvent
.FeatureURL
.Path
== "FontHeight")
976 css::frame::status::FontHeight aFontHeight
;
977 aEvent
.State
>>= aFontHeight
;
978 aBuffer
.append(aFontHeight
.Height
);
980 else if (aEvent
.FeatureURL
.Path
== "StyleApply")
982 css::frame::status::Template aTemplate
;
983 aEvent
.State
>>= aTemplate
;
984 aBuffer
.append(aTemplate
.StyleName
);
986 else if (aEvent
.FeatureURL
.Path
== "BackColor" ||
987 aEvent
.FeatureURL
.Path
== "BackgroundColor" ||
988 aEvent
.FeatureURL
.Path
== "CharBackColor" ||
989 aEvent
.FeatureURL
.Path
== "Color" ||
990 aEvent
.FeatureURL
.Path
== "FontColor")
992 sal_Int32 nColor
= -1;
993 aEvent
.State
>>= nColor
;
994 aBuffer
.append(nColor
);
996 else if (aEvent
.FeatureURL
.Path
== "Undo" ||
997 aEvent
.FeatureURL
.Path
== "Redo" ||
998 aEvent
.FeatureURL
.Path
== "Cut" ||
999 aEvent
.FeatureURL
.Path
== "Copy" ||
1000 aEvent
.FeatureURL
.Path
== "Paste" ||
1001 aEvent
.FeatureURL
.Path
== "SelectAll" ||
1002 aEvent
.FeatureURL
.Path
== "InsertAnnotation" ||
1003 aEvent
.FeatureURL
.Path
== "InsertRowsBefore" ||
1004 aEvent
.FeatureURL
.Path
== "InsertRowsAfter" ||
1005 aEvent
.FeatureURL
.Path
== "InsertColumnsBefore" ||
1006 aEvent
.FeatureURL
.Path
== "InsertColumnsAfter" ||
1007 aEvent
.FeatureURL
.Path
== "DeleteRows" ||
1008 aEvent
.FeatureURL
.Path
== "DeleteColumns" ||
1009 aEvent
.FeatureURL
.Path
== "DeleteTable" ||
1010 aEvent
.FeatureURL
.Path
== "SelectTable" ||
1011 aEvent
.FeatureURL
.Path
== "EntireRow" ||
1012 aEvent
.FeatureURL
.Path
== "EntireColumn" ||
1013 aEvent
.FeatureURL
.Path
== "EntireCell" ||
1014 aEvent
.FeatureURL
.Path
== "MergeCells")
1016 aBuffer
.append(aEvent
.IsEnabled
? OUString("enabled") : OUString("disabled"));
1018 else if (aEvent
.FeatureURL
.Path
== "InsertPage" ||
1019 aEvent
.FeatureURL
.Path
== "DeletePage" ||
1020 aEvent
.FeatureURL
.Path
== "DuplicatePage")
1022 aBuffer
.append(OUString::boolean(aEvent
.IsEnabled
));
1024 else if (aEvent
.FeatureURL
.Path
== "AssignLayout" ||
1025 aEvent
.FeatureURL
.Path
== "StatusSelectionMode" ||
1026 aEvent
.FeatureURL
.Path
== "Signature")
1030 if (aEvent
.IsEnabled
&& (aEvent
.State
>>= aInt32
))
1032 aBuffer
.append(OUString::number(aInt32
));
1035 else if (aEvent
.FeatureURL
.Path
== "StatusDocPos" ||
1036 aEvent
.FeatureURL
.Path
== "RowColSelCount" ||
1037 aEvent
.FeatureURL
.Path
== "StatusPageStyle" ||
1038 aEvent
.FeatureURL
.Path
== "StateTableCell")
1042 if (aEvent
.IsEnabled
&& (aEvent
.State
>>= aString
))
1044 aBuffer
.append(aString
);
1047 else if (aEvent
.FeatureURL
.Path
== "InsertMode")
1051 if (aEvent
.IsEnabled
&& (aEvent
.State
>>= aBool
))
1053 aBuffer
.append(OUString::boolean(aBool
));
1056 else if (aEvent
.FeatureURL
.Path
== "Position")
1058 css::awt::Point aPoint
;
1060 if (aEvent
.IsEnabled
&& (aEvent
.State
>>= aPoint
))
1062 aBuffer
.append(OUString::number(aPoint
.X
) + OUString(" / ") + OUString::number(aPoint
.Y
));
1065 else if (aEvent
.FeatureURL
.Path
== "StatusBarFunc" ||
1066 aEvent
.FeatureURL
.Path
== "Size")
1068 css::awt::Size aSize
;
1070 if (aEvent
.IsEnabled
&& (aEvent
.State
>>= aSize
))
1072 aBuffer
.append(OUString::number(aSize
.Width
) + OUString(" x ") + OUString::number(aSize
.Height
));
1080 OUString payload
= aBuffer
.makeStringAndClear();
1081 if (const SfxViewShell
* pViewShell
= pViewFrame
->GetViewShell())
1082 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
, payload
.toUtf8().getStr());
1085 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */