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_java.h>
22 #include <rtl/strbuf.hxx>
23 #include <tools/debug.hxx>
24 #include <svl/eitem.hxx>
25 #include <svl/intitem.hxx>
26 #include <svl/itemset.hxx>
27 #include <svl/visitem.hxx>
28 #include <svtools/javacontext.hxx>
29 #include <svtools/javainteractionhandler.hxx>
30 #include <svl/itempool.hxx>
31 #include <tools/urlobj.hxx>
32 #include <com/sun/star/awt/FontDescriptor.hpp>
33 #include <com/sun/star/awt/Point.hpp>
34 #include <com/sun/star/awt/Size.hpp>
35 #include <com/sun/star/util/URLTransformer.hpp>
36 #include <com/sun/star/util/XURLTransformer.hpp>
37 #include <com/sun/star/frame/Desktop.hpp>
38 #include <com/sun/star/frame/XFrame.hpp>
39 #include <com/sun/star/frame/status/FontHeight.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/status/Template.hpp>
43 #include <com/sun/star/frame/DispatchResultState.hpp>
44 #include <com/sun/star/frame/ModuleManager.hpp>
45 #include <com/sun/star/frame/status/Visibility.hpp>
46 #include <comphelper/processfactory.hxx>
47 #include <officecfg/Office/Common.hxx>
48 #include <uno/current_context.hxx>
49 #include <vcl/svapp.hxx>
50 #include <vcl/uitest/logger.hxx>
51 #include <boost/property_tree/json_parser.hpp>
52 #include <tools/json_writer.hxx>
54 #include <sfx2/app.hxx>
55 #include <unoctitm.hxx>
56 #include <sfx2/viewfrm.hxx>
57 #include <sfx2/frame.hxx>
58 #include <sfx2/ctrlitem.hxx>
59 #include <sfx2/sfxuno.hxx>
60 #include <sfx2/bindings.hxx>
61 #include <sfx2/dispatch.hxx>
62 #include <sfx2/sfxsids.hrc>
63 #include <sfx2/request.hxx>
64 #include <sfx2/msg.hxx>
65 #include <sfx2/viewsh.hxx>
66 #include <slotserv.hxx>
67 #include <osl/file.hxx>
68 #include <rtl/ustring.hxx>
69 #include <unotools/pathoptions.hxx>
71 #include <sfx2/lokhelper.hxx>
72 #include <basic/sberrors.hxx>
76 #include <string_view>
78 #include <sal/log.hxx>
79 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
80 #include <comphelper/lok.hxx>
81 #include <comphelper/servicehelper.hxx>
83 #include <desktop/crashreport.hxx>
84 #include <vcl/threadex.hxx>
85 #include <unotools/mediadescriptor.hxx>
86 #include <tools/json_writer.hxx>
88 using namespace ::com::sun::star
;
89 using namespace ::com::sun::star::uno
;
90 using namespace ::com::sun::star::util
;
109 const char* const URLTypeNames
[URLType_COUNT
] =
121 static void InterceptLOKStateChangeEvent( sal_uInt16 nSID
, SfxViewFrame
* pViewFrame
, const css::frame::FeatureStateEvent
& aEvent
, const SfxPoolItem
* pState
);
123 void SfxStatusDispatcher::ReleaseAll()
125 css::lang::EventObject aObject
;
126 aObject
.Source
= static_cast<cppu::OWeakObject
*>(this);
127 aListeners
.disposeAndClear( aObject
);
130 void SAL_CALL
SfxStatusDispatcher::dispatch( const css::util::URL
&, const css::uno::Sequence
< css::beans::PropertyValue
>& )
134 void SAL_CALL
SfxStatusDispatcher::dispatchWithNotification(
135 const css::util::URL
&,
136 const css::uno::Sequence
< css::beans::PropertyValue
>&,
137 const css::uno::Reference
< css::frame::XDispatchResultListener
>& )
141 SfxStatusDispatcher::SfxStatusDispatcher()
142 : aListeners( aMutex
)
146 void SAL_CALL
SfxStatusDispatcher::addStatusListener(const css::uno::Reference
< css::frame::XStatusListener
> & aListener
, const css::util::URL
& aURL
)
148 aListeners
.addInterface( aURL
.Complete
, aListener
);
149 if ( aURL
.Complete
== ".uno:LifeTime" )
151 css::frame::FeatureStateEvent aEvent
;
152 aEvent
.FeatureURL
= aURL
;
153 aEvent
.Source
= static_cast<css::frame::XDispatch
*>(this);
154 aEvent
.IsEnabled
= true;
155 aEvent
.Requery
= false;
156 aListener
->statusChanged( aEvent
);
160 void SAL_CALL
SfxStatusDispatcher::removeStatusListener( const css::uno::Reference
< css::frame::XStatusListener
> & aListener
, const css::util::URL
& aURL
)
162 aListeners
.removeInterface( aURL
.Complete
, aListener
);
167 sal_Int64 SAL_CALL
SfxOfficeDispatch::getSomething( const css::uno::Sequence
< sal_Int8
>& aIdentifier
)
169 return comphelper::getSomethingImpl(aIdentifier
, this);
172 SfxOfficeDispatch::SfxOfficeDispatch( SfxBindings
& rBindings
, SfxDispatcher
* pDispat
, const SfxSlot
* pSlot
, const css::util::URL
& rURL
)
173 : pImpl( new SfxDispatchController_Impl( this, &rBindings
, 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
179 SfxOfficeDispatch::SfxOfficeDispatch( SfxDispatcher
* pDispat
, const SfxSlot
* pSlot
, const css::util::URL
& rURL
)
180 : pImpl( new SfxDispatchController_Impl( this, nullptr, pDispat
, pSlot
, rURL
))
182 // pImpl is an adapter that shows a css::frame::XDispatch-Interface to the outside and uses a SfxControllerItem to monitor a state
185 SfxOfficeDispatch::~SfxOfficeDispatch()
189 // when dispatch object is released, destroy its connection to this object and destroy it
190 pImpl
->UnBindController();
192 // Ensure that SfxDispatchController_Impl is deleted while the solar mutex is locked, since
193 // that derives from SfxListener.
194 SolarMutexGuard aGuard
;
199 const css::uno::Sequence
< sal_Int8
>& SfxOfficeDispatch::getUnoTunnelId()
201 // {38 57 CA 80 09 36 11 d4 83 FE 00 50 04 52 6B 21}
202 static const sal_uInt8 pGUID
[16] = { 0x38, 0x57, 0xCA, 0x80, 0x09, 0x36, 0x11, 0xd4, 0x83, 0xFE, 0x00, 0x50, 0x04, 0x52, 0x6B, 0x21 };
203 static css::uno::Sequence
< sal_Int8
> seqID(reinterpret_cast<const sal_Int8
*>(pGUID
), 16) ;
207 #if HAVE_FEATURE_JAVA
208 // The JavaContext contains an interaction handler which is used when
209 // the creation of a Java Virtual Machine fails. There shall only be one
210 // user notification (message box) even if the same error (interaction)
211 // reoccurs. The effect is, that if a user selects a menu entry than they
212 // may get only one notification that a JRE is not selected.
213 // This function checks if a JavaContext is already available (typically
214 // created by Desktop::Main() in app.cxx), and creates new one if not.
216 std::unique_ptr
< css::uno::ContextLayer
> EnsureJavaContext()
218 css::uno::Reference
< css::uno::XCurrentContext
> xContext(css::uno::getCurrentContext());
221 css::uno::Reference
< css::task::XInteractionHandler
> xHandler
;
222 xContext
->getValueByName(JAVA_INTERACTION_HANDLER_NAME
) >>= xHandler
;
224 return nullptr; // No need to add new layer: JavaContext already present
226 return std::make_unique
< css::uno::ContextLayer
>(new svt::JavaContext(xContext
));
231 void SAL_CALL
SfxOfficeDispatch::dispatch( const css::util::URL
& aURL
, const css::uno::Sequence
< css::beans::PropertyValue
>& aArgs
)
233 // ControllerItem is the Impl class
236 #if HAVE_FEATURE_JAVA
237 std::unique_ptr
< css::uno::ContextLayer
> layer(EnsureJavaContext());
239 utl::MediaDescriptor
aDescriptor(aArgs
);
240 bool bOnMainThread
= aDescriptor
.getUnpackedValueOrDefault("OnMainThread", false);
243 // Make sure that we own the solar mutex, otherwise later
244 // vcl::SolarThreadExecutor::execute() will release the solar mutex, even if it's owned by
245 // an other thread, leading to an std::abort() at the end.
246 SolarMutexGuard aGuard
;
247 vcl::solarthread::syncExecute([this, &aURL
, &aArgs
]() {
248 pImpl
->dispatch(aURL
, aArgs
,
249 css::uno::Reference
<css::frame::XDispatchResultListener
>());
254 pImpl
->dispatch(aURL
, aArgs
,
255 css::uno::Reference
<css::frame::XDispatchResultListener
>());
260 void SAL_CALL
SfxOfficeDispatch::dispatchWithNotification( const css::util::URL
& aURL
,
261 const css::uno::Sequence
< css::beans::PropertyValue
>& aArgs
,
262 const css::uno::Reference
< css::frame::XDispatchResultListener
>& rListener
)
264 // ControllerItem is the Impl class
267 #if HAVE_FEATURE_JAVA
268 std::unique_ptr
< css::uno::ContextLayer
> layer(EnsureJavaContext());
270 pImpl
->dispatch( aURL
, aArgs
, rListener
);
274 void SAL_CALL
SfxOfficeDispatch::addStatusListener(const css::uno::Reference
< css::frame::XStatusListener
> & aListener
, const css::util::URL
& aURL
)
276 GetListeners().addInterface( aURL
.Complete
, aListener
);
279 // ControllerItem is the Impl class
280 pImpl
->addStatusListener( aListener
, aURL
);
284 SfxDispatcher
* SfxOfficeDispatch::GetDispatcher_Impl()
286 return pImpl
->GetDispatcher();
289 void SfxOfficeDispatch::SetFrame(const css::uno::Reference
< css::frame::XFrame
>& xFrame
)
292 pImpl
->SetFrame( xFrame
);
295 void SfxOfficeDispatch::SetMasterUnoCommand( bool bSet
)
298 pImpl
->setMasterSlaveCommand( bSet
);
301 // Determine if URL contains a master/slave command which must be handled a little bit different
302 bool SfxOfficeDispatch::IsMasterUnoCommand( const css::util::URL
& aURL
)
304 return aURL
.Protocol
== ".uno:" && ( aURL
.Path
.indexOf( '.' ) > 0 );
307 OUString
SfxOfficeDispatch::GetMasterUnoCommand( const css::util::URL
& aURL
)
309 OUString aMasterCommand
;
310 if ( IsMasterUnoCommand( aURL
))
312 sal_Int32 nIndex
= aURL
.Path
.indexOf( '.' );
314 aMasterCommand
= aURL
.Path
.copy( 0, nIndex
);
317 return aMasterCommand
;
320 SfxDispatchController_Impl::SfxDispatchController_Impl(
321 SfxOfficeDispatch
* pDisp
,
323 SfxDispatcher
* pDispat
,
324 const SfxSlot
* pSlot
,
325 const css::util::URL
& rURL
)
326 : aDispatchURL( rURL
)
327 , pDispatcher( pDispat
)
329 , pLastState( nullptr )
331 , bMasterSlave( false )
334 if ( aDispatchURL
.Protocol
== "slot:" && pSlot
->pUnoName
)
336 aDispatchURL
.Complete
= ".uno:" + OUString::createFromAscii(pSlot
->pUnoName
);
337 Reference
< XURLTransformer
> xTrans( URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
338 xTrans
->parseStrict( aDispatchURL
);
341 sal_uInt16 nSlot
= pSlot
->GetSlotId();
345 // Bind immediately to enable the cache to recycle dispatches when asked for the same command
346 // a command in "slot" or in ".uno" notation must be treated as identical commands!
347 pBindings
->ENTERREGISTRATIONS();
348 BindInternal_Impl( nSlot
, pBindings
);
349 pBindings
->LEAVEREGISTRATIONS();
352 assert(SfxApplication::Get()->GetAppDispatcher_Impl() == pDispatcher
353 || pDispatcher
->GetFrame() != nullptr);
354 if (pDispatcher
->GetFrame())
356 StartListening(*pDispatcher
->GetFrame());
360 StartListening(*SfxApplication::Get());
364 void SfxDispatchController_Impl::Notify(SfxBroadcaster
& rBC
, SfxHint
const& rHint
)
366 if (rHint
.GetId() == SfxHintId::Dying
)
367 { // both pBindings and pDispatcher are dead if SfxViewFrame is dead
369 pDispatcher
= nullptr;
374 SfxDispatchController_Impl::~SfxDispatchController_Impl()
376 if ( pLastState
&& !IsInvalidItem( pLastState
) )
382 pDispatch
->pImpl
= nullptr;
384 // force all listeners to release the dispatch object
385 css::lang::EventObject aObject
;
386 aObject
.Source
= static_cast<cppu::OWeakObject
*>(pDispatch
);
387 pDispatch
->GetListeners().disposeAndClear( aObject
);
391 void SfxDispatchController_Impl::SetFrame(const css::uno::Reference
< css::frame::XFrame
>& _xFrame
)
396 void SfxDispatchController_Impl::setMasterSlaveCommand( bool bSet
)
401 void SfxDispatchController_Impl::UnBindController()
406 GetBindings().ENTERREGISTRATIONS();
407 SfxControllerItem::UnBind();
408 GetBindings().LEAVEREGISTRATIONS();
412 void SfxDispatchController_Impl::addParametersToArgs( const css::util::URL
& aURL
, css::uno::Sequence
< css::beans::PropertyValue
>& rArgs
)
414 // Extract the parameter from the URL and put them into the property value sequence
415 sal_Int32 nQueryIndex
= aURL
.Complete
.indexOf( '?' );
416 if ( nQueryIndex
<= 0 )
419 OUString
aParamString( aURL
.Complete
.copy( nQueryIndex
+1 ));
420 sal_Int32 nIndex
= 0;
423 OUString aToken
= aParamString
.getToken( 0, '&', nIndex
);
425 sal_Int32 nParmIndex
= 0;
427 OUString aParamName
= aToken
.getToken( 0, '=', nParmIndex
);
428 OUString aValue
= aToken
.getToken( 0, '=', nParmIndex
);
430 if ( !aParamName
.isEmpty() )
434 aParamName
= aToken
.getToken( 0, ':', nParmIndex
);
435 aParamType
= aToken
.getToken( 0, ':', nParmIndex
);
438 sal_Int32 nLen
= rArgs
.getLength();
439 rArgs
.realloc( nLen
+1 );
440 auto pArgs
= rArgs
.getArray();
441 pArgs
[nLen
].Name
= aParamName
;
443 if ( aParamType
.isEmpty() )
446 pArgs
[nLen
].Value
<<= aValue
.toInt32();
448 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_BOOL
], 4 ))
451 pArgs
[nLen
].Value
<<= aValue
.toBoolean();
453 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_BYTE
], 4 ))
456 pArgs
[nLen
].Value
<<= sal_Int8( aValue
.toInt32() );
458 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_LONG
], 4 ))
461 pArgs
[nLen
].Value
<<= aValue
.toInt32();
463 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_SHORT
], 5 ))
466 pArgs
[nLen
].Value
<<= sal_Int16( aValue
.toInt32() );
468 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_HYPER
], 5 ))
471 pArgs
[nLen
].Value
<<= aValue
.toInt64();
473 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_FLOAT
], 5 ))
476 pArgs
[nLen
].Value
<<= aValue
.toFloat();
478 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_STRING
], 6 ))
481 pArgs
[nLen
].Value
<<= INetURLObject::decode( aValue
, INetURLObject::DecodeMechanism::WithCharset
);
483 else if ( aParamType
.equalsAsciiL( URLTypeNames
[URLType_DOUBLE
], 6))
486 pArgs
[nLen
].Value
<<= aValue
.toDouble();
489 while ( nIndex
>= 0 );
492 MapUnit
SfxDispatchController_Impl::GetCoreMetric( SfxItemPool
const & rPool
, sal_uInt16 nSlotId
)
494 sal_uInt16 nWhich
= rPool
.GetWhich( nSlotId
);
495 return rPool
.GetMetric( nWhich
);
498 OUString
SfxDispatchController_Impl::getSlaveCommand( const css::util::URL
& rURL
)
500 OUString aSlaveCommand
;
501 sal_Int32 nIndex
= rURL
.Path
.indexOf( '.' );
502 if (( nIndex
> 0 ) && ( nIndex
< rURL
.Path
.getLength() ))
503 aSlaveCommand
= rURL
.Path
.copy( nIndex
+1 );
504 return aSlaveCommand
;
509 void collectUIInformation(const util::URL
& rURL
, const css::uno::Sequence
< css::beans::PropertyValue
>& rArgs
)
511 static const char* pFile
= std::getenv("LO_COLLECT_UIINFO");
515 UITestLogger::getInstance().logCommand(
516 OUStringConcatenation("Send UNO Command (\"" + rURL
.Complete
+ "\") "), rArgs
);
521 void SfxDispatchController_Impl::dispatch( const css::util::URL
& aURL
,
522 const css::uno::Sequence
< css::beans::PropertyValue
>& aArgs
,
523 const css::uno::Reference
< css::frame::XDispatchResultListener
>& rListener
)
525 if ( aURL
.Protocol
== ".uno:")
527 CrashReporter::logUnoCommand(aURL
.Path
);
529 collectUIInformation(aURL
, aArgs
);
531 SolarMutexGuard aGuard
;
533 if (comphelper::LibreOfficeKit::isActive() &&
534 SfxViewShell::Current()->isBlockedCommand(aURL
.Complete
))
536 tools::JsonWriter aTree
;
537 aTree
.put("code", "");
538 aTree
.put("kind", "BlockedCommand");
539 aTree
.put("cmd", aURL
.Complete
);
540 aTree
.put("message", "Blocked feature");
541 aTree
.put("viewID", SfxViewShell::Current()->GetViewShellId().get());
543 SfxViewShell::Current()->libreOfficeKitViewCallback(LOK_COMMAND_BLOCKED
, aTree
.extractData());
550 (aURL
.Protocol
== ".uno:" && aURL
.Path
== aDispatchURL
.Path
) ||
551 (aURL
.Protocol
== "slot:" && aURL
.Path
.toInt32() == GetId())
556 if ( !pDispatcher
&& pBindings
)
557 pDispatcher
= GetBindings().GetDispatcher_Impl();
559 css::uno::Sequence
< css::beans::PropertyValue
> lNewArgs
;
560 sal_Int32 nCount
= aArgs
.getLength();
562 // Support for URL based arguments
563 INetURLObject
aURLObj( aURL
.Complete
);
564 if ( aURLObj
.HasParam() )
565 addParametersToArgs( aURL
, lNewArgs
);
567 // Try to find call mode and frame name inside given arguments...
568 SfxCallMode nCall
= SfxCallMode::RECORD
;
569 sal_Int32 nMarkArg
= -1;
571 // Filter arguments which shouldn't be part of the sequence property value
572 sal_uInt16
nModifier(0);
573 std::vector
< css::beans::PropertyValue
> aAddArgs
;
574 for( sal_Int32 n
=0; n
<nCount
; n
++ )
576 const css::beans::PropertyValue
& rProp
= aArgs
[n
];
577 if( rProp
.Name
== "SynchronMode" )
580 if( rProp
.Value
>>= bTemp
)
581 nCall
= bTemp
? SfxCallMode::SYNCHRON
: SfxCallMode::ASYNCHRON
;
583 else if( rProp
.Name
== "Bookmark" )
586 aAddArgs
.push_back( aArgs
[n
] );
588 else if( rProp
.Name
== "KeyModifier" )
589 rProp
.Value
>>= nModifier
;
591 aAddArgs
.push_back( aArgs
[n
] );
594 // Add needed arguments to sequence property value
595 sal_uInt32 nAddArgs
= aAddArgs
.size();
598 sal_uInt32
nIndex( lNewArgs
.getLength() );
600 lNewArgs
.realloc( nIndex
+ nAddArgs
);
601 std::copy(aAddArgs
.begin(), aAddArgs
.end(), std::next(lNewArgs
.getArray(), nIndex
));
604 // Overwrite possible detected synchron argument, if real listener exists (currently no other way)
605 if ( rListener
.is() )
606 nCall
= SfxCallMode::SYNCHRON
;
608 if( GetId() == SID_JUMPTOMARK
&& nMarkArg
== - 1 )
610 // we offer dispatches for SID_JUMPTOMARK if the URL points to a bookmark inside the document
611 // so we must retrieve this as an argument from the parsed URL
612 lNewArgs
.realloc( lNewArgs
.getLength()+1 );
613 auto& el
= lNewArgs
.getArray()[lNewArgs
.getLength()-1];
614 el
.Name
= "Bookmark";
615 el
.Value
<<= aURL
.Mark
;
618 css::uno::Reference
< css::frame::XFrame
> xFrameRef(xFrame
.get(), css::uno::UNO_QUERY
);
619 if (! xFrameRef
.is() && pDispatcher
)
621 SfxViewFrame
* pViewFrame
= pDispatcher
->GetFrame();
623 xFrameRef
= pViewFrame
->GetFrame().GetFrameInterface();
626 bool bSuccess
= false;
627 const SfxPoolItem
* pItem
= nullptr;
628 MapUnit
eMapUnit( MapUnit::Map100thMM
);
630 // Extra scope so that aInternalSet is destroyed before
631 // rListener->dispatchFinished potentially calls
632 // framework::Desktop::terminate -> SfxApplication::Deinitialize ->
636 SfxAllItemSet
aInternalSet( SfxGetpApp()->GetPool() );
637 if (xFrameRef
.is()) // an empty set is no problem ... but an empty frame reference can be a problem !
638 aInternalSet
.Put( SfxUnoFrameItem( SID_FILLFRAME
, xFrameRef
) );
640 SfxShell
* pShell( nullptr );
641 // #i102619# Retrieve metric from shell before execution - the shell could be destroyed after execution
642 if ( pDispatcher
->GetBindings() )
644 if ( !pDispatcher
->IsLocked() )
646 const SfxSlot
*pSlot
= nullptr;
647 if ( pDispatcher
->GetShellAndSlot_Impl( GetId(), &pShell
, &pSlot
, false, false ) )
651 // Extract slave command and add argument to the args list. Master slot MUST
652 // have an argument that has the same name as the master slot and type is SfxStringItem.
653 sal_Int32 nIndex
= lNewArgs
.getLength();
654 lNewArgs
.realloc( nIndex
+1 );
655 auto plNewArgs
= lNewArgs
.getArray();
656 plNewArgs
[nIndex
].Name
= OUString::createFromAscii( pSlot
->pUnoName
);
657 plNewArgs
[nIndex
].Value
<<= SfxDispatchController_Impl::getSlaveCommand( aDispatchURL
);
660 eMapUnit
= GetCoreMetric( pShell
->GetPool(), GetId() );
661 std::optional
<SfxAllItemSet
> xSet(pShell
->GetPool());
662 TransformParameters(GetId(), lNewArgs
, *xSet
, pSlot
);
665 // execute with arguments - call directly
666 pItem
= pDispatcher
->Execute(GetId(), nCall
, &*xSet
, &aInternalSet
, nModifier
);
667 if ( pItem
!= nullptr )
669 if (const SfxBoolItem
* pBoolItem
= dynamic_cast<const SfxBoolItem
*>(pItem
))
670 bSuccess
= pBoolItem
->GetValue();
671 else if ( !pItem
->IsVoidItem() )
672 bSuccess
= true; // all other types are true
674 // else bSuccess = false look to line 664 it is false
678 // Be sure to delete this before we send a dispatch
679 // request, which will destroy the current shell.
682 // execute using bindings, enables support for toggle/enum etc.
683 SfxRequest
aReq( GetId(), nCall
, pShell
->GetPool() );
684 aReq
.SetModifier( nModifier
);
685 aReq
.SetInternalArgs_Impl(aInternalSet
);
686 pDispatcher
->GetBindings()->Execute_Impl( aReq
, pSlot
, pShell
);
687 pItem
= aReq
.GetReturnValue();
688 bSuccess
= aReq
.IsDone() || pItem
!= nullptr;
692 SAL_INFO("sfx.control", "MacroPlayer: Unknown slot dispatched!");
697 eMapUnit
= GetCoreMetric( SfxGetpApp()->GetPool(), GetId() );
699 SfxAllItemSet
aSet( SfxGetpApp()->GetPool() );
700 TransformParameters( GetId(), lNewArgs
, aSet
);
703 pItem
= pDispatcher
->Execute(GetId(), nCall
, &aSet
, &aInternalSet
, nModifier
);
705 // SfxRequests take empty sets as argument sets, GetArgs() returning non-zero!
706 pItem
= pDispatcher
->Execute(GetId(), nCall
, nullptr, &aInternalSet
, nModifier
);
708 // no bindings, no invalidate ( usually done in SfxDispatcher::Call_Impl()! )
709 if (SfxApplication
* pApp
= SfxApplication::Get())
711 SfxDispatcher
* pAppDispat
= pApp
->GetAppDispatcher_Impl();
714 const SfxPoolItem
* pState
=nullptr;
715 SfxItemState eState
= pDispatcher
->QueryState( GetId(), pState
);
716 StateChangedAtToolBoxControl( GetId(), eState
, pState
);
720 bSuccess
= (pItem
!= nullptr);
724 if ( !rListener
.is() )
727 css::frame::DispatchResultEvent aEvent
;
729 aEvent
.State
= css::frame::DispatchResultState::SUCCESS
;
731 aEvent
.State
= css::frame::DispatchResultState::FAILURE
;
733 aEvent
.Source
= static_cast<css::frame::XDispatch
*>(pDispatch
);
734 if ( bSuccess
&& pItem
&& !pItem
->IsVoidItem() )
736 sal_uInt16
nSubId( 0 );
737 if ( eMapUnit
== MapUnit::MapTwip
)
738 nSubId
|= CONVERT_TWIPS
;
739 pItem
->QueryValue( aEvent
.Result
, static_cast<sal_uInt8
>(nSubId
) );
742 rListener
->dispatchFinished( aEvent
);
745 SfxDispatcher
* SfxDispatchController_Impl::GetDispatcher()
747 if ( !pDispatcher
&& pBindings
)
748 pDispatcher
= GetBindings().GetDispatcher_Impl();
752 void SfxDispatchController_Impl::addStatusListener(const css::uno::Reference
< css::frame::XStatusListener
> & aListener
, const css::util::URL
& aURL
)
754 SolarMutexGuard aGuard
;
758 // Use alternative QueryState call to have a valid UNO representation of the state.
759 css::uno::Any aState
;
760 if ( !pDispatcher
&& pBindings
)
761 pDispatcher
= GetBindings().GetDispatcher_Impl();
762 SfxItemState eState
= pDispatcher
? pDispatcher
->QueryState( GetId(), aState
) : SfxItemState::DONTCARE
;
764 if ( eState
== SfxItemState::DONTCARE
)
766 // Use special uno struct to transport don't care state
767 css::frame::status::ItemStatus aItemStatus
;
768 aItemStatus
.State
= css::frame::status::ItemState::DONT_CARE
;
769 aState
<<= aItemStatus
;
772 css::frame::FeatureStateEvent aEvent
;
773 aEvent
.FeatureURL
= aURL
;
774 aEvent
.Source
= static_cast<css::frame::XDispatch
*>(pDispatch
);
775 aEvent
.Requery
= false;
778 aEvent
.IsEnabled
= eState
!= SfxItemState::DISABLED
;
779 aEvent
.State
= aState
;
783 css::frame::status::Visibility aVisibilityStatus
;
784 aVisibilityStatus
.bVisible
= false;
786 // MBA: we might decide to *not* disable "invisible" slots, but this would be
787 // a change that needs to adjust at least the testtool
788 aEvent
.IsEnabled
= false;
789 aEvent
.State
<<= aVisibilityStatus
;
792 aListener
->statusChanged( aEvent
);
795 void SfxDispatchController_Impl::sendStatusChanged(const OUString
& rURL
, const css::frame::FeatureStateEvent
& rEvent
)
797 ::comphelper::OInterfaceContainerHelper2
* pContnr
= pDispatch
->GetListeners().getContainer(rURL
);
800 ::comphelper::OInterfaceIteratorHelper2
aIt(*pContnr
);
801 while (aIt
.hasMoreElements())
805 static_cast<css::frame::XStatusListener
*>(aIt
.next())->statusChanged(rEvent
);
807 catch (const css::uno::RuntimeException
&)
814 void SfxDispatchController_Impl::StateChanged( sal_uInt16 nSID
, SfxItemState eState
, const SfxPoolItem
* pState
, SfxSlotServer
const * pSlotServ
)
819 // Bindings instance notifies controller about a state change, listeners must be notified also
820 // Don't cache visibility state changes as they are volatile. We need our real state to send it
821 // to our controllers after visibility is set to true.
823 if ( pState
&& !IsInvalidItem( pState
) )
825 if ( auto pVisibilityItem
= dynamic_cast< const SfxVisibilityItem
*>( pState
) )
826 bVisible
= pVisibilityItem
->GetValue();
829 if (pLastState
&& !IsInvalidItem(pLastState
))
831 bNotify
= typeid(*pState
) != typeid(*pLastState
) || *pState
!= *pLastState
;
834 pLastState
= !IsInvalidItem(pState
) ? pState
->Clone() : pState
;
840 if ( pLastState
&& !IsInvalidItem( pLastState
) )
848 css::uno::Any aState
;
849 if ( ( eState
>= SfxItemState::DEFAULT
) && pState
&& !IsInvalidItem( pState
) && !pState
->IsVoidItem() )
851 // Retrieve metric from pool to have correct sub ID when calling QueryValue
852 sal_uInt16
nSubId( 0 );
853 MapUnit
eMapUnit( MapUnit::Map100thMM
);
855 // retrieve the core metric
856 // it's enough to check the objectshell, the only shell that does not use the pool of the document
857 // is SfxViewFrame, but it hasn't any metric parameters
858 // TODO/LATER: what about the FormShell? Does it use any metric data?! Perhaps it should use the Pool of the document!
859 if ( pSlotServ
&& pDispatcher
)
861 SfxShell
* pShell
= pDispatcher
->GetShell( pSlotServ
->GetShellLevel() );
862 DBG_ASSERT( pShell
, "Can't get core metric without shell!" );
864 eMapUnit
= GetCoreMetric( pShell
->GetPool(), nSID
);
867 if ( eMapUnit
== MapUnit::MapTwip
)
868 nSubId
|= CONVERT_TWIPS
;
870 pState
->QueryValue( aState
, static_cast<sal_uInt8
>(nSubId
) );
872 else if ( eState
== SfxItemState::DONTCARE
)
874 // Use special uno struct to transport don't care state
875 css::frame::status::ItemStatus aItemStatus
;
876 aItemStatus
.State
= css::frame::status::ItemState::DONT_CARE
;
877 aState
<<= aItemStatus
;
880 css::frame::FeatureStateEvent aEvent
;
881 aEvent
.FeatureURL
= aDispatchURL
;
882 aEvent
.Source
= static_cast<css::frame::XDispatch
*>(pDispatch
);
883 aEvent
.IsEnabled
= eState
!= SfxItemState::DISABLED
;
884 aEvent
.Requery
= false;
885 aEvent
.State
= aState
;
887 if (pDispatcher
&& pDispatcher
->GetFrame())
889 InterceptLOKStateChangeEvent(nSID
, pDispatcher
->GetFrame(), aEvent
, pState
);
892 const std::vector
<OUString
> aContainedTypes
= pDispatch
->GetListeners().getContainedTypes();
893 for (const OUString
& rName
: aContainedTypes
)
895 if (rName
== aDispatchURL
.Main
|| rName
== aDispatchURL
.Complete
)
896 sendStatusChanged(rName
, aEvent
);
900 void SfxDispatchController_Impl::StateChangedAtToolBoxControl( sal_uInt16 nSID
, SfxItemState eState
, const SfxPoolItem
* pState
)
902 StateChanged( nSID
, eState
, pState
, nullptr );
905 static void InterceptLOKStateChangeEvent(sal_uInt16 nSID
, SfxViewFrame
* pViewFrame
, const css::frame::FeatureStateEvent
& aEvent
, const SfxPoolItem
* pState
)
907 if (!comphelper::LibreOfficeKit::isActive())
910 OUStringBuffer aBuffer
;
911 aBuffer
.append(aEvent
.FeatureURL
.Complete
);
912 aBuffer
.append(u
'=');
914 if (aEvent
.FeatureURL
.Path
== "Bold" ||
915 aEvent
.FeatureURL
.Path
== "CenterPara" ||
916 aEvent
.FeatureURL
.Path
== "CharBackgroundExt" ||
917 aEvent
.FeatureURL
.Path
== "ControlCodes" ||
918 aEvent
.FeatureURL
.Path
== "DefaultBullet" ||
919 aEvent
.FeatureURL
.Path
== "DefaultNumbering" ||
920 aEvent
.FeatureURL
.Path
== "Italic" ||
921 aEvent
.FeatureURL
.Path
== "JustifyPara" ||
922 aEvent
.FeatureURL
.Path
== "LeftPara" ||
923 aEvent
.FeatureURL
.Path
== "OutlineFont" ||
924 aEvent
.FeatureURL
.Path
== "RightPara" ||
925 aEvent
.FeatureURL
.Path
== "Shadowed" ||
926 aEvent
.FeatureURL
.Path
== "SpellOnline" ||
927 aEvent
.FeatureURL
.Path
== "OnlineAutoFormat" ||
928 aEvent
.FeatureURL
.Path
== "SubScript" ||
929 aEvent
.FeatureURL
.Path
== "SuperScript" ||
930 aEvent
.FeatureURL
.Path
== "Strikeout" ||
931 aEvent
.FeatureURL
.Path
== "Underline" ||
932 aEvent
.FeatureURL
.Path
== "ModifiedStatus" ||
933 aEvent
.FeatureURL
.Path
== "TrackChanges" ||
934 aEvent
.FeatureURL
.Path
== "ShowTrackedChanges" ||
935 aEvent
.FeatureURL
.Path
== "NextTrackedChange" ||
936 aEvent
.FeatureURL
.Path
== "PreviousTrackedChange" ||
937 aEvent
.FeatureURL
.Path
== "AlignLeft" ||
938 aEvent
.FeatureURL
.Path
== "AlignHorizontalCenter" ||
939 aEvent
.FeatureURL
.Path
== "AlignRight" ||
940 aEvent
.FeatureURL
.Path
== "DocumentRepair" ||
941 aEvent
.FeatureURL
.Path
== "ObjectAlignLeft" ||
942 aEvent
.FeatureURL
.Path
== "ObjectAlignRight" ||
943 aEvent
.FeatureURL
.Path
== "AlignCenter" ||
944 aEvent
.FeatureURL
.Path
== "AlignUp" ||
945 aEvent
.FeatureURL
.Path
== "AlignMiddle" ||
946 aEvent
.FeatureURL
.Path
== "AlignDown" ||
947 aEvent
.FeatureURL
.Path
== "TraceChangeMode" ||
948 aEvent
.FeatureURL
.Path
== "FormatPaintbrush" ||
949 aEvent
.FeatureURL
.Path
== "FreezePanes" ||
950 aEvent
.FeatureURL
.Path
== "Sidebar" ||
951 aEvent
.FeatureURL
.Path
== "SheetRightToLeft" ||
952 aEvent
.FeatureURL
.Path
== "SpacePara1" ||
953 aEvent
.FeatureURL
.Path
== "SpacePara15" ||
954 aEvent
.FeatureURL
.Path
== "SpacePara2")
957 aEvent
.State
>>= bTemp
;
958 aBuffer
.append(bTemp
);
960 else if (aEvent
.FeatureURL
.Path
== "CharFontName")
962 css::awt::FontDescriptor aFontDesc
;
963 aEvent
.State
>>= aFontDesc
;
964 aBuffer
.append(aFontDesc
.Name
);
966 else if (aEvent
.FeatureURL
.Path
== "FontHeight")
968 css::frame::status::FontHeight aFontHeight
;
969 aEvent
.State
>>= aFontHeight
;
970 aBuffer
.append(aFontHeight
.Height
);
972 else if (aEvent
.FeatureURL
.Path
== "StyleApply")
974 css::frame::status::Template aTemplate
;
975 aEvent
.State
>>= aTemplate
;
976 aBuffer
.append(aTemplate
.StyleName
);
978 else if (aEvent
.FeatureURL
.Path
== "BackColor" ||
979 aEvent
.FeatureURL
.Path
== "BackgroundColor" ||
980 aEvent
.FeatureURL
.Path
== "CharBackColor" ||
981 aEvent
.FeatureURL
.Path
== "Color" ||
982 aEvent
.FeatureURL
.Path
== "FontColor" ||
983 aEvent
.FeatureURL
.Path
== "FrameLineColor" ||
984 aEvent
.FeatureURL
.Path
== "GlowColor")
986 sal_Int32 nColor
= -1;
987 aEvent
.State
>>= nColor
;
988 aBuffer
.append(nColor
);
990 else if (aEvent
.FeatureURL
.Path
== "Undo" ||
991 aEvent
.FeatureURL
.Path
== "Redo")
993 const SfxUInt32Item
* pUndoConflict
= dynamic_cast< const SfxUInt32Item
* >( pState
);
994 if ( pUndoConflict
&& pUndoConflict
->GetValue() > 0 )
996 aBuffer
.append("disabled");
1000 aBuffer
.append(aEvent
.IsEnabled
? std::u16string_view(u
"enabled") : std::u16string_view(u
"disabled"));
1003 else if (aEvent
.FeatureURL
.Path
== "Cut" ||
1004 aEvent
.FeatureURL
.Path
== "Copy" ||
1005 aEvent
.FeatureURL
.Path
== "Paste" ||
1006 aEvent
.FeatureURL
.Path
== "SelectAll" ||
1007 aEvent
.FeatureURL
.Path
== "InsertAnnotation" ||
1008 aEvent
.FeatureURL
.Path
== "DeleteAnnotation" ||
1009 aEvent
.FeatureURL
.Path
== "ResolveAnnotation" ||
1010 aEvent
.FeatureURL
.Path
== "ResolveAnnotationThread" ||
1011 aEvent
.FeatureURL
.Path
== "InsertRowsBefore" ||
1012 aEvent
.FeatureURL
.Path
== "InsertRowsAfter" ||
1013 aEvent
.FeatureURL
.Path
== "InsertColumnsBefore" ||
1014 aEvent
.FeatureURL
.Path
== "InsertColumnsAfter" ||
1015 aEvent
.FeatureURL
.Path
== "MergeCells" ||
1016 aEvent
.FeatureURL
.Path
== "InsertObjectChart" ||
1017 aEvent
.FeatureURL
.Path
== "InsertSection" ||
1018 aEvent
.FeatureURL
.Path
== "InsertAnnotation" ||
1019 aEvent
.FeatureURL
.Path
== "InsertPagebreak" ||
1020 aEvent
.FeatureURL
.Path
== "InsertColumnBreak" ||
1021 aEvent
.FeatureURL
.Path
== "HyperlinkDialog" ||
1022 aEvent
.FeatureURL
.Path
== "InsertSymbol" ||
1023 aEvent
.FeatureURL
.Path
== "InsertPage" ||
1024 aEvent
.FeatureURL
.Path
== "DeletePage" ||
1025 aEvent
.FeatureURL
.Path
== "DuplicatePage" ||
1026 aEvent
.FeatureURL
.Path
== "DeleteRows" ||
1027 aEvent
.FeatureURL
.Path
== "DeleteColumns" ||
1028 aEvent
.FeatureURL
.Path
== "DeleteTable" ||
1029 aEvent
.FeatureURL
.Path
== "SelectTable" ||
1030 aEvent
.FeatureURL
.Path
== "EntireRow" ||
1031 aEvent
.FeatureURL
.Path
== "EntireColumn" ||
1032 aEvent
.FeatureURL
.Path
== "EntireCell" ||
1033 aEvent
.FeatureURL
.Path
== "SortAscending" ||
1034 aEvent
.FeatureURL
.Path
== "SortDescending" ||
1035 aEvent
.FeatureURL
.Path
== "AcceptAllTrackedChanges" ||
1036 aEvent
.FeatureURL
.Path
== "RejectAllTrackedChanges" ||
1037 aEvent
.FeatureURL
.Path
== "AcceptTrackedChange" ||
1038 aEvent
.FeatureURL
.Path
== "RejectTrackedChange" ||
1039 aEvent
.FeatureURL
.Path
== "NextTrackedChange" ||
1040 aEvent
.FeatureURL
.Path
== "PreviousTrackedChange" ||
1041 aEvent
.FeatureURL
.Path
== "FormatGroup" ||
1042 aEvent
.FeatureURL
.Path
== "ObjectBackOne" ||
1043 aEvent
.FeatureURL
.Path
== "SendToBack" ||
1044 aEvent
.FeatureURL
.Path
== "ObjectForwardOne" ||
1045 aEvent
.FeatureURL
.Path
== "BringToFront" ||
1046 aEvent
.FeatureURL
.Path
== "WrapRight" ||
1047 aEvent
.FeatureURL
.Path
== "WrapThrough" ||
1048 aEvent
.FeatureURL
.Path
== "WrapLeft" ||
1049 aEvent
.FeatureURL
.Path
== "WrapIdeal" ||
1050 aEvent
.FeatureURL
.Path
== "WrapOn" ||
1051 aEvent
.FeatureURL
.Path
== "WrapOff" ||
1052 aEvent
.FeatureURL
.Path
== "UpdateCurIndex" ||
1053 aEvent
.FeatureURL
.Path
== "InsertCaptionDialog" ||
1054 aEvent
.FeatureURL
.Path
== "MergeCells" ||
1055 aEvent
.FeatureURL
.Path
== "SplitTable" ||
1056 aEvent
.FeatureURL
.Path
== "DeleteNote" ||
1057 aEvent
.FeatureURL
.Path
== "AcceptChanges" ||
1058 aEvent
.FeatureURL
.Path
== "SetDefault" ||
1059 aEvent
.FeatureURL
.Path
== "ParaLeftToRight" ||
1060 aEvent
.FeatureURL
.Path
== "ParaRightToLeft" ||
1061 aEvent
.FeatureURL
.Path
== "ParaspaceIncrease" ||
1062 aEvent
.FeatureURL
.Path
== "ParaspaceDecrease" ||
1063 aEvent
.FeatureURL
.Path
== "TableDialog" ||
1064 aEvent
.FeatureURL
.Path
== "FormatCellDialog" ||
1065 aEvent
.FeatureURL
.Path
== "FontDialog" ||
1066 aEvent
.FeatureURL
.Path
== "ParagraphDialog" ||
1067 aEvent
.FeatureURL
.Path
== "OutlineBullet" ||
1068 aEvent
.FeatureURL
.Path
== "InsertIndexesEntry" ||
1069 aEvent
.FeatureURL
.Path
== "TransformDialog" ||
1070 aEvent
.FeatureURL
.Path
== "EditRegion" ||
1071 aEvent
.FeatureURL
.Path
== "ThesaurusDialog" ||
1072 aEvent
.FeatureURL
.Path
== "OutlineRight" ||
1073 aEvent
.FeatureURL
.Path
== "OutlineLeft" ||
1074 aEvent
.FeatureURL
.Path
== "OutlineDown" ||
1075 aEvent
.FeatureURL
.Path
== "OutlineUp" ||
1076 aEvent
.FeatureURL
.Path
== "FormatArea" ||
1077 aEvent
.FeatureURL
.Path
== "FormatLine" ||
1078 aEvent
.FeatureURL
.Path
== "FormatColumns" ||
1079 aEvent
.FeatureURL
.Path
== "Watermark" ||
1080 aEvent
.FeatureURL
.Path
== "InsertBreak" ||
1081 aEvent
.FeatureURL
.Path
== "InsertEndnote" ||
1082 aEvent
.FeatureURL
.Path
== "InsertFootnote" ||
1083 aEvent
.FeatureURL
.Path
== "InsertReferenceField" ||
1084 aEvent
.FeatureURL
.Path
== "InsertBookmark" ||
1085 aEvent
.FeatureURL
.Path
== "InsertAuthoritiesEntry" ||
1086 aEvent
.FeatureURL
.Path
== "InsertMultiIndex" ||
1087 aEvent
.FeatureURL
.Path
== "InsertField" ||
1088 aEvent
.FeatureURL
.Path
== "InsertPageNumberField" ||
1089 aEvent
.FeatureURL
.Path
== "InsertPageCountField" ||
1090 aEvent
.FeatureURL
.Path
== "InsertDateField" ||
1091 aEvent
.FeatureURL
.Path
== "InsertTitleField" ||
1092 aEvent
.FeatureURL
.Path
== "InsertFieldCtrl" ||
1093 aEvent
.FeatureURL
.Path
== "CharmapControl" ||
1094 aEvent
.FeatureURL
.Path
== "EnterGroup" ||
1095 aEvent
.FeatureURL
.Path
== "LeaveGroup" ||
1096 aEvent
.FeatureURL
.Path
== "Combine" ||
1097 aEvent
.FeatureURL
.Path
== "Merge" ||
1098 aEvent
.FeatureURL
.Path
== "Dismantle" ||
1099 aEvent
.FeatureURL
.Path
== "Substract" ||
1100 aEvent
.FeatureURL
.Path
== "DistributeSelection" ||
1101 aEvent
.FeatureURL
.Path
== "Intersect" ||
1102 aEvent
.FeatureURL
.Path
== "ResetAttributes" ||
1103 aEvent
.FeatureURL
.Path
== "IncrementIndent" ||
1104 aEvent
.FeatureURL
.Path
== "DecrementIndent" ||
1105 aEvent
.FeatureURL
.Path
== "NumberFormatDecDecimals" ||
1106 aEvent
.FeatureURL
.Path
== "NumberFormatIncDecimals")
1108 aBuffer
.append(aEvent
.IsEnabled
? std::u16string_view(u
"enabled") : std::u16string_view(u
"disabled"));
1110 else if (aEvent
.FeatureURL
.Path
== "AssignLayout" ||
1111 aEvent
.FeatureURL
.Path
== "StatusSelectionMode" ||
1112 aEvent
.FeatureURL
.Path
== "Signature" ||
1113 aEvent
.FeatureURL
.Path
== "SelectionMode" ||
1114 aEvent
.FeatureURL
.Path
== "StatusBarFunc" ||
1115 aEvent
.FeatureURL
.Path
== "FreezePanesColumn" ||
1116 aEvent
.FeatureURL
.Path
== "FreezePanesRow")
1120 if (aEvent
.IsEnabled
&& (aEvent
.State
>>= aInt32
))
1122 aBuffer
.append(aInt32
);
1125 else if (aEvent
.FeatureURL
.Path
== "TransformPosX" ||
1126 aEvent
.FeatureURL
.Path
== "TransformPosY" ||
1127 aEvent
.FeatureURL
.Path
== "TransformWidth" ||
1128 aEvent
.FeatureURL
.Path
== "TransformHeight")
1130 const SfxViewShell
* pViewShell
= SfxViewShell::Current();
1131 if (aEvent
.IsEnabled
&& pViewShell
&& pViewShell
->isLOKMobilePhone())
1133 boost::property_tree::ptree aTree
;
1134 boost::property_tree::ptree aState
;
1135 OUString
aStr(aEvent
.FeatureURL
.Complete
);
1137 aTree
.put("commandName", aStr
.toUtf8().getStr());
1138 pViewFrame
->GetBindings().QueryControlState(nSID
, aState
);
1139 aTree
.add_child("state", aState
);
1141 aBuffer
.setLength(0);
1142 std::stringstream aStream
;
1143 boost::property_tree::write_json(aStream
, aTree
);
1144 aBuffer
.appendAscii(aStream
.str().c_str());
1147 else if (aEvent
.FeatureURL
.Path
== "StatusDocPos" ||
1148 aEvent
.FeatureURL
.Path
== "RowColSelCount" ||
1149 aEvent
.FeatureURL
.Path
== "StatusPageStyle" ||
1150 aEvent
.FeatureURL
.Path
== "StateTableCell" ||
1151 aEvent
.FeatureURL
.Path
== "StateWordCount" ||
1152 aEvent
.FeatureURL
.Path
== "PageStyleName" ||
1153 aEvent
.FeatureURL
.Path
== "PageStatus" ||
1154 aEvent
.FeatureURL
.Path
== "LayoutStatus" ||
1155 aEvent
.FeatureURL
.Path
== "Scale" ||
1156 aEvent
.FeatureURL
.Path
== "Context")
1160 if (aEvent
.IsEnabled
&& (aEvent
.State
>>= aString
))
1162 aBuffer
.append(aString
);
1165 else if (aEvent
.FeatureURL
.Path
== "InsertMode" ||
1166 aEvent
.FeatureURL
.Path
== "WrapText" ||
1167 aEvent
.FeatureURL
.Path
== "NumberFormatCurrency" ||
1168 aEvent
.FeatureURL
.Path
== "NumberFormatPercent" ||
1169 aEvent
.FeatureURL
.Path
== "NumberFormatDecimal" ||
1170 aEvent
.FeatureURL
.Path
== "NumberFormatDate" ||
1171 aEvent
.FeatureURL
.Path
== "ShowResolvedAnnotations")
1175 if (aEvent
.IsEnabled
&& (aEvent
.State
>>= aBool
))
1177 aBuffer
.append(OUString::boolean(aBool
));
1180 else if (aEvent
.FeatureURL
.Path
== "ToggleMergeCells")
1184 if (aEvent
.IsEnabled
&& (aEvent
.State
>>= aBool
))
1186 aBuffer
.append(OUString::boolean(aBool
));
1190 aBuffer
.append("disabled");
1193 else if (aEvent
.FeatureURL
.Path
== "Position")
1195 css::awt::Point aPoint
;
1197 if (aEvent
.IsEnabled
&& (aEvent
.State
>>= aPoint
))
1199 aBuffer
.append( OUString::number(aPoint
.X
) + " / " + OUString::number(aPoint
.Y
));
1202 else if (aEvent
.FeatureURL
.Path
== "Size")
1204 css::awt::Size aSize
;
1206 if (aEvent
.IsEnabled
&& (aEvent
.State
>>= aSize
))
1208 aBuffer
.append( OUString::number(aSize
.Width
) + " x " + OUString::number(aSize
.Height
) );
1211 else if (aEvent
.FeatureURL
.Path
== "LanguageStatus" ||
1212 aEvent
.FeatureURL
.Path
== "StatePageNumber")
1214 css::uno::Sequence
< OUString
> aSeq
;
1216 if (aEvent
.IsEnabled
)
1219 if (aEvent
.State
>>= sValue
)
1221 aBuffer
.append(sValue
);
1223 else if (aEvent
.State
>>= aSeq
)
1225 aBuffer
.append(aSeq
[0]);
1229 else if (aEvent
.FeatureURL
.Path
== "InsertPageHeader" ||
1230 aEvent
.FeatureURL
.Path
== "InsertPageFooter")
1232 if (aEvent
.IsEnabled
)
1234 css::uno::Sequence
< OUString
> aSeq
;
1235 if (aEvent
.State
>>= aSeq
)
1237 aBuffer
.append(u
'{');
1238 for (sal_Int32 itSeq
= 0; itSeq
< aSeq
.getLength(); itSeq
++)
1240 aBuffer
.append("\"" + aSeq
[itSeq
]);
1241 if (itSeq
!= aSeq
.getLength() - 1)
1242 aBuffer
.append("\":true,");
1244 aBuffer
.append("\":true");
1246 aBuffer
.append(u
'}');
1250 else if (aEvent
.FeatureURL
.Path
== "TableColumWidth" ||
1251 aEvent
.FeatureURL
.Path
== "TableRowHeight")
1254 if (aEvent
.State
>>= nValue
)
1256 float nScaleValue
= 1000.0;
1257 nValue
*= nScaleValue
;
1258 sal_Int32 nConvertedValue
= o3tl::convert(nValue
, o3tl::Length::twip
, o3tl::Length::in
);
1259 aBuffer
.append(nConvertedValue
/ nScaleValue
);
1264 // Try to send JSON state version
1265 SfxLokHelper::sendUnoStatus(pViewFrame
->GetViewShell(), pState
);
1270 OUString payload
= aBuffer
.makeStringAndClear();
1271 if (const SfxViewShell
* pViewShell
= pViewFrame
->GetViewShell())
1272 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
, payload
.toUtf8().getStr());
1275 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */