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