xref: /core/sfx2/source/control/unoctitm.cxx (revision ffa17012)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <config_java.h>
21 
22 #include <tools/debug.hxx>
23 #include <svl/eitem.hxx>
24 #include <svl/intitem.hxx>
25 #include <svl/itempool.hxx>
26 #include <svl/itemset.hxx>
27 #include <svl/stritem.hxx>
28 #include <svl/visitem.hxx>
29 #include <svtools/javacontext.hxx>
30 #include <svtools/javainteractionhandler.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/XFrame.hpp>
38 #include <com/sun/star/frame/status/FontHeight.hpp>
39 #include <com/sun/star/frame/status/ItemStatus.hpp>
40 #include <com/sun/star/frame/status/ItemState.hpp>
41 #include <com/sun/star/frame/status/Template.hpp>
42 #include <com/sun/star/frame/DispatchResultState.hpp>
43 #include <com/sun/star/frame/status/Visibility.hpp>
44 #include <comphelper/processfactory.hxx>
45 #include <uno/current_context.hxx>
46 #include <utility>
47 #include <vcl/svapp.hxx>
48 #include <vcl/uitest/logger.hxx>
49 #include <boost/property_tree/json_parser.hpp>
50 #include <tools/json_writer.hxx>
51 
52 #include <sfx2/app.hxx>
53 #include <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 <sfx2/msg.hxx>
63 #include <sfx2/viewsh.hxx>
64 #include <slotserv.hxx>
65 #include <rtl/ustring.hxx>
66 #include <sfx2/lokhelper.hxx>
67 
68 #include <memory>
69 #include <string_view>
70 
71 #include <sal/log.hxx>
72 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
73 #include <comphelper/lok.hxx>
74 #include <comphelper/servicehelper.hxx>
75 
76 #include <desktop/crashreport.hxx>
77 #include <vcl/threadex.hxx>
78 #include <unotools/mediadescriptor.hxx>
79 
80 #include <frozen/bits/defines.h>
81 #include <frozen/bits/elsa_std.h>
82 #include <frozen/unordered_map.h>
83 
84 using namespace ::com::sun::star;
85 using namespace ::com::sun::star::uno;
86 using namespace ::com::sun::star::util;
87 
88 namespace {
89 
90 enum URLTypeId
91 {
92     URLType_BOOL,
93     URLType_BYTE,
94     URLType_SHORT,
95     URLType_LONG,
96     URLType_HYPER,
97     URLType_STRING,
98     URLType_FLOAT,
99     URLType_DOUBLE,
100     URLType_COUNT
101 };
102 
103 }
104 
105 const char* const URLTypeNames[URLType_COUNT] =
106 {
107     "bool",
108     "byte",
109     "short",
110     "long",
111     "hyper",
112     "string",
113     "float",
114     "double"
115 };
116 
117 static void InterceptLOKStateChangeEvent( sal_uInt16 nSID, SfxViewFrame* pViewFrame, const css::frame::FeatureStateEvent& aEvent, const SfxPoolItem* pState );
118 
ReleaseAll()119 void SfxStatusDispatcher::ReleaseAll()
120 {
121     css::lang::EventObject aObject;
122     aObject.Source = getXWeak();
123     std::unique_lock aGuard(maMutex);
124     maListeners.disposeAndClear( aGuard, aObject );
125 }
126 
sendStatusChanged(const OUString & rURL,const css::frame::FeatureStateEvent & rEvent)127 void SfxStatusDispatcher::sendStatusChanged(const OUString& rURL, const css::frame::FeatureStateEvent& rEvent)
128 {
129     std::unique_lock aGuard(maMutex);
130     ::comphelper::OInterfaceContainerHelper4<css::frame::XStatusListener>* pContnr = maListeners.getContainer(aGuard, rURL);
131     if (!pContnr)
132         return;
133     pContnr->forEach(aGuard,
134         [&rEvent](const css::uno::Reference<css::frame::XStatusListener>& xListener)
135         {
136             xListener->statusChanged(rEvent);
137         }
138     );
139 }
140 
dispatch(const css::util::URL &,const css::uno::Sequence<css::beans::PropertyValue> &)141 void SAL_CALL SfxStatusDispatcher::dispatch( const css::util::URL&, const css::uno::Sequence< css::beans::PropertyValue >& )
142 {
143 }
144 
dispatchWithNotification(const css::util::URL &,const css::uno::Sequence<css::beans::PropertyValue> &,const css::uno::Reference<css::frame::XDispatchResultListener> &)145 void SAL_CALL SfxStatusDispatcher::dispatchWithNotification(
146     const css::util::URL&,
147     const css::uno::Sequence< css::beans::PropertyValue >&,
148     const css::uno::Reference< css::frame::XDispatchResultListener >& )
149 {
150 }
151 
SfxStatusDispatcher()152 SfxStatusDispatcher::SfxStatusDispatcher()
153 {
154 }
155 
addStatusListener(const css::uno::Reference<css::frame::XStatusListener> & aListener,const css::util::URL & aURL)156 void SAL_CALL SfxStatusDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener > & aListener, const css::util::URL& aURL)
157 {
158     {
159         std::unique_lock aGuard(maMutex);
160         maListeners.addInterface( aGuard, aURL.Complete, aListener );
161     }
162     if ( aURL.Complete == ".uno:LifeTime" )
163     {
164         css::frame::FeatureStateEvent aEvent;
165         aEvent.FeatureURL = aURL;
166         aEvent.Source = static_cast<css::frame::XDispatch*>(this);
167         aEvent.IsEnabled = true;
168         aEvent.Requery = false;
169         aListener->statusChanged( aEvent );
170     }
171 }
172 
removeStatusListener(const css::uno::Reference<css::frame::XStatusListener> & aListener,const css::util::URL & aURL)173 void SAL_CALL SfxStatusDispatcher::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener > & aListener, const css::util::URL& aURL )
174 {
175     std::unique_lock aGuard(maMutex);
176     maListeners.removeInterface( aGuard, aURL.Complete, aListener );
177 }
178 
179 
SfxOfficeDispatch(SfxBindings & rBindings,SfxDispatcher * pDispat,const SfxSlot * pSlot,const css::util::URL & rURL)180 SfxOfficeDispatch::SfxOfficeDispatch( SfxBindings& rBindings, SfxDispatcher* pDispat, const SfxSlot* pSlot, const css::util::URL& rURL )
181     : pImpl( new SfxDispatchController_Impl( this, &rBindings, pDispat, pSlot, rURL ))
182 {
183     // pImpl is an adapter that shows a css::frame::XDispatch-Interface to the outside and uses a SfxControllerItem to monitor a state
184 
185 }
186 
SfxOfficeDispatch(SfxDispatcher * pDispat,const SfxSlot * pSlot,const css::util::URL & rURL)187 SfxOfficeDispatch::SfxOfficeDispatch( SfxDispatcher* pDispat, const SfxSlot* pSlot, const css::util::URL& rURL )
188     : pImpl( new SfxDispatchController_Impl( this, nullptr, pDispat, pSlot, rURL ))
189 {
190     // pImpl is an adapter that shows a css::frame::XDispatch-Interface to the outside and uses a SfxControllerItem to monitor a state
191 }
192 
~SfxOfficeDispatch()193 SfxOfficeDispatch::~SfxOfficeDispatch()
194 {
195     if ( pImpl )
196     {
197         // when dispatch object is released, destroy its connection to this object and destroy it
198         pImpl->UnBindController();
199 
200         // Ensure that SfxDispatchController_Impl is deleted while the solar mutex is locked, since
201         // that derives from SfxListener.
202         SolarMutexGuard aGuard;
203         pImpl.reset();
204     }
205 }
206 
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.
215 namespace {
EnsureJavaContext()216 std::unique_ptr< css::uno::ContextLayer > EnsureJavaContext()
217 {
218     css::uno::Reference< css::uno::XCurrentContext > xContext(css::uno::getCurrentContext());
219     if (xContext.is())
220     {
221         css::uno::Reference< css::task::XInteractionHandler > xHandler;
222         xContext->getValueByName(JAVA_INTERACTION_HANDLER_NAME) >>= xHandler;
223         if (xHandler.is())
224             return nullptr; // No need to add new layer: JavaContext already present
225     }
226     return std::make_unique< css::uno::ContextLayer >(new svt::JavaContext(xContext));
227 }
228 }
229 #endif
230 
dispatch(const css::util::URL & aURL,const css::uno::Sequence<css::beans::PropertyValue> & aArgs)231 void SAL_CALL SfxOfficeDispatch::dispatch( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& aArgs )
232 {
233     // ControllerItem is the Impl class
234     if ( pImpl )
235     {
236 #if HAVE_FEATURE_JAVA
237         std::unique_ptr< css::uno::ContextLayer > layer(EnsureJavaContext());
238 #endif
239         utl::MediaDescriptor aDescriptor(aArgs);
240         bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault(u"OnMainThread"_ustr, false);
241         if (bOnMainThread)
242         {
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             // another 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>());
250             });
251         }
252         else
253         {
254             pImpl->dispatch(aURL, aArgs,
255                             css::uno::Reference<css::frame::XDispatchResultListener>());
256         }
257     }
258 }
259 
dispatchWithNotification(const css::util::URL & aURL,const css::uno::Sequence<css::beans::PropertyValue> & aArgs,const css::uno::Reference<css::frame::XDispatchResultListener> & rListener)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 )
263 {
264     // ControllerItem is the Impl class
265     if ( pImpl )
266     {
267 #if HAVE_FEATURE_JAVA
268         std::unique_ptr< css::uno::ContextLayer > layer(EnsureJavaContext());
269 #endif
270         pImpl->dispatch( aURL, aArgs, rListener );
271     }
272 }
273 
addStatusListener(const css::uno::Reference<css::frame::XStatusListener> & aListener,const css::util::URL & aURL)274 void SAL_CALL SfxOfficeDispatch::addStatusListener(const css::uno::Reference< css::frame::XStatusListener > & aListener, const css::util::URL& aURL)
275 {
276     {
277         std::unique_lock aGuard(maMutex);
278         maListeners.addInterface( aGuard, aURL.Complete, aListener );
279     }
280     if ( pImpl )
281     {
282         // ControllerItem is the Impl class
283         pImpl->addStatusListener( aListener, aURL );
284     }
285 }
286 
GetDispatcher_Impl()287 SfxDispatcher* SfxOfficeDispatch::GetDispatcher_Impl()
288 {
289     return pImpl->GetDispatcher();
290 }
291 
GetId() const292 sal_uInt16 SfxOfficeDispatch::GetId() const
293 {
294     return pImpl ? pImpl->GetId() : 0;
295 }
296 
SetFrame(const css::uno::Reference<css::frame::XFrame> & xFrame)297 void SfxOfficeDispatch::SetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame)
298 {
299     if ( pImpl )
300         pImpl->SetFrame( xFrame );
301 }
302 
SetMasterUnoCommand(bool bSet)303 void SfxOfficeDispatch::SetMasterUnoCommand( bool bSet )
304 {
305     if ( pImpl )
306         pImpl->setMasterSlaveCommand( bSet );
307 }
308 
309 // Determine if URL contains a master/slave command which must be handled a little bit different
IsMasterUnoCommand(const css::util::URL & aURL)310 bool SfxOfficeDispatch::IsMasterUnoCommand( const css::util::URL& aURL )
311 {
312     return aURL.Protocol == ".uno:" && ( aURL.Path.indexOf( '.' ) > 0 );
313 }
314 
GetMasterUnoCommand(const css::util::URL & aURL)315 OUString SfxOfficeDispatch::GetMasterUnoCommand( const css::util::URL& aURL )
316 {
317     OUString aMasterCommand;
318     if ( IsMasterUnoCommand( aURL ))
319     {
320         sal_Int32 nIndex = aURL.Path.indexOf( '.' );
321         if ( nIndex > 0 )
322             aMasterCommand = aURL.Path.copy( 0, nIndex );
323     }
324 
325     return aMasterCommand;
326 }
327 
SfxDispatchController_Impl(SfxOfficeDispatch * pDisp,SfxBindings * pBind,SfxDispatcher * pDispat,const SfxSlot * pSlot,css::util::URL aURL)328 SfxDispatchController_Impl::SfxDispatchController_Impl(
329     SfxOfficeDispatch*                 pDisp,
330     SfxBindings*                       pBind,
331     SfxDispatcher*                     pDispat,
332     const SfxSlot*                     pSlot,
333     css::util::URL               aURL )
334     : aDispatchURL(std::move( aURL ))
335     , pDispatcher( pDispat )
336     , pBindings( pBind )
337     , pLastState( nullptr )
338     , pDispatch( pDisp )
339     , bMasterSlave( false )
340     , bVisible( true )
341 {
342     if ( aDispatchURL.Protocol == "slot:" && !pSlot->aUnoName.isEmpty() )
343     {
344         aDispatchURL.Complete = pSlot->GetCommand();
345         Reference< XURLTransformer > xTrans( URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
346         xTrans->parseStrict( aDispatchURL );
347     }
348 
349     sal_uInt16 nSlot = pSlot->GetSlotId();
350     SetId( nSlot );
351     if ( pBindings )
352     {
353         // Bind immediately to enable the cache to recycle dispatches when asked for the same command
354         // a command in "slot" or in ".uno" notation must be treated as identical commands!
355         pBindings->ENTERREGISTRATIONS();
356         BindInternal_Impl( nSlot, pBindings );
357         pBindings->LEAVEREGISTRATIONS();
358     }
359     assert(pDispatcher);
360     assert(SfxApplication::Get()->GetAppDispatcher_Impl() == pDispatcher
361         || pDispatcher->GetFrame() != nullptr);
362     if (pDispatcher->GetFrame())
363     {
364         StartListening(*pDispatcher->GetFrame());
365     }
366     else
367     {
368         StartListening(*SfxApplication::Get());
369     }
370 }
371 
Notify(SfxBroadcaster & rBC,SfxHint const & rHint)372 void SfxDispatchController_Impl::Notify(SfxBroadcaster& rBC, SfxHint const& rHint)
373 {
374     if (rHint.GetId() == SfxHintId::Dying)
375     {   // both pBindings and pDispatcher are dead if SfxViewFrame is dead
376         pBindings = nullptr;
377         pDispatcher = nullptr;
378         EndListening(rBC);
379     }
380 }
381 
~SfxDispatchController_Impl()382 SfxDispatchController_Impl::~SfxDispatchController_Impl()
383 {
384     if ( pLastState && !IsInvalidItem( pLastState ) )
385         delete pLastState;
386 
387     if ( pDispatch )
388     {
389         // disconnect
390         pDispatch->pImpl = nullptr;
391 
392         // force all listeners to release the dispatch object
393         pDispatch->ReleaseAll();
394     }
395 }
396 
SetFrame(const css::uno::Reference<css::frame::XFrame> & _xFrame)397 void SfxDispatchController_Impl::SetFrame(const css::uno::Reference< css::frame::XFrame >& _xFrame)
398 {
399     xFrame = _xFrame;
400 }
401 
setMasterSlaveCommand(bool bSet)402 void SfxDispatchController_Impl::setMasterSlaveCommand( bool bSet )
403 {
404     bMasterSlave = bSet;
405 }
406 
UnBindController()407 void SfxDispatchController_Impl::UnBindController()
408 {
409     pDispatch = nullptr;
410     if ( IsBound() )
411     {
412         GetBindings().ENTERREGISTRATIONS();
413         SfxControllerItem::UnBind();
414         GetBindings().LEAVEREGISTRATIONS();
415     }
416 }
417 
addParametersToArgs(const css::util::URL & aURL,css::uno::Sequence<css::beans::PropertyValue> & rArgs)418 void SfxDispatchController_Impl::addParametersToArgs( const css::util::URL& aURL, css::uno::Sequence< css::beans::PropertyValue >& rArgs )
419 {
420     // Extract the parameter from the URL and put them into the property value sequence
421     sal_Int32 nQueryIndex = aURL.Complete.indexOf( '?' );
422     if ( nQueryIndex <= 0 )
423         return;
424 
425     OUString aParamString( aURL.Complete.copy( nQueryIndex+1 ));
426     sal_Int32 nIndex = 0;
427     do
428     {
429         OUString aToken = aParamString.getToken( 0, '&', nIndex );
430 
431         sal_Int32 nParmIndex = 0;
432         OUString aParamType;
433         OUString aParamName = aToken.getToken( 0, '=', nParmIndex );
434         OUString aValue     = aToken.getToken( 0, '=', nParmIndex );
435 
436         if ( !aParamName.isEmpty() )
437         {
438             nParmIndex = 0;
439             aToken = aParamName;
440             aParamName = aToken.getToken( 0, ':', nParmIndex );
441             aParamType = aToken.getToken( 0, ':', nParmIndex );
442         }
443 
444         sal_Int32 nLen = rArgs.getLength();
445         rArgs.realloc( nLen+1 );
446         auto pArgs = rArgs.getArray();
447         pArgs[nLen].Name = aParamName;
448 
449         if ( aParamType.isEmpty() )
450         {
451             // Default: LONG
452             pArgs[nLen].Value <<= aValue.toInt32();
453         }
454         else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_BOOL], 4 ))
455         {
456             // sal_Bool support
457             pArgs[nLen].Value <<= aValue.toBoolean();
458         }
459         else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_BYTE], 4 ))
460         {
461             // sal_uInt8 support
462             pArgs[nLen].Value <<= sal_Int8( aValue.toInt32() );
463         }
464         else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_LONG], 4 ))
465         {
466             // LONG support
467             pArgs[nLen].Value <<= aValue.toInt32();
468         }
469         else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_SHORT], 5 ))
470         {
471             // SHORT support
472             pArgs[nLen].Value <<= sal_Int16( aValue.toInt32() );
473         }
474         else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_HYPER], 5 ))
475         {
476             // HYPER support
477             pArgs[nLen].Value <<= aValue.toInt64();
478         }
479         else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_FLOAT], 5 ))
480         {
481             // FLOAT support
482             pArgs[nLen].Value <<= aValue.toFloat();
483         }
484         else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_STRING], 6 ))
485         {
486             // STRING support
487             pArgs[nLen].Value <<= INetURLObject::decode( aValue, INetURLObject::DecodeMechanism::WithCharset );
488         }
489         else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_DOUBLE], 6))
490         {
491             // DOUBLE support
492             pArgs[nLen].Value <<= aValue.toDouble();
493         }
494     }
495     while ( nIndex >= 0 );
496 }
497 
GetCoreMetric(SfxItemPool const & rPool,sal_uInt16 nSlotId)498 MapUnit SfxDispatchController_Impl::GetCoreMetric( SfxItemPool const & rPool, sal_uInt16 nSlotId )
499 {
500     sal_uInt16 nWhich = rPool.GetWhichIDFromSlotID( nSlotId );
501     return rPool.GetMetric( nWhich );
502 }
503 
getSlaveCommand(const css::util::URL & rURL)504 OUString SfxDispatchController_Impl::getSlaveCommand( const css::util::URL& rURL )
505 {
506     OUString   aSlaveCommand;
507     sal_Int32       nIndex = rURL.Path.indexOf( '.' );
508     if (( nIndex > 0 ) && ( nIndex < rURL.Path.getLength() ))
509         aSlaveCommand = rURL.Path.copy( nIndex+1 );
510     return aSlaveCommand;
511 }
512 
513 namespace {
514 
collectUIInformation(const util::URL & rURL,const css::uno::Sequence<css::beans::PropertyValue> & rArgs)515 void collectUIInformation(const util::URL& rURL, const css::uno::Sequence< css::beans::PropertyValue >& rArgs)
516 {
517     static const char* pFile = std::getenv("LO_COLLECT_UIINFO");
518     if (!pFile)
519         return;
520 
521     UITestLogger::getInstance().logCommand(
522         Concat2View("Send UNO Command (\"" + rURL.Complete + "\") "), rArgs);
523 }
524 
525 }
526 
dispatch(const css::util::URL & aURL,const css::uno::Sequence<css::beans::PropertyValue> & aArgs,const css::uno::Reference<css::frame::XDispatchResultListener> & rListener)527 void SfxDispatchController_Impl::dispatch( const css::util::URL& aURL,
528         const css::uno::Sequence< css::beans::PropertyValue >& aArgs,
529         const css::uno::Reference< css::frame::XDispatchResultListener >& rListener )
530 {
531     if ( aURL.Protocol == ".uno:")
532     {
533         CrashReporter::logUnoCommand(aURL.Path);
534     }
535     collectUIInformation(aURL, aArgs);
536 
537     SolarMutexGuard aGuard;
538 
539     if (comphelper::LibreOfficeKit::isActive())
540     {
541         const SfxViewShell* pViewShell = SfxViewShell::Current();
542         if (pViewShell && pViewShell->isBlockedCommand(aURL.Complete))
543         {
544             tools::JsonWriter aTree;
545             aTree.put("code", "");
546             aTree.put("kind", "BlockedCommand");
547             aTree.put("cmd", aURL.Complete);
548             aTree.put("message", "Blocked feature");
549             aTree.put("viewID", pViewShell->GetViewShellId().get());
550 
551             pViewShell->libreOfficeKitViewCallback(LOK_COMMAND_BLOCKED, aTree.finishAndGetAsOString());
552             return;
553         }
554     }
555 
556     if (
557         !(pDispatch &&
558         (
559          (aURL.Protocol == ".uno:" && aURL.Path == aDispatchURL.Path) ||
560          (aURL.Protocol == "slot:" && aURL.Path.toInt32() == GetId())
561         ))
562        )
563         return;
564 
565     if ( !pDispatcher && pBindings )
566         pDispatcher = GetBindings().GetDispatcher_Impl();
567 
568     css::uno::Sequence< css::beans::PropertyValue > lNewArgs;
569     sal_Int32 nCount = aArgs.getLength();
570 
571     // Support for URL based arguments
572     INetURLObject aURLObj( aURL.Complete );
573     if ( aURLObj.HasParam() )
574         addParametersToArgs( aURL, lNewArgs );
575 
576     // Try to find call mode and frame name inside given arguments...
577     SfxCallMode nCall = SfxCallMode::RECORD;
578     sal_Int32   nMarkArg = -1;
579 
580     // Filter arguments which shouldn't be part of the sequence property value
581     sal_uInt16  nModifier(0);
582     std::vector< css::beans::PropertyValue > aAddArgs;
583     for( sal_Int32 n=0; n<nCount; n++ )
584     {
585         const css::beans::PropertyValue& rProp = aArgs[n];
586         if( rProp.Name == "SynchronMode" )
587         {
588             bool    bTemp;
589             if( rProp.Value >>= bTemp )
590                 nCall = bTemp ? SfxCallMode::SYNCHRON : SfxCallMode::ASYNCHRON;
591         }
592         else if( rProp.Name == "Bookmark" )
593         {
594             nMarkArg = n;
595             aAddArgs.push_back( aArgs[n] );
596         }
597         else if( rProp.Name == "KeyModifier" )
598             rProp.Value >>= nModifier;
599         else
600             aAddArgs.push_back( aArgs[n] );
601     }
602 
603     // Add needed arguments to sequence property value
604     sal_uInt32 nAddArgs = aAddArgs.size();
605     if ( nAddArgs > 0 )
606     {
607         sal_uInt32 nIndex( lNewArgs.getLength() );
608 
609         lNewArgs.realloc( nIndex + nAddArgs );
610         std::copy(aAddArgs.begin(), aAddArgs.end(), std::next(lNewArgs.getArray(), nIndex));
611     }
612 
613     // Overwrite possible detected synchron argument, if real listener exists (currently no other way)
614     if ( rListener.is() )
615         nCall = SfxCallMode::SYNCHRON;
616 
617     if( GetId() == SID_JUMPTOMARK && nMarkArg == - 1 )
618     {
619         // we offer dispatches for SID_JUMPTOMARK if the URL points to a bookmark inside the document
620         // so we must retrieve this as an argument from the parsed URL
621         lNewArgs.realloc( lNewArgs.getLength()+1 );
622         auto& el = lNewArgs.getArray()[lNewArgs.getLength()-1];
623         el.Name = "Bookmark";
624         el.Value <<= aURL.Mark;
625     }
626 
627     css::uno::Reference< css::frame::XFrame > xFrameRef(xFrame.get(), css::uno::UNO_QUERY);
628     if (! xFrameRef.is() && pDispatcher)
629     {
630         SfxViewFrame* pViewFrame = pDispatcher->GetFrame();
631         if (pViewFrame)
632             xFrameRef = pViewFrame->GetFrame().GetFrameInterface();
633     }
634 
635     bool bSuccess = false;
636     SfxPoolItemHolder aItem;
637     MapUnit eMapUnit( MapUnit::Map100thMM );
638 
639     // Extra scope so that aInternalSet is destroyed before
640     // rListener->dispatchFinished potentially calls
641     // framework::Desktop::terminate -> SfxApplication::Deinitialize ->
642     // ~CntItemPool:
643     if (pDispatcher)
644     {
645         SfxAllItemSet aInternalSet( SfxGetpApp()->GetPool() );
646         if (xFrameRef.is()) // an empty set is no problem ... but an empty frame reference can be a problem !
647             aInternalSet.Put( SfxUnoFrameItem( SID_FILLFRAME, xFrameRef ) );
648 
649         SfxShell* pShell( nullptr );
650         // #i102619# Retrieve metric from shell before execution - the shell could be destroyed after execution
651         if ( pDispatcher->GetBindings() )
652         {
653             if ( !pDispatcher->IsLocked() )
654             {
655                 const SfxSlot *pSlot = nullptr;
656                 if ( pDispatcher->GetShellAndSlot_Impl( GetId(), &pShell, &pSlot, false, false ) )
657                 {
658                     if ( bMasterSlave )
659                     {
660                         // Extract slave command and add argument to the args list. Master slot MUST
661                         // have an argument that has the same name as the master slot and type is SfxStringItem.
662                         sal_Int32 nIndex = lNewArgs.getLength();
663                         lNewArgs.realloc( nIndex+1 );
664                         auto plNewArgs = lNewArgs.getArray();
665                         plNewArgs[nIndex].Name   = pSlot->aUnoName;
666                         plNewArgs[nIndex].Value  <<= SfxDispatchController_Impl::getSlaveCommand( aDispatchURL );
667                     }
668 
669                     eMapUnit = GetCoreMetric( pShell->GetPool(), GetId() );
670                     std::optional<SfxAllItemSet> xSet(pShell->GetPool());
671                     TransformParameters(GetId(), lNewArgs, *xSet, pSlot);
672                     if (xSet->Count())
673                     {
674                         // execute with arguments - call directly
675                         aItem = pDispatcher->Execute(GetId(), nCall, &*xSet, &aInternalSet, nModifier);
676                         if (aItem)
677                         {
678                             if (const SfxBoolItem* pBoolItem = dynamic_cast<const SfxBoolItem*>(aItem.getItem()))
679                                 bSuccess = pBoolItem->GetValue();
680                             else if ( !IsDisabledItem(aItem.getItem()) )
681                                 bSuccess = true;  // all other types are true
682                         }
683                         // else bSuccess = false look to line 664 it is false
684                     }
685                     else
686                     {
687                         // Be sure to delete this before we send a dispatch
688                         // request, which will destroy the current shell.
689                         xSet.reset();
690 
691                         // execute using bindings, enables support for toggle/enum etc.
692                         SfxRequest aReq( GetId(), nCall, pShell->GetPool() );
693                         aReq.SetModifier( nModifier );
694                         aReq.SetInternalArgs_Impl(aInternalSet);
695                         pDispatcher->GetBindings()->Execute_Impl( aReq, pSlot, pShell );
696                         aItem = aReq.GetReturnValue();
697                         bSuccess = aReq.IsDone() || aItem;
698                     }
699                 }
700                 else
701                     SAL_INFO("sfx.control", "MacroPlayer: Unknown slot dispatched!");
702             }
703         }
704         else
705         {
706             eMapUnit = GetCoreMetric( SfxGetpApp()->GetPool(), GetId() );
707             // AppDispatcher
708             SfxAllItemSet aSet( SfxGetpApp()->GetPool() );
709             TransformParameters( GetId(), lNewArgs, aSet );
710 
711             if ( aSet.Count() )
712                 aItem = pDispatcher->Execute(GetId(), nCall, &aSet, &aInternalSet, nModifier);
713             else
714                 // SfxRequests take empty sets as argument sets, GetArgs() returning non-zero!
715                 aItem = pDispatcher->Execute(GetId(), nCall, nullptr, &aInternalSet, nModifier);
716 
717             // no bindings, no invalidate ( usually done in SfxDispatcher::Call_Impl()! )
718             if (SfxApplication* pApp = SfxApplication::Get())
719             {
720                 SfxDispatcher* pAppDispat = pApp->GetAppDispatcher_Impl();
721                 if ( pAppDispat )
722                 {
723                     SfxPoolItemHolder aResult;
724                     SfxItemState eState(pDispatcher->QueryState(GetId(), aResult));
725                     StateChangedAtToolBoxControl(GetId(), eState, aResult.getItem());
726                 }
727             }
728 
729             bSuccess = aItem.is();
730         }
731     }
732 
733     if ( !rListener.is() )
734         return;
735 
736     css::frame::DispatchResultEvent aEvent;
737     if ( bSuccess )
738         aEvent.State = css::frame::DispatchResultState::SUCCESS;
739     else
740         aEvent.State = css::frame::DispatchResultState::FAILURE;
741 
742     aEvent.Source = static_cast<css::frame::XDispatch*>(pDispatch);
743     if ( bSuccess && aItem && !IsDisabledItem(aItem.getItem()) )
744     {
745         sal_uInt16 nSubId( 0 );
746         if ( eMapUnit == MapUnit::MapTwip )
747             nSubId |= CONVERT_TWIPS;
748         aItem.getItem()->QueryValue( aEvent.Result, static_cast<sal_uInt8>(nSubId) );
749     }
750 
751     rListener->dispatchFinished( aEvent );
752 }
753 
GetDispatcher()754 SfxDispatcher* SfxDispatchController_Impl::GetDispatcher()
755 {
756     if ( !pDispatcher && pBindings )
757         pDispatcher = GetBindings().GetDispatcher_Impl();
758     return pDispatcher;
759 }
760 
addStatusListener(const css::uno::Reference<css::frame::XStatusListener> & aListener,const css::util::URL & aURL)761 void SfxDispatchController_Impl::addStatusListener(const css::uno::Reference< css::frame::XStatusListener > & aListener, const css::util::URL& aURL)
762 {
763     SolarMutexGuard aGuard;
764     if ( !pDispatch )
765         return;
766 
767     // Use alternative QueryState call to have a valid UNO representation of the state.
768     css::uno::Any aState;
769     if ( !pDispatcher && pBindings )
770         pDispatcher = GetBindings().GetDispatcher_Impl();
771     SfxItemState eState = pDispatcher ? pDispatcher->QueryState( GetId(), aState ) : SfxItemState::INVALID;
772 
773     if ( eState == SfxItemState::INVALID )
774     {
775         // Use special uno struct to transport don't care state
776         css::frame::status::ItemStatus aItemStatus;
777         aItemStatus.State = css::frame::status::ItemState::DONT_CARE;
778         aState <<= aItemStatus;
779     }
780 
781     css::frame::FeatureStateEvent  aEvent;
782     aEvent.FeatureURL = aURL;
783     aEvent.Source     = static_cast<css::frame::XDispatch*>(pDispatch);
784     aEvent.Requery    = false;
785     if ( bVisible )
786     {
787         aEvent.IsEnabled  = eState != SfxItemState::DISABLED;
788         aEvent.State      = aState;
789     }
790     else
791     {
792         css::frame::status::Visibility aVisibilityStatus;
793         aVisibilityStatus.bVisible = false;
794 
795         // MBA: we might decide to *not* disable "invisible" slots, but this would be
796         // a change that needs to adjust at least the testtool
797         aEvent.IsEnabled           = false;
798         aEvent.State               <<= aVisibilityStatus;
799     }
800 
801     aListener->statusChanged( aEvent );
802 }
803 
sendStatusChanged(const OUString & rURL,const css::frame::FeatureStateEvent & rEvent)804 void SfxDispatchController_Impl::sendStatusChanged(const OUString& rURL, const css::frame::FeatureStateEvent& rEvent)
805 {
806     pDispatch->sendStatusChanged(rURL, rEvent);
807 }
808 
StateChanged(sal_uInt16 nSID,SfxItemState eState,const SfxPoolItem * pState,SfxSlotServer const * pSlotServ)809 void SfxDispatchController_Impl::StateChanged( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState, SfxSlotServer const * pSlotServ )
810 {
811     if ( !pDispatch )
812         return;
813 
814     // Bindings instance notifies controller about a state change, listeners must be notified also
815     // Don't cache visibility state changes as they are volatile. We need our real state to send it
816     // to our controllers after visibility is set to true.
817     bool bNotify = true;
818     if ( pState && !IsInvalidItem( pState ) )
819     {
820         if ( auto pVisibilityItem = dynamic_cast< const SfxVisibilityItem *>( pState ) )
821             bVisible = pVisibilityItem->GetValue();
822         else
823         {
824             if (pLastState && !IsInvalidItem(pLastState))
825             {
826                 bNotify = typeid(*pState) != typeid(*pLastState) || *pState != *pLastState;
827                 delete pLastState;
828             }
829             pLastState = !IsInvalidItem(pState) ? pState->Clone() : pState;
830             bVisible = true;
831         }
832     }
833     else
834     {
835         if ( pLastState && !IsInvalidItem( pLastState ) )
836             delete pLastState;
837         pLastState = pState;
838     }
839 
840     if (!bNotify)
841         return;
842 
843     css::uno::Any aState;
844     if ( ( eState >= SfxItemState::DEFAULT ) && pState && !IsInvalidItem( pState ) && !IsDisabledItem(pState) )
845     {
846         // Retrieve metric from pool to have correct sub ID when calling QueryValue
847         sal_uInt16     nSubId( 0 );
848         MapUnit eMapUnit( MapUnit::Map100thMM );
849 
850         // retrieve the core metric
851         // it's enough to check the objectshell, the only shell that does not use the pool of the document
852         // is SfxViewFrame, but it hasn't any metric parameters
853         // TODO/LATER: what about the FormShell? Does it use any metric data?! Perhaps it should use the Pool of the document!
854         if ( pSlotServ && pDispatcher )
855         {
856             if (SfxShell* pShell = pDispatcher->GetShell( pSlotServ->GetShellLevel() ))
857                 eMapUnit = GetCoreMetric( pShell->GetPool(), nSID );
858         }
859 
860         if ( eMapUnit == MapUnit::MapTwip )
861             nSubId |= CONVERT_TWIPS;
862 
863         pState->QueryValue( aState, static_cast<sal_uInt8>(nSubId) );
864     }
865     else if ( eState == SfxItemState::INVALID )
866     {
867         // Use special uno struct to transport don't care state
868         css::frame::status::ItemStatus aItemStatus;
869         aItemStatus.State = css::frame::status::ItemState::DONT_CARE;
870         aState <<= aItemStatus;
871     }
872 
873     css::frame::FeatureStateEvent aEvent;
874     aEvent.FeatureURL = aDispatchURL;
875     aEvent.Source = static_cast<css::frame::XDispatch*>(pDispatch);
876     aEvent.IsEnabled = eState != SfxItemState::DISABLED;
877     aEvent.Requery = false;
878     aEvent.State = aState;
879 
880     if (pDispatcher && pDispatcher->GetFrame())
881     {
882         InterceptLOKStateChangeEvent(nSID, pDispatcher->GetFrame(), aEvent, pState);
883     }
884 
885     const std::vector<OUString> aContainedTypes = pDispatch->getContainedTypes();
886     for (const OUString& rName: aContainedTypes)
887     {
888         if (rName == aDispatchURL.Main || rName == aDispatchURL.Complete)
889             sendStatusChanged(rName, aEvent);
890     }
891 }
892 
StateChangedAtToolBoxControl(sal_uInt16 nSID,SfxItemState eState,const SfxPoolItem * pState)893 void SfxDispatchController_Impl::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
894 {
895     StateChanged( nSID, eState, pState, nullptr );
896 }
897 
898 namespace
899 {
900 using PayloadGetter_t = OString (*)(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent&,
901                                    const SfxPoolItem*);
902 
IsActivePayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)903 OString IsActivePayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
904                         const SfxPoolItem*)
905 {
906     bool bTemp = false;
907     aEvent.State >>= bTemp;
908     return aEvent.FeatureURL.Complete.toUtf8() + "=" + OString::boolean(bTemp);
909 }
910 
FontNamePayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)911 OString FontNamePayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
912                         const SfxPoolItem*)
913 {
914     css::awt::FontDescriptor aFontDesc;
915     aEvent.State >>= aFontDesc;
916     return aEvent.FeatureURL.Complete.toUtf8() + "=" + aFontDesc.Name.toUtf8();
917 }
918 
FontHeightPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)919 OString FontHeightPayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
920                           const SfxPoolItem*)
921 {
922     css::frame::status::FontHeight aFontHeight;
923     aEvent.State >>= aFontHeight;
924     return aEvent.FeatureURL.Complete.toUtf8() + "=" + OString::number(aFontHeight.Height);
925 }
926 
StyleApplyPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)927 OString StyleApplyPayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
928                           const SfxPoolItem*)
929 {
930     css::frame::status::Template aTemplate;
931     aEvent.State >>= aTemplate;
932     return aEvent.FeatureURL.Complete.toUtf8() + "=" + aTemplate.StyleName.toUtf8();
933 }
934 
ColorPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)935 OString ColorPayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
936                      const SfxPoolItem*)
937 {
938     sal_Int32 nColor = -1;
939     aEvent.State >>= nColor;
940     return aEvent.FeatureURL.Complete.toUtf8() + "=" + OString::number(nColor);
941 }
942 
UndoRedoPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem * pState)943 OString UndoRedoPayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
944                         const SfxPoolItem* pState)
945 {
946     if (aEvent.IsEnabled)
947         if (auto pUndoConflict = dynamic_cast<const SfxUInt32Item*>(pState);
948             !pUndoConflict || pUndoConflict->GetValue() == 0)
949             return aEvent.FeatureURL.Complete.toUtf8() + "=enabled";
950     return aEvent.FeatureURL.Complete.toUtf8() + "=disabled";
951 }
952 
EnabledPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)953 OString EnabledPayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
954                        const SfxPoolItem*)
955 {
956     return aEvent.FeatureURL.Complete.toUtf8()
957            + (aEvent.IsEnabled ? std::string_view("=enabled") : std::string_view("=disabled"));
958 }
959 
ParaDirectionPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)960 OString ParaDirectionPayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
961                              const SfxPoolItem*)
962 {
963     tools::JsonWriter aTree;
964     bool bTemp = false;
965     aEvent.State >>= bTemp;
966     aTree.put("commandName", aEvent.FeatureURL.Complete);
967     aTree.put("disabled", !aEvent.IsEnabled);
968     aTree.put("state", bTemp ? std::string_view("true") : std::string_view("false"));
969     return aTree.finishAndGetAsOString();
970 }
971 
Int32Payload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)972 OString Int32Payload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
973                      const SfxPoolItem*)
974 {
975     OStringBuffer aBuffer(aEvent.FeatureURL.Complete.toUtf8() + "=");
976     if (sal_Int32 aInt32; aEvent.IsEnabled && (aEvent.State >>= aInt32))
977         aBuffer.append(aInt32);
978     return aBuffer.makeStringAndClear();
979 }
980 
TransformPayload(sal_uInt16 nSID,SfxViewFrame * pViewFrame,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)981 OString TransformPayload(sal_uInt16 nSID, SfxViewFrame* pViewFrame,
982                          const css::frame::FeatureStateEvent& aEvent, const SfxPoolItem*)
983 {
984     if (aEvent.IsEnabled && pViewFrame->GetViewShell()->isLOKMobilePhone())
985     {
986         boost::property_tree::ptree aTree;
987         boost::property_tree::ptree aState;
988 
989         aTree.put("commandName", aEvent.FeatureURL.Complete);
990         pViewFrame->GetBindings().QueryControlState(nSID, aState);
991         aTree.add_child("state", aState);
992 
993         std::stringstream aStream;
994         boost::property_tree::write_json(aStream, aTree);
995         return OString(aStream.str());
996     }
997     return aEvent.FeatureURL.Complete.toUtf8() + "="; // Should an empty string be returned?
998 }
999 
StringPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)1000 OString StringPayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
1001                       const SfxPoolItem*)
1002 {
1003     OStringBuffer aBuffer(aEvent.FeatureURL.Complete.toUtf8() + "=");
1004     if (OUString aString; aEvent.IsEnabled && (aEvent.State >>= aString))
1005         aBuffer.append(aString.toUtf8());
1006     return aBuffer.makeStringAndClear();
1007 }
1008 
RowColSelCountPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)1009 OString RowColSelCountPayload(sal_uInt16, SfxViewFrame*,
1010                               const css::frame::FeatureStateEvent& aEvent, const SfxPoolItem*)
1011 {
1012     OUString aString;
1013     if (aEvent.IsEnabled)
1014         aEvent.State >>= aString;
1015     tools::JsonWriter aTree;
1016     aTree.put("commandName", aEvent.FeatureURL.Complete);
1017     aTree.put("locale", comphelper::LibreOfficeKit::getLocale().getBcp47());
1018     aTree.put("state", aString);
1019     return aTree.finishAndGetAsOString();
1020 }
1021 
StateTableCellPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem * pState)1022 OString StateTableCellPayload(sal_uInt16, SfxViewFrame*,
1023                               const css::frame::FeatureStateEvent& aEvent,
1024                               const SfxPoolItem* pState)
1025 {
1026     OStringBuffer aBuffer(aEvent.FeatureURL.Complete.toUtf8() + "=");
1027     if (aEvent.IsEnabled)
1028         if (const SfxStringItem* pSvxStatusItem = dynamic_cast<const SfxStringItem*>(pState))
1029             aBuffer.append(pSvxStatusItem->GetValue().toUtf8());
1030     return aBuffer.makeStringAndClear();
1031 }
1032 
BooleanPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)1033 OString BooleanPayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
1034                        const SfxPoolItem*)
1035 {
1036     OStringBuffer aBuffer(aEvent.FeatureURL.Complete.toUtf8() + "=");
1037     if (bool aBool; aEvent.IsEnabled && (aEvent.State >>= aBool))
1038         aBuffer.append(aBool);
1039     return aBuffer.makeStringAndClear();
1040 }
1041 
BooleanOrDisabledPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)1042 OString BooleanOrDisabledPayload(sal_uInt16, SfxViewFrame*,
1043                                  const css::frame::FeatureStateEvent& aEvent, const SfxPoolItem*)
1044 {
1045     OStringBuffer aBuffer(aEvent.FeatureURL.Complete.toUtf8() + "=");
1046     if (bool aBool; aEvent.IsEnabled && (aEvent.State >>= aBool))
1047         aBuffer.append(aBool);
1048     else
1049         aBuffer.append("disabled");
1050     return aBuffer.makeStringAndClear();
1051 }
1052 
PointPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)1053 OString PointPayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
1054                      const SfxPoolItem*)
1055 {
1056     OStringBuffer aBuffer(aEvent.FeatureURL.Complete.toUtf8() + "=");
1057     if (css::awt::Point aPoint; aEvent.IsEnabled && (aEvent.State >>= aPoint))
1058         aBuffer.append(OString::number(aPoint.X) + " / " + OString::number(aPoint.Y));
1059     return aBuffer.makeStringAndClear();
1060 }
1061 
SizePayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)1062 OString SizePayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
1063                     const SfxPoolItem*)
1064 {
1065     OStringBuffer aBuffer(aEvent.FeatureURL.Complete.toUtf8() + "=");
1066     if (css::awt::Size aSize; aEvent.IsEnabled && (aEvent.State >>= aSize))
1067         aBuffer.append(OString::number(aSize.Width) + " x " + OString::number(aSize.Height));
1068     return aBuffer.makeStringAndClear();
1069 }
1070 
StringOrStrSeqPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)1071 OString StringOrStrSeqPayload(sal_uInt16, SfxViewFrame*,
1072                               const css::frame::FeatureStateEvent& aEvent, const SfxPoolItem*)
1073 {
1074     OStringBuffer aBuffer(aEvent.FeatureURL.Complete.toUtf8() + "=");
1075     if (aEvent.IsEnabled)
1076     {
1077         if (OUString sValue; aEvent.State >>= sValue)
1078             aBuffer.append(sValue.toUtf8());
1079         else if (css::uno::Sequence<OUString> aSeq; aEvent.State >>= aSeq)
1080             aBuffer.append(aSeq[0].toUtf8());
1081     }
1082     return aBuffer.makeStringAndClear();
1083 }
1084 
StrSeqPayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)1085 OString StrSeqPayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
1086                       const SfxPoolItem*)
1087 {
1088     OString json;
1089     if (aEvent.IsEnabled)
1090     {
1091         if (css::uno::Sequence<OUString> aSeq; aEvent.State >>= aSeq)
1092         {
1093             tools::JsonWriter aTree;
1094             for (const auto& s : aSeq)
1095                 aTree.put(s.toUtf8(), "true");
1096             json = aTree.finishAndGetAsOString();
1097         }
1098     }
1099     return aEvent.FeatureURL.Complete.toUtf8() + "=" + json;
1100 }
1101 
TableSizePayload(sal_uInt16,SfxViewFrame *,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem *)1102 OString TableSizePayload(sal_uInt16, SfxViewFrame*, const css::frame::FeatureStateEvent& aEvent,
1103                          const SfxPoolItem*)
1104 {
1105     OStringBuffer aBuffer(aEvent.FeatureURL.Complete.toUtf8() + "=");
1106     if (sal_Int32 nValue; aEvent.State >>= nValue)
1107         aBuffer.append(o3tl::convert<double>(nValue, o3tl::Length::twip, o3tl::Length::in));
1108     return aBuffer.makeStringAndClear();
1109 }
1110 
1111 constexpr auto handlers = frozen::make_unordered_map<std::u16string_view, PayloadGetter_t>({
1112     { u"Bold", IsActivePayload },
1113     { u"CenterPara", IsActivePayload },
1114     { u"CharBackgroundExt", IsActivePayload },
1115     { u"ControlCodes", IsActivePayload },
1116     { u"DefaultBullet", IsActivePayload },
1117     { u"DefaultNumbering", IsActivePayload },
1118     { u"Italic", IsActivePayload },
1119     { u"JustifyPara", IsActivePayload },
1120     { u"LeftPara", IsActivePayload },
1121     { u"OutlineFont", IsActivePayload },
1122     { u"RightPara", IsActivePayload },
1123     { u"Shadowed", IsActivePayload },
1124     { u"SpellOnline", IsActivePayload },
1125     { u"OnlineAutoFormat", IsActivePayload },
1126     { u"SubScript", IsActivePayload },
1127     { u"SuperScript", IsActivePayload },
1128     { u"Strikeout", IsActivePayload },
1129     { u"Underline", IsActivePayload },
1130     { u"ModifiedStatus", IsActivePayload },
1131     { u"TrackChanges", IsActivePayload },
1132     { u"ShowTrackedChanges", IsActivePayload },
1133     { u"AlignLeft", IsActivePayload },
1134     { u"AlignHorizontalCenter", IsActivePayload },
1135     { u"AlignRight", IsActivePayload },
1136     { u"DocumentRepair", IsActivePayload },
1137     { u"ObjectAlignLeft", IsActivePayload },
1138     { u"ObjectAlignRight", IsActivePayload },
1139     { u"AlignCenter", IsActivePayload },
1140     { u"AlignUp", IsActivePayload },
1141     { u"AlignMiddle", IsActivePayload },
1142     { u"AlignDown", IsActivePayload },
1143     { u"TraceChangeMode", IsActivePayload },
1144     { u"FormatPaintbrush", IsActivePayload },
1145     { u"FreezePanes", IsActivePayload },
1146     { u"Sidebar", IsActivePayload },
1147     { u"SpacePara1", IsActivePayload },
1148     { u"SpacePara15", IsActivePayload },
1149     { u"SpacePara2", IsActivePayload },
1150     { u"DataFilterAutoFilter", IsActivePayload },
1151     { u"CellProtection", IsActivePayload },
1152     { u"NormalMultiPaneGUI", IsActivePayload },
1153     { u"NotesMode", IsActivePayload },
1154     { u"SlideMasterPage", IsActivePayload },
1155 
1156     { u"CharFontName", FontNamePayload },
1157 
1158     { u"FontHeight", FontHeightPayload },
1159 
1160     { u"StyleApply", StyleApplyPayload },
1161 
1162     { u"BackColor", ColorPayload },
1163     { u"BackgroundColor", ColorPayload },
1164     { u"CharBackColor", ColorPayload },
1165     { u"Color", ColorPayload },
1166     { u"FontColor", ColorPayload },
1167     { u"FrameLineColor", ColorPayload },
1168     { u"GlowColor", ColorPayload },
1169 
1170     { u"Undo", UndoRedoPayload },
1171     { u"Redo", UndoRedoPayload },
1172 
1173     { u"Cut", EnabledPayload },
1174     { u"Copy", EnabledPayload },
1175     { u"Paste", EnabledPayload },
1176     { u"SelectAll", EnabledPayload },
1177     { u"InsertAnnotation", EnabledPayload },
1178     { u"DeleteAnnotation", EnabledPayload },
1179     { u"ResolveAnnotation", EnabledPayload },
1180     { u"ResolveAnnotationThread", EnabledPayload },
1181     { u"InsertRowsBefore", EnabledPayload },
1182     { u"InsertRowsAfter", EnabledPayload },
1183     { u"InsertColumnsBefore", EnabledPayload },
1184     { u"InsertColumnsAfter", EnabledPayload },
1185     { u"NameGroup", EnabledPayload },
1186     { u"ObjectTitleDescription", EnabledPayload },
1187     { u"MergeCells", EnabledPayload },
1188     { u"InsertObjectChart", EnabledPayload },
1189     { u"InsertSection", EnabledPayload },
1190     { u"InsertPagebreak", EnabledPayload },
1191     { u"InsertColumnBreak", EnabledPayload },
1192     { u"HyperlinkDialog", EnabledPayload },
1193     { u"InsertSymbol", EnabledPayload },
1194     { u"InsertPage", EnabledPayload },
1195     { u"DeletePage", EnabledPayload },
1196     { u"DuplicatePage", EnabledPayload },
1197     { u"DeleteRows", EnabledPayload },
1198     { u"DeleteColumns", EnabledPayload },
1199     { u"DeleteTable", EnabledPayload },
1200     { u"SelectTable", EnabledPayload },
1201     { u"EntireRow", EnabledPayload },
1202     { u"EntireColumn", EnabledPayload },
1203     { u"EntireCell", EnabledPayload },
1204     { u"SortAscending", EnabledPayload },
1205     { u"SortDescending", EnabledPayload },
1206     { u"AcceptAllTrackedChanges", EnabledPayload },
1207     { u"RejectAllTrackedChanges", EnabledPayload },
1208     { u"AcceptTrackedChange", EnabledPayload },
1209     { u"RejectTrackedChange", EnabledPayload },
1210     { u"AcceptTrackedChangeToNext", EnabledPayload },
1211     { u"RejectTrackedChangeToNext", EnabledPayload },
1212     { u"NextTrackedChange", EnabledPayload },
1213     { u"PreviousTrackedChange", EnabledPayload },
1214     { u"FormatGroup", EnabledPayload },
1215     { u"ObjectBackOne", EnabledPayload },
1216     { u"SendToBack", EnabledPayload },
1217     { u"ObjectForwardOne", EnabledPayload },
1218     { u"BringToFront", EnabledPayload },
1219     { u"WrapRight", EnabledPayload },
1220     { u"WrapThrough", EnabledPayload },
1221     { u"WrapLeft", EnabledPayload },
1222     { u"WrapIdeal", EnabledPayload },
1223     { u"WrapOn", EnabledPayload },
1224     { u"WrapOff", EnabledPayload },
1225     { u"UpdateCurIndex", EnabledPayload },
1226     { u"InsertCaptionDialog", EnabledPayload },
1227     { u"SplitTable", EnabledPayload },
1228     { u"SplitCell", EnabledPayload },
1229     { u"DeleteNote", EnabledPayload },
1230     { u"AcceptChanges", EnabledPayload },
1231     { u"SetDefault", EnabledPayload },
1232     { u"ParaspaceIncrease", EnabledPayload },
1233     { u"ParaspaceDecrease", EnabledPayload },
1234     { u"TableDialog", EnabledPayload },
1235     { u"FormatCellDialog", EnabledPayload },
1236     { u"FontDialog", EnabledPayload },
1237     { u"ParagraphDialog", EnabledPayload },
1238     { u"OutlineBullet", EnabledPayload },
1239     { u"InsertIndexesEntry", EnabledPayload },
1240     { u"TransformDialog", EnabledPayload },
1241     { u"EditRegion", EnabledPayload },
1242     { u"ThesaurusDialog", EnabledPayload },
1243     { u"OutlineRight", EnabledPayload },
1244     { u"OutlineLeft", EnabledPayload },
1245     { u"OutlineDown", EnabledPayload },
1246     { u"OutlineUp", EnabledPayload },
1247     { u"FormatArea", EnabledPayload },
1248     { u"FormatLine", EnabledPayload },
1249     { u"FormatColumns", EnabledPayload },
1250     { u"Watermark", EnabledPayload },
1251     { u"InsertBreak", EnabledPayload },
1252     { u"InsertEndnote", EnabledPayload },
1253     { u"InsertFootnote", EnabledPayload },
1254     { u"InsertReferenceField", EnabledPayload },
1255     { u"InsertBookmark", EnabledPayload },
1256     { u"InsertAuthoritiesEntry", EnabledPayload },
1257     { u"InsertMultiIndex", EnabledPayload },
1258     { u"InsertField", EnabledPayload },
1259     { u"PageNumberWizard", EnabledPayload },
1260     { u"InsertPageNumberField", EnabledPayload },
1261     { u"InsertPageCountField", EnabledPayload },
1262     { u"InsertDateField", EnabledPayload },
1263     { u"InsertTitleField", EnabledPayload },
1264     { u"InsertFieldCtrl", EnabledPayload },
1265     { u"CharmapControl", EnabledPayload },
1266     { u"EnterGroup", EnabledPayload },
1267     { u"LeaveGroup", EnabledPayload },
1268     { u"Combine", EnabledPayload },
1269     { u"Merge", EnabledPayload },
1270     { u"Dismantle", EnabledPayload },
1271     { u"Substract", EnabledPayload },
1272     { u"DistributeSelection", EnabledPayload },
1273     { u"Intersect", EnabledPayload },
1274     { u"ResetAttributes", EnabledPayload },
1275     { u"IncrementIndent", EnabledPayload },
1276     { u"DecrementIndent", EnabledPayload },
1277     { u"EditHeaderAndFooter", EnabledPayload },
1278     { u"InsertSparkline", EnabledPayload },
1279     { u"DeleteSparkline", EnabledPayload },
1280     { u"DeleteSparklineGroup", EnabledPayload },
1281     { u"EditSparklineGroup", EnabledPayload },
1282     { u"EditSparkline", EnabledPayload },
1283     { u"GroupSparklines", EnabledPayload },
1284     { u"UngroupSparklines", EnabledPayload },
1285     { u"FormatSparklineMenu", EnabledPayload },
1286     { u"DataDataPilotRun", EnabledPayload },
1287     { u"RecalcPivotTable", EnabledPayload },
1288     { u"DeletePivotTable", EnabledPayload },
1289     { u"NumberFormatDecDecimals", EnabledPayload },
1290     { u"NumberFormatIncDecimals", EnabledPayload },
1291     { u"Protect", EnabledPayload },
1292     { u"UnsetCellsReadOnly", EnabledPayload },
1293     { u"ContentControlProperties", EnabledPayload },
1294     { u"InsertCheckboxContentControl", EnabledPayload },
1295     { u"InsertContentControl", EnabledPayload },
1296     { u"InsertDateContentControl", EnabledPayload },
1297     { u"InsertDropdownContentControl", EnabledPayload },
1298     { u"InsertPlainTextContentControl", EnabledPayload },
1299     { u"InsertPictureContentControl", EnabledPayload },
1300     { u"ChangeBezier", EnabledPayload },
1301 
1302     { u"ParaLeftToRight", ParaDirectionPayload },
1303     { u"ParaRightToLeft", ParaDirectionPayload },
1304 
1305     { u"AssignLayout", Int32Payload },
1306     { u"StatusSelectionMode", Int32Payload },
1307     { u"Signature", Int32Payload },
1308     { u"SelectionMode", Int32Payload },
1309     { u"StatusBarFunc", Int32Payload },
1310 
1311     { u"TransformPosX", TransformPayload },
1312     { u"TransformPosY", TransformPayload },
1313     { u"TransformWidth", TransformPayload },
1314     { u"TransformHeight", TransformPayload },
1315 
1316     { u"StatusDocPos", StringPayload },
1317     { u"StatusPageStyle", StringPayload },
1318     { u"StateWordCount", StringPayload },
1319     { u"PageStyleName", StringPayload },
1320     { u"PageStatus", StringPayload },
1321     { u"LayoutStatus", StringPayload },
1322     { u"Scale", StringPayload },
1323     { u"Context", StringPayload },
1324 
1325     { u"RowColSelCount", RowColSelCountPayload },
1326 
1327     { u"StateTableCell", StateTableCellPayload },
1328 
1329     { u"InsertMode", BooleanPayload },
1330     { u"WrapText", BooleanPayload },
1331     { u"NumberFormatCurrency", BooleanPayload },
1332     { u"NumberFormatPercent", BooleanPayload },
1333     { u"NumberFormatDecimal", BooleanPayload },
1334     { u"NumberFormatDate", BooleanPayload },
1335     { u"ShowResolvedAnnotations", BooleanPayload },
1336 
1337     { u"ToggleMergeCells", BooleanOrDisabledPayload },
1338     { u"SheetRightToLeft", BooleanOrDisabledPayload },
1339     { u"ToggleSheetGrid", BooleanOrDisabledPayload },
1340 
1341     { u"Position", PointPayload },
1342     { u"FreezePanesColumn", PointPayload },
1343     { u"FreezePanesRow", PointPayload },
1344 
1345     { u"Size", SizePayload },
1346 
1347     { u"LanguageStatus", StringOrStrSeqPayload },
1348     { u"StatePageNumber", StringOrStrSeqPayload },
1349 
1350     { u"InsertPageHeader", StrSeqPayload },
1351     { u"InsertPageFooter", StrSeqPayload },
1352 
1353     { u"TableColumWidth", TableSizePayload },
1354     { u"TableRowHeight", TableSizePayload },
1355 });
1356 }
1357 
InterceptLOKStateChangeEvent(sal_uInt16 nSID,SfxViewFrame * pViewFrame,const css::frame::FeatureStateEvent & aEvent,const SfxPoolItem * pState)1358 static void InterceptLOKStateChangeEvent(sal_uInt16 nSID, SfxViewFrame* pViewFrame, const css::frame::FeatureStateEvent& aEvent, const SfxPoolItem* pState)
1359 {
1360     const SfxViewShell* pViewShell = pViewFrame->GetViewShell();
1361     if (!comphelper::LibreOfficeKit::isActive() || !pViewShell)
1362         return;
1363 
1364     auto handler = handlers.find(aEvent.FeatureURL.Path);
1365     if (handler == handlers.end())
1366     {
1367         // Try to send JSON state version
1368         SfxLokHelper::sendUnoStatus(pViewShell, pState);
1369 
1370         return;
1371     }
1372 
1373     pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
1374                                            handler->second(nSID, pViewFrame, aEvent, pState));
1375 }
1376 
1377 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1378