summaryrefslogtreecommitdiffstats
path: root/framework/source
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /framework/source
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--framework/source/accelerators/acceleratorcache.cxx120
-rw-r--r--framework/source/accelerators/acceleratorconfiguration.cxx1326
-rw-r--r--framework/source/accelerators/documentacceleratorconfiguration.cxx196
-rw-r--r--framework/source/accelerators/globalacceleratorconfiguration.cxx124
-rw-r--r--framework/source/accelerators/keymapping.cxx208
-rw-r--r--framework/source/accelerators/moduleacceleratorconfiguration.cxx157
-rw-r--r--framework/source/accelerators/presethandler.cxx727
-rw-r--r--framework/source/accelerators/storageholder.cxx447
-rw-r--r--framework/source/classes/framecontainer.cxx293
-rw-r--r--framework/source/classes/taskcreator.cxx110
-rw-r--r--framework/source/dispatch/closedispatcher.cxx612
-rw-r--r--framework/source/dispatch/dispatchdisabler.cxx170
-rw-r--r--framework/source/dispatch/dispatchinformationprovider.cxx130
-rw-r--r--framework/source/dispatch/dispatchprovider.cxx585
-rw-r--r--framework/source/dispatch/interceptionhelper.cxx270
-rw-r--r--framework/source/dispatch/isstartmoduledispatch.hxx32
-rw-r--r--framework/source/dispatch/loaddispatcher.cxx148
-rw-r--r--framework/source/dispatch/mailtodispatcher.cxx233
-rw-r--r--framework/source/dispatch/oxt_handler.cxx175
-rw-r--r--framework/source/dispatch/popupmenudispatcher.cxx269
-rw-r--r--framework/source/dispatch/servicehandler.cxx260
-rw-r--r--framework/source/dispatch/startmoduledispatcher.cxx147
-rw-r--r--framework/source/dispatch/systemexec.cxx156
-rw-r--r--framework/source/dispatch/windowcommanddispatch.cxx158
-rw-r--r--framework/source/fwe/classes/actiontriggercontainer.cxx134
-rw-r--r--framework/source/fwe/classes/actiontriggerpropertyset.cxx371
-rw-r--r--framework/source/fwe/classes/actiontriggerseparatorpropertyset.cxx245
-rw-r--r--framework/source/fwe/classes/addonmenu.cxx299
-rw-r--r--framework/source/fwe/classes/addonsoptions.cxx1958
-rw-r--r--framework/source/fwe/classes/framelistanalyzer.cxx260
-rw-r--r--framework/source/fwe/classes/fwkresid.cxx24
-rw-r--r--framework/source/fwe/classes/rootactiontriggercontainer.cxx251
-rw-r--r--framework/source/fwe/classes/sfxhelperfunctions.cxx181
-rw-r--r--framework/source/fwe/dispatch/interaction.cxx225
-rw-r--r--framework/source/fwe/helper/actiontriggerhelper.cxx376
-rw-r--r--framework/source/fwe/helper/configimporter.cxx74
-rw-r--r--framework/source/fwe/helper/documentundoguard.cxx223
-rw-r--r--framework/source/fwe/helper/propertysetcontainer.cxx158
-rw-r--r--framework/source/fwe/helper/titlehelper.cxx685
-rw-r--r--framework/source/fwe/helper/undomanagerhelper.cxx1056
-rw-r--r--framework/source/fwe/xml/menuconfiguration.cxx156
-rw-r--r--framework/source/fwe/xml/menudocumenthandler.cxx897
-rw-r--r--framework/source/fwe/xml/saxnamespacefilter.cxx163
-rw-r--r--framework/source/fwe/xml/statusbarconfiguration.cxx105
-rw-r--r--framework/source/fwe/xml/statusbardocumenthandler.cxx632
-rw-r--r--framework/source/fwe/xml/toolboxconfiguration.cxx106
-rw-r--r--framework/source/fwe/xml/toolboxdocumenthandler.cxx734
-rw-r--r--framework/source/fwe/xml/xmlnamespaces.cxx152
-rw-r--r--framework/source/fwi/classes/converter.cxx116
-rw-r--r--framework/source/fwi/classes/protocolhandlercache.cxx257
-rw-r--r--framework/source/fwi/helper/mischelper.cxx154
-rw-r--r--framework/source/fwi/helper/shareablemutex.cxx49
-rw-r--r--framework/source/fwi/jobs/configaccess.cxx184
-rw-r--r--framework/source/fwi/threadhelp/transactionmanager.cxx219
-rw-r--r--framework/source/fwi/uielement/constitemcontainer.cxx288
-rw-r--r--framework/source/fwi/uielement/itemcontainer.cxx212
-rw-r--r--framework/source/fwi/uielement/rootitemcontainer.cxx313
-rw-r--r--framework/source/helper/dockingareadefaultacceptor.cxx129
-rw-r--r--framework/source/helper/ocomponentaccess.cxx167
-rw-r--r--framework/source/helper/ocomponentenumeration.cxx117
-rw-r--r--framework/source/helper/oframes.cxx370
-rw-r--r--framework/source/helper/persistentwindowstate.cxx267
-rw-r--r--framework/source/helper/statusindicator.cxx128
-rw-r--r--framework/source/helper/statusindicatorfactory.cxx577
-rw-r--r--framework/source/helper/tagwindowasmodified.cxx146
-rw-r--r--framework/source/helper/titlebarupdate.cxx316
-rw-r--r--framework/source/helper/uiconfigelementwrapperbase.cxx481
-rw-r--r--framework/source/helper/uielementwrapperbase.cxx203
-rw-r--r--framework/source/helper/vclstatusindicator.cxx140
-rw-r--r--framework/source/helper/wakeupthread.cxx60
-rw-r--r--framework/source/inc/accelerators/acceleratorcache.hxx115
-rw-r--r--framework/source/inc/accelerators/acceleratorconfiguration.hxx312
-rw-r--r--framework/source/inc/accelerators/keymapping.hxx126
-rw-r--r--framework/source/inc/accelerators/presethandler.hxx378
-rw-r--r--framework/source/inc/accelerators/storageholder.hxx182
-rw-r--r--framework/source/inc/dispatch/dispatchdisabler.hxx96
-rw-r--r--framework/source/inc/dispatch/loaddispatcher.hxx120
-rw-r--r--framework/source/inc/dispatch/windowcommanddispatch.hxx109
-rw-r--r--framework/source/inc/loadenv/actionlockguard.hxx141
-rw-r--r--framework/source/inc/loadenv/loadenv.hxx540
-rw-r--r--framework/source/inc/loadenv/loadenvexception.hxx87
-rw-r--r--framework/source/inc/loadenv/targethelper.hxx90
-rw-r--r--framework/source/inc/pattern/frame.hxx79
-rw-r--r--framework/source/inc/pattern/window.hxx62
-rw-r--r--framework/source/interaction/quietinteraction.cxx129
-rw-r--r--framework/source/jobs/helponstartup.cxx336
-rw-r--r--framework/source/jobs/job.cxx857
-rw-r--r--framework/source/jobs/jobdata.cxx563
-rw-r--r--framework/source/jobs/jobdispatch.cxx496
-rw-r--r--framework/source/jobs/jobexecutor.cxx399
-rw-r--r--framework/source/jobs/jobresult.cxx192
-rw-r--r--framework/source/jobs/joburl.cxx253
-rw-r--r--framework/source/jobs/shelljob.cxx168
-rw-r--r--framework/source/layoutmanager/helpers.cxx340
-rw-r--r--framework/source/layoutmanager/helpers.hxx67
-rw-r--r--framework/source/layoutmanager/layoutmanager.cxx3079
-rw-r--r--framework/source/layoutmanager/toolbarlayoutmanager.cxx4116
-rw-r--r--framework/source/layoutmanager/toolbarlayoutmanager.hxx283
-rw-r--r--framework/source/layoutmanager/uielement.cxx99
-rw-r--r--framework/source/loadenv/loadenv.cxx1808
-rw-r--r--framework/source/loadenv/targethelper.cxx64
-rw-r--r--framework/source/recording/dispatchrecorder.cxx439
-rw-r--r--framework/source/recording/dispatchrecordersupplier.cxx161
-rw-r--r--framework/source/services/ContextChangeEventMultiplexer.cxx366
-rw-r--r--framework/source/services/autorecovery.cxx4243
-rw-r--r--framework/source/services/desktop.cxx1775
-rw-r--r--framework/source/services/dispatchhelper.cxx218
-rw-r--r--framework/source/services/frame.cxx3340
-rw-r--r--framework/source/services/mediatypedetectionhelper.cxx92
-rw-r--r--framework/source/services/modulemanager.cxx350
-rw-r--r--framework/source/services/pathsettings.cxx1422
-rw-r--r--framework/source/services/sessionlistener.cxx413
-rw-r--r--framework/source/services/substitutepathvars.cxx695
-rw-r--r--framework/source/services/taskcreatorsrv.cxx357
-rw-r--r--framework/source/services/uriabbreviation.cxx75
-rw-r--r--framework/source/services/urltransformer.cxx310
-rw-r--r--framework/source/uiconfiguration/CommandImageResolver.cxx152
-rw-r--r--framework/source/uiconfiguration/CommandImageResolver.hxx53
-rw-r--r--framework/source/uiconfiguration/ImageList.cxx201
-rw-r--r--framework/source/uiconfiguration/ImageList.hxx78
-rw-r--r--framework/source/uiconfiguration/globalsettings.cxx262
-rw-r--r--framework/source/uiconfiguration/graphicnameaccess.cxx80
-rw-r--r--framework/source/uiconfiguration/imagemanager.cxx173
-rw-r--r--framework/source/uiconfiguration/imagemanagerimpl.cxx1205
-rw-r--r--framework/source/uiconfiguration/imagemanagerimpl.hxx187
-rw-r--r--framework/source/uiconfiguration/moduleuicfgsupplier.cxx180
-rw-r--r--framework/source/uiconfiguration/moduleuiconfigurationmanager.cxx1655
-rw-r--r--framework/source/uiconfiguration/uicategorydescription.cxx395
-rw-r--r--framework/source/uiconfiguration/uiconfigurationmanager.cxx1377
-rw-r--r--framework/source/uiconfiguration/windowstateconfiguration.cxx1390
-rw-r--r--framework/source/uielement/FixedImageToolbarController.cxx139
-rw-r--r--framework/source/uielement/FixedTextToolbarController.cxx127
-rw-r--r--framework/source/uielement/addonstoolbarwrapper.cxx171
-rw-r--r--framework/source/uielement/buttontoolbarcontroller.cxx280
-rw-r--r--framework/source/uielement/comboboxtoolbarcontroller.cxx333
-rw-r--r--framework/source/uielement/complextoolbarcontroller.cxx326
-rw-r--r--framework/source/uielement/controlmenucontroller.cxx327
-rw-r--r--framework/source/uielement/dropdownboxtoolbarcontroller.cxx282
-rw-r--r--framework/source/uielement/edittoolbarcontroller.cxx217
-rw-r--r--framework/source/uielement/fontmenucontroller.cxx218
-rw-r--r--framework/source/uielement/fontsizemenucontroller.cxx285
-rw-r--r--framework/source/uielement/footermenucontroller.cxx73
-rw-r--r--framework/source/uielement/genericstatusbarcontroller.cxx157
-rw-r--r--framework/source/uielement/generictoolbarcontroller.cxx323
-rw-r--r--framework/source/uielement/headermenucontroller.cxx237
-rw-r--r--framework/source/uielement/imagebuttontoolbarcontroller.cxx148
-rw-r--r--framework/source/uielement/langselectionmenucontroller.cxx294
-rw-r--r--framework/source/uielement/langselectionstatusbarcontroller.cxx359
-rw-r--r--framework/source/uielement/macrosmenucontroller.cxx170
-rw-r--r--framework/source/uielement/menubarmanager.cxx1572
-rw-r--r--framework/source/uielement/menubarmerger.cxx430
-rw-r--r--framework/source/uielement/menubarwrapper.cxx352
-rw-r--r--framework/source/uielement/newmenucontroller.cxx513
-rw-r--r--framework/source/uielement/objectmenucontroller.cxx136
-rw-r--r--framework/source/uielement/popuptoolbarcontroller.cxx799
-rw-r--r--framework/source/uielement/progressbarwrapper.cxx310
-rw-r--r--framework/source/uielement/recentfilesmenucontroller.cxx411
-rw-r--r--framework/source/uielement/resourcemenucontroller.cxx584
-rw-r--r--framework/source/uielement/spinfieldtoolbarcontroller.cxx452
-rw-r--r--framework/source/uielement/statusbar.cxx89
-rw-r--r--framework/source/uielement/statusbaritem.cxx236
-rw-r--r--framework/source/uielement/statusbarmanager.cxx656
-rw-r--r--framework/source/uielement/statusbarmerger.cxx237
-rw-r--r--framework/source/uielement/statusbarwrapper.cxx171
-rw-r--r--framework/source/uielement/statusindicatorinterfacewrapper.cxx102
-rw-r--r--framework/source/uielement/styletoolbarcontroller.cxx243
-rw-r--r--framework/source/uielement/subtoolbarcontroller.cxx547
-rw-r--r--framework/source/uielement/thesaurusmenucontroller.cxx185
-rw-r--r--framework/source/uielement/togglebuttontoolbarcontroller.cxx270
-rw-r--r--framework/source/uielement/toolbarmanager.cxx2365
-rw-r--r--framework/source/uielement/toolbarmerger.cxx645
-rw-r--r--framework/source/uielement/toolbarmodemenucontroller.cxx294
-rw-r--r--framework/source/uielement/toolbarsmenucontroller.cxx794
-rw-r--r--framework/source/uielement/toolbarwrapper.cxx328
-rw-r--r--framework/source/uielement/uicommanddescription.cxx725
-rw-r--r--framework/source/uifactory/addonstoolbarfactory.cxx206
-rw-r--r--framework/source/uifactory/factoryconfiguration.cxx289
-rw-r--r--framework/source/uifactory/menubarfactory.cxx172
-rw-r--r--framework/source/uifactory/statusbarfactory.cxx84
-rw-r--r--framework/source/uifactory/toolbarfactory.cxx84
-rw-r--r--framework/source/uifactory/uicontrollerfactory.cxx341
-rw-r--r--framework/source/uifactory/uielementfactorymanager.cxx553
-rw-r--r--framework/source/uifactory/windowcontentfactorymanager.cxx204
-rw-r--r--framework/source/xml/acceleratorconfigurationreader.cxx258
-rw-r--r--framework/source/xml/acceleratorconfigurationwriter.cxx123
-rw-r--r--framework/source/xml/imagesconfiguration.cxx104
-rw-r--r--framework/source/xml/imagesdocumenthandler.cxx372
187 files changed, 79443 insertions, 0 deletions
diff --git a/framework/source/accelerators/acceleratorcache.cxx b/framework/source/accelerators/acceleratorcache.cxx
new file mode 100644
index 000000000..98596a895
--- /dev/null
+++ b/framework/source/accelerators/acceleratorcache.cxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <accelerators/acceleratorcache.hxx>
+
+#include <com/sun/star/container/NoSuchElementException.hpp>
+
+#include <vcl/svapp.hxx>
+
+namespace framework
+{
+bool AcceleratorCache::hasKey(const css::awt::KeyEvent& aKey) const
+{
+ SolarMutexGuard g;
+ return (m_lKey2Commands.find(aKey) != m_lKey2Commands.end());
+}
+
+bool AcceleratorCache::hasCommand(const OUString& sCommand) const
+{
+ SolarMutexGuard g;
+ return (m_lCommand2Keys.find(sCommand) != m_lCommand2Keys.end());
+}
+
+AcceleratorCache::TKeyList AcceleratorCache::getAllKeys() const
+{
+ SolarMutexGuard g;
+ TKeyList lKeys;
+ lKeys.reserve(m_lKey2Commands.size());
+
+ for (auto const& key2Command : m_lKey2Commands)
+ {
+ lKeys.push_back(key2Command.first);
+ }
+
+ return lKeys;
+}
+
+void AcceleratorCache::setKeyCommandPair(const css::awt::KeyEvent& aKey, const OUString& sCommand)
+{
+ SolarMutexGuard g;
+
+ // register command for the specified key
+ m_lKey2Commands[aKey] = sCommand;
+
+ // update optimized structure to bind multiple keys to one command
+ TKeyList& rKeyList = m_lCommand2Keys[sCommand];
+ rKeyList.push_back(aKey);
+}
+
+AcceleratorCache::TKeyList AcceleratorCache::getKeysByCommand(const OUString& sCommand) const
+{
+ SolarMutexGuard g;
+ TCommand2Keys::const_iterator pCommand = m_lCommand2Keys.find(sCommand);
+ if (pCommand == m_lCommand2Keys.end())
+ throw css::container::NoSuchElementException();
+ return pCommand->second;
+}
+
+OUString AcceleratorCache::getCommandByKey(const css::awt::KeyEvent& aKey) const
+{
+ SolarMutexGuard g;
+ TKey2Commands::const_iterator pKey = m_lKey2Commands.find(aKey);
+ if (pKey == m_lKey2Commands.end())
+ throw css::container::NoSuchElementException();
+ return pKey->second;
+}
+
+void AcceleratorCache::removeKey(const css::awt::KeyEvent& aKey)
+{
+ SolarMutexGuard g;
+
+ // check if key exists
+ TKey2Commands::const_iterator pKey = m_lKey2Commands.find(aKey);
+ if (pKey == m_lKey2Commands.end())
+ return;
+
+ // get its registered command
+ // Because we must know its place inside the optimized
+ // structure, which bind keys to commands, too!
+ OUString sCommand = pKey->second;
+ pKey = m_lKey2Commands.end(); // nobody should use an undefined value .-)
+
+ // remove key from primary list
+ m_lKey2Commands.erase(aKey);
+
+ // remove key from optimized command list
+ m_lCommand2Keys.erase(sCommand);
+}
+
+void AcceleratorCache::removeCommand(const OUString& sCommand)
+{
+ SolarMutexGuard g;
+
+ const TKeyList& lKeys = getKeysByCommand(sCommand);
+ for (auto const& lKey : lKeys)
+ {
+ removeKey(lKey);
+ }
+ m_lCommand2Keys.erase(sCommand);
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/accelerators/acceleratorconfiguration.cxx b/framework/source/accelerators/acceleratorconfiguration.cxx
new file mode 100644
index 000000000..ad08f375a
--- /dev/null
+++ b/framework/source/accelerators/acceleratorconfiguration.cxx
@@ -0,0 +1,1326 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <utility>
+
+#include <accelerators/acceleratorconfiguration.hxx>
+#include <accelerators/keymapping.hxx>
+#include <accelerators/presethandler.hxx>
+
+#include <xml/saxnamespacefilter.hxx>
+#include <xml/acceleratorconfigurationreader.hxx>
+#include <xml/acceleratorconfigurationwriter.hxx>
+
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+
+#include <vcl/svapp.hxx>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/awt/KeyEvent.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <comphelper/configurationhelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <officecfg/Setup.hxx>
+#include <unotools/configpaths.hxx>
+#include <svtools/acceleratorexecute.hxx>
+#include <sal/log.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <o3tl/string_view.hxx>
+
+constexpr OUStringLiteral PRESET_DEFAULT = u"default";
+constexpr OUStringLiteral TARGET_CURRENT = u"current";
+
+namespace framework
+{
+ constexpr OUStringLiteral CFG_ENTRY_SECONDARY = u"SecondaryKeys";
+ constexpr OUStringLiteral CFG_PROP_COMMAND = u"Command";
+
+ static OUString lcl_getKeyString(const css::awt::KeyEvent& aKeyEvent)
+ {
+ const sal_Int32 nBeginIndex = 4; // "KEY_" is the prefix of an identifier...
+ OUString sKey(KeyMapping::get().mapCodeToIdentifier(aKeyEvent.KeyCode));
+ if (sKey.getLength() < nBeginIndex) // dead key
+ return OUString();
+ OUStringBuffer sKeyBuffer(sKey.subView(nBeginIndex));
+
+ if ( (aKeyEvent.Modifiers & css::awt::KeyModifier::SHIFT) == css::awt::KeyModifier::SHIFT )
+ sKeyBuffer.append("_SHIFT");
+ if ( (aKeyEvent.Modifiers & css::awt::KeyModifier::MOD1 ) == css::awt::KeyModifier::MOD1 )
+ sKeyBuffer.append("_MOD1");
+ if ( (aKeyEvent.Modifiers & css::awt::KeyModifier::MOD2 ) == css::awt::KeyModifier::MOD2 )
+ sKeyBuffer.append("_MOD2");
+ if ( (aKeyEvent.Modifiers & css::awt::KeyModifier::MOD3 ) == css::awt::KeyModifier::MOD3 )
+ sKeyBuffer.append("_MOD3");
+
+ return sKeyBuffer.makeStringAndClear();
+ }
+
+XMLBasedAcceleratorConfiguration::XMLBasedAcceleratorConfiguration(const css::uno::Reference< css::uno::XComponentContext >& xContext)
+ : m_xContext (xContext )
+ , m_aPresetHandler(xContext )
+{
+}
+
+XMLBasedAcceleratorConfiguration::~XMLBasedAcceleratorConfiguration()
+{
+ SAL_WARN_IF(m_pWriteCache, "fwk.accelerators", "XMLBasedAcceleratorConfiguration::~XMLBasedAcceleratorConfiguration(): Changes not flushed. Ignore it ...");
+}
+
+css::uno::Sequence< css::awt::KeyEvent > SAL_CALL XMLBasedAcceleratorConfiguration::getAllKeyEvents()
+{
+ SolarMutexGuard g;
+ AcceleratorCache& rCache = impl_getCFG();
+ AcceleratorCache::TKeyList lKeys = rCache.getAllKeys();
+ return comphelper::containerToSequence(lKeys);
+}
+
+OUString SAL_CALL XMLBasedAcceleratorConfiguration::getCommandByKeyEvent(const css::awt::KeyEvent& aKeyEvent)
+{
+ SolarMutexGuard g;
+ AcceleratorCache& rCache = impl_getCFG();
+ if (!rCache.hasKey(aKeyEvent))
+ throw css::container::NoSuchElementException(
+ OUString(),
+ static_cast< ::cppu::OWeakObject* >(this));
+ return rCache.getCommandByKey(aKeyEvent);
+}
+
+void SAL_CALL XMLBasedAcceleratorConfiguration::setKeyEvent(const css::awt::KeyEvent& aKeyEvent,
+ const OUString& sCommand )
+{
+ if (
+ (aKeyEvent.KeyCode == 0) &&
+ (aKeyEvent.KeyChar == 0) &&
+ (aKeyEvent.KeyFunc == 0) &&
+ (aKeyEvent.Modifiers == 0)
+ )
+ throw css::lang::IllegalArgumentException(
+ "Such key event seems not to be supported by any operating system.",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 0);
+
+ if (sCommand.isEmpty())
+ throw css::lang::IllegalArgumentException(
+ "Empty command strings are not allowed here.",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 1);
+
+ SolarMutexGuard g;
+ AcceleratorCache& rCache = impl_getCFG(true); // sal_True => force getting of a writeable cache!
+ rCache.setKeyCommandPair(aKeyEvent, sCommand);
+}
+
+void SAL_CALL XMLBasedAcceleratorConfiguration::removeKeyEvent(const css::awt::KeyEvent& aKeyEvent)
+{
+ SolarMutexGuard g;
+ AcceleratorCache& rCache = impl_getCFG(true); // true => force using of a writeable cache
+ if (!rCache.hasKey(aKeyEvent))
+ throw css::container::NoSuchElementException(
+ OUString(),
+ static_cast< ::cppu::OWeakObject* >(this));
+ rCache.removeKey(aKeyEvent);
+}
+
+css::uno::Sequence< css::awt::KeyEvent > SAL_CALL XMLBasedAcceleratorConfiguration::getKeyEventsByCommand(const OUString& sCommand)
+{
+ if (sCommand.isEmpty())
+ throw css::lang::IllegalArgumentException(
+ "Empty command strings are not allowed here.",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 1);
+
+ SolarMutexGuard g;
+ AcceleratorCache& rCache = impl_getCFG();
+ if (!rCache.hasCommand(sCommand))
+ throw css::container::NoSuchElementException(
+ OUString(),
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ AcceleratorCache::TKeyList lKeys = rCache.getKeysByCommand(sCommand);
+ return comphelper::containerToSequence(lKeys);
+}
+
+css::uno::Sequence< css::uno::Any > SAL_CALL XMLBasedAcceleratorConfiguration::getPreferredKeyEventsForCommandList(const css::uno::Sequence< OUString >& lCommandList)
+{
+ SolarMutexGuard g;
+
+ sal_Int32 i = 0;
+ sal_Int32 c = lCommandList.getLength();
+ css::uno::Sequence< css::uno::Any > lPreferredOnes (c); // don't pack list!
+ AcceleratorCache& rCache = impl_getCFG();
+
+ auto lPreferredOnesRange = asNonConstRange(lPreferredOnes);
+ for (i=0; i<c; ++i)
+ {
+ const OUString& rCommand = lCommandList[i];
+ if (rCommand.isEmpty())
+ throw css::lang::IllegalArgumentException(
+ "Empty command strings are not allowed here.",
+ static_cast< ::cppu::OWeakObject* >(this),
+ static_cast<sal_Int16>(i));
+
+ if (!rCache.hasCommand(rCommand))
+ continue;
+
+ AcceleratorCache::TKeyList lKeys = rCache.getKeysByCommand(rCommand);
+ if ( lKeys.empty() )
+ continue;
+
+ css::uno::Any& rAny = lPreferredOnesRange[i];
+ rAny <<= *(lKeys.begin());
+ }
+
+ return lPreferredOnes;
+}
+
+void SAL_CALL XMLBasedAcceleratorConfiguration::removeCommandFromAllKeyEvents(const OUString& sCommand)
+{
+ if (sCommand.isEmpty())
+ throw css::lang::IllegalArgumentException(
+ "Empty command strings are not allowed here.",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 0);
+
+ SolarMutexGuard g;
+ AcceleratorCache& rCache = impl_getCFG(true); // sal_True => force getting of a writeable cache!
+ if (!rCache.hasCommand(sCommand))
+ throw css::container::NoSuchElementException(
+ "Command does not exists inside this container.",
+ static_cast< ::cppu::OWeakObject* >(this));
+ rCache.removeCommand(sCommand);
+}
+
+void SAL_CALL XMLBasedAcceleratorConfiguration::reload()
+{
+ css::uno::Reference< css::io::XStream > xStream;
+ css::uno::Reference< css::io::XStream > xStreamNoLang;
+ {
+ SolarMutexGuard g;
+ xStream = m_aPresetHandler.openTarget(TARGET_CURRENT,
+ css::embed::ElementModes::READ);
+ try
+ {
+ xStreamNoLang = m_aPresetHandler.openPreset(PRESET_DEFAULT);
+ }
+ catch(const css::io::IOException&) {} // does not have to exist
+ }
+
+ css::uno::Reference< css::io::XInputStream > xIn;
+ if (xStream.is())
+ xIn = xStream->getInputStream();
+ if (!xIn.is())
+ throw css::io::IOException(
+ "Could not open accelerator configuration for reading.",
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ // impl_ts_load() does not clear the cache
+ {
+ SolarMutexGuard g;
+ m_aReadCache = AcceleratorCache();
+ }
+
+ impl_ts_load(xIn);
+
+ // Load also the general language independent default accelerators
+ // (ignoring the already defined accelerators)
+ if (xStreamNoLang.is())
+ {
+ xIn = xStreamNoLang->getInputStream();
+ if (xIn.is())
+ impl_ts_load(xIn);
+ }
+}
+
+void SAL_CALL XMLBasedAcceleratorConfiguration::store()
+{
+ css::uno::Reference< css::io::XStream > xStream;
+ {
+ SolarMutexGuard g;
+ xStream = m_aPresetHandler.openTarget(TARGET_CURRENT,
+ css::embed::ElementModes::READWRITE); // open or create!
+ }
+
+ css::uno::Reference< css::io::XOutputStream > xOut;
+ if (xStream.is())
+ xOut = xStream->getOutputStream();
+
+ if (!xOut.is())
+ throw css::io::IOException(
+ "Could not open accelerator configuration for saving.",
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ impl_ts_save(xOut);
+
+ xOut.clear();
+ xStream.clear();
+
+ m_aPresetHandler.commitUserChanges();
+}
+
+void SAL_CALL XMLBasedAcceleratorConfiguration::storeToStorage(const css::uno::Reference< css::embed::XStorage >& xStorage)
+{
+ // no fallback from read/write to readonly!
+ css::uno::Reference< css::io::XStream > xStream = xStorage->openStreamElement(TARGET_CURRENT, css::embed::ElementModes::READWRITE);
+
+ css::uno::Reference< css::io::XOutputStream > xOut;
+ if (xStream.is())
+ xOut = xStream->getOutputStream();
+
+ if (!xOut.is())
+ throw css::io::IOException(
+ "Could not open accelerator configuration for saving.",
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ impl_ts_save(xOut);
+
+ // TODO inform listener about success, so it can flush the root and sub storage of this stream!
+}
+
+sal_Bool SAL_CALL XMLBasedAcceleratorConfiguration::isModified()
+{
+ SolarMutexGuard g;
+ return (m_pWriteCache != nullptr);
+}
+
+sal_Bool SAL_CALL XMLBasedAcceleratorConfiguration::isReadOnly()
+{
+ css::uno::Reference< css::io::XStream > xStream;
+ {
+ SolarMutexGuard g;
+ xStream = m_aPresetHandler.openTarget(TARGET_CURRENT,
+ css::embed::ElementModes::READWRITE); // open or create!
+ }
+
+ css::uno::Reference< css::io::XOutputStream > xOut;
+ if (xStream.is())
+ xOut = xStream->getOutputStream();
+ return !(xOut.is());
+}
+
+void SAL_CALL XMLBasedAcceleratorConfiguration::setStorage(const css::uno::Reference< css::embed::XStorage >& /*xStorage*/)
+{
+ SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::setStorage(): implement this HACK .-)");
+}
+
+sal_Bool SAL_CALL XMLBasedAcceleratorConfiguration::hasStorage()
+{
+ SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::hasStorage(): implement this HACK .-)");
+ return false;
+}
+
+void SAL_CALL XMLBasedAcceleratorConfiguration::addConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& /*xListener*/)
+{
+ SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::addConfigurationListener(): implement me");
+}
+
+void SAL_CALL XMLBasedAcceleratorConfiguration::removeConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& /*xListener*/)
+{
+ SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::removeConfigurationListener(): implement me");
+}
+
+void SAL_CALL XMLBasedAcceleratorConfiguration::reset()
+{
+ {
+ SolarMutexGuard g;
+ m_aPresetHandler.copyPresetToTarget(PRESET_DEFAULT, TARGET_CURRENT);
+ }
+
+ reload();
+}
+
+void SAL_CALL XMLBasedAcceleratorConfiguration::addResetListener(const css::uno::Reference< css::form::XResetListener >& /*xListener*/)
+{
+ SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::addResetListener(): implement me");
+}
+
+void SAL_CALL XMLBasedAcceleratorConfiguration::removeResetListener(const css::uno::Reference< css::form::XResetListener >& /*xListener*/)
+{
+ SAL_INFO("fwk.accelerators", "XMLBasedAcceleratorConfiguration::removeResetListener(): implement me");
+}
+
+// IStorageListener
+void XMLBasedAcceleratorConfiguration::changesOccurred()
+{
+ reload();
+}
+
+void XMLBasedAcceleratorConfiguration::impl_ts_load(const css::uno::Reference< css::io::XInputStream >& xStream)
+{
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ {
+ SolarMutexGuard g;
+ xContext = m_xContext;
+ m_pWriteCache.reset();
+ }
+
+ css::uno::Reference< css::io::XSeekable > xSeek(xStream, css::uno::UNO_QUERY);
+ if (xSeek.is())
+ xSeek->seek(0);
+
+ SolarMutexGuard g;
+
+ // create the parser queue
+ // Note: Use special filter object between parser and reader
+ // to get filtered xml with right namespaces ...
+ // Use further a temp cache for reading!
+ rtl::Reference<AcceleratorConfigurationReader> pReader = new AcceleratorConfigurationReader(m_aReadCache);
+ rtl::Reference<SaxNamespaceFilter> pFilter = new SaxNamespaceFilter(pReader);
+
+ // connect parser, filter and stream
+ css::uno::Reference< css::xml::sax::XParser > xParser = css::xml::sax::Parser::create(xContext);
+ xParser->setDocumentHandler(pFilter);
+
+ css::xml::sax::InputSource aSource;
+ aSource.aInputStream = xStream;
+
+ // TODO think about error handling
+ xParser->parseStream(aSource);
+}
+
+void XMLBasedAcceleratorConfiguration::impl_ts_save(const css::uno::Reference< css::io::XOutputStream >& xStream)
+{
+ bool bChanged;
+ AcceleratorCache aCache;
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ {
+ SolarMutexGuard g;
+ bChanged = (m_pWriteCache != nullptr);
+ if (bChanged)
+ aCache = *m_pWriteCache;
+ else
+ aCache = m_aReadCache;
+ xContext = m_xContext;
+ }
+
+ css::uno::Reference< css::io::XTruncate > xClearable(xStream, css::uno::UNO_QUERY_THROW);
+ xClearable->truncate();
+
+ // TODO can be removed if seek(0) is done by truncate() automatically!
+ css::uno::Reference< css::io::XSeekable > xSeek(xStream, css::uno::UNO_QUERY);
+ if (xSeek.is())
+ xSeek->seek(0);
+
+ // combine writer/cache/stream etcpp.
+ css::uno::Reference< css::xml::sax::XWriter > xWriter = css::xml::sax::Writer::create(xContext);
+ xWriter->setOutputStream(xStream);
+
+ // write into the stream
+ css::uno::Reference< css::xml::sax::XDocumentHandler > xHandler(xWriter, css::uno::UNO_QUERY_THROW);
+ AcceleratorConfigurationWriter aWriter(aCache, xHandler);
+ aWriter.flush();
+
+ SolarMutexGuard g;
+ // take over all changes into the readonly cache ...
+ // and forget the copy-on-write copied cache
+ if (bChanged)
+ {
+ m_aReadCache = *m_pWriteCache;
+ m_pWriteCache.reset();
+ }
+}
+
+AcceleratorCache& XMLBasedAcceleratorConfiguration::impl_getCFG(bool bWriteAccessRequested)
+{
+ SolarMutexGuard g;
+
+ //create copy of our readonly-cache, if write access is forced ... but
+ //not still possible!
+ if ( bWriteAccessRequested && !m_pWriteCache )
+ {
+ m_pWriteCache.reset(new AcceleratorCache(m_aReadCache));
+ }
+
+ // in case, we have a writeable cache, we use it for reading too!
+ // Otherwise the API user can't find its own changes...
+ if (m_pWriteCache)
+ return *m_pWriteCache;
+ else
+ return m_aReadCache;
+}
+
+OUString XMLBasedAcceleratorConfiguration::impl_ts_getLocale() const
+{
+ OUString sISOLocale = officecfg::Setup::L10N::ooLocale::get();
+
+ if (sISOLocale.isEmpty())
+ return "en-US";
+ return sISOLocale;
+}
+
+/*******************************************************************************
+*
+* XCU based accelerator configuration
+*
+*******************************************************************************/
+
+XCUBasedAcceleratorConfiguration::XCUBasedAcceleratorConfiguration(css::uno::Reference< css::uno::XComponentContext > xContext)
+ : m_xContext (std::move(xContext ))
+{
+ m_xCfg.set(
+ ::comphelper::ConfigurationHelper::openConfig( m_xContext, "org.openoffice.Office.Accelerators", ::comphelper::EConfigurationModes::AllLocales ),
+ css::uno::UNO_QUERY );
+}
+
+XCUBasedAcceleratorConfiguration::~XCUBasedAcceleratorConfiguration()
+{
+}
+
+css::uno::Sequence< css::awt::KeyEvent > SAL_CALL XCUBasedAcceleratorConfiguration::getAllKeyEvents()
+{
+ SolarMutexGuard g;
+
+ AcceleratorCache::TKeyList lKeys = impl_getCFG(true).getAllKeys(); //get keys from PrimaryKeys set
+
+ AcceleratorCache::TKeyList lSecondaryKeys = impl_getCFG(false).getAllKeys(); //get keys from SecondaryKeys set
+ lKeys.reserve(lKeys.size()+lSecondaryKeys.size());
+ for (auto const& secondaryKey : lSecondaryKeys)
+ lKeys.push_back(secondaryKey);
+
+ return comphelper::containerToSequence(lKeys);
+}
+
+OUString SAL_CALL XCUBasedAcceleratorConfiguration::getCommandByKeyEvent(const css::awt::KeyEvent& aKeyEvent)
+{
+ SolarMutexGuard g;
+
+ AcceleratorCache& rPrimaryCache = impl_getCFG(true );
+ AcceleratorCache& rSecondaryCache = impl_getCFG(false);
+
+ if (!rPrimaryCache.hasKey(aKeyEvent) && !rSecondaryCache.hasKey(aKeyEvent))
+ throw css::container::NoSuchElementException(
+ OUString(),
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ if (rPrimaryCache.hasKey(aKeyEvent))
+ return rPrimaryCache.getCommandByKey(aKeyEvent);
+ else
+ return rSecondaryCache.getCommandByKey(aKeyEvent);
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::setKeyEvent(const css::awt::KeyEvent& aKeyEvent,
+ const OUString& sCommand )
+{
+ SAL_INFO( "fwk.accelerators", "XCUBasedAcceleratorConfiguration::setKeyEvent" );
+
+ if (
+ (aKeyEvent.KeyCode == 0) &&
+ (aKeyEvent.KeyChar == 0) &&
+ (aKeyEvent.KeyFunc == 0) &&
+ (aKeyEvent.Modifiers == 0)
+ )
+ throw css::lang::IllegalArgumentException(
+ "Such key event seems not to be supported by any operating system.",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 0);
+
+ if (sCommand.isEmpty())
+ throw css::lang::IllegalArgumentException(
+ "Empty command strings are not allowed here.",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 1);
+
+ SolarMutexGuard g;
+
+ AcceleratorCache& rPrimaryCache = impl_getCFG(true, true ); // sal_True => force getting of a writeable cache!
+ AcceleratorCache& rSecondaryCache = impl_getCFG(false, true); // sal_True => force getting of a writeable cache!
+
+ if ( rPrimaryCache.hasKey(aKeyEvent) )
+ {
+ OUString sOriginalCommand = rPrimaryCache.getCommandByKey(aKeyEvent);
+ if ( sCommand != sOriginalCommand )
+ {
+ if (rSecondaryCache.hasCommand(sOriginalCommand))
+ {
+ AcceleratorCache::TKeyList lSecondaryKeys = rSecondaryCache.getKeysByCommand(sOriginalCommand);
+ rSecondaryCache.removeKey(lSecondaryKeys[0]);
+ rPrimaryCache.setKeyCommandPair(lSecondaryKeys[0], sOriginalCommand);
+ }
+
+ if (rPrimaryCache.hasCommand(sCommand))
+ {
+ AcceleratorCache::TKeyList lPrimaryKeys = rPrimaryCache.getKeysByCommand(sCommand);
+ rPrimaryCache.removeKey(lPrimaryKeys[0]);
+ rSecondaryCache.setKeyCommandPair(lPrimaryKeys[0], sCommand);
+ }
+
+ rPrimaryCache.setKeyCommandPair(aKeyEvent, sCommand);
+ }
+ }
+
+ else if ( rSecondaryCache.hasKey(aKeyEvent) )
+ {
+ OUString sOriginalCommand = rSecondaryCache.getCommandByKey(aKeyEvent);
+ if (sCommand != sOriginalCommand)
+ {
+ if (rPrimaryCache.hasCommand(sCommand))
+ {
+ AcceleratorCache::TKeyList lPrimaryKeys = rPrimaryCache.getKeysByCommand(sCommand);
+ rPrimaryCache.removeKey(lPrimaryKeys[0]);
+ rSecondaryCache.setKeyCommandPair(lPrimaryKeys[0], sCommand);
+ }
+
+ rSecondaryCache.removeKey(aKeyEvent);
+ rPrimaryCache.setKeyCommandPair(aKeyEvent, sCommand);
+ }
+ }
+
+ else
+ {
+ if (rPrimaryCache.hasCommand(sCommand))
+ {
+ AcceleratorCache::TKeyList lPrimaryKeys = rPrimaryCache.getKeysByCommand(sCommand);
+ rPrimaryCache.removeKey(lPrimaryKeys[0]);
+ rSecondaryCache.setKeyCommandPair(lPrimaryKeys[0], sCommand);
+ }
+
+ rPrimaryCache.setKeyCommandPair(aKeyEvent, sCommand);
+ }
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::removeKeyEvent(const css::awt::KeyEvent& aKeyEvent)
+{
+ SolarMutexGuard g;
+
+ AcceleratorCache& rPrimaryCache = impl_getCFG(true, true );
+ AcceleratorCache& rSecondaryCache = impl_getCFG(false, true);
+
+ if (!rPrimaryCache.hasKey(aKeyEvent) && !rSecondaryCache.hasKey(aKeyEvent))
+ throw css::container::NoSuchElementException(
+ OUString(),
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ if (rPrimaryCache.hasKey(aKeyEvent))
+ {
+ OUString sDelCommand = rPrimaryCache.getCommandByKey(aKeyEvent);
+ if (!sDelCommand.isEmpty())
+ {
+ OUString sOriginalCommand = rPrimaryCache.getCommandByKey(aKeyEvent);
+ if (rSecondaryCache.hasCommand(sOriginalCommand))
+ {
+ AcceleratorCache::TKeyList lSecondaryKeys = rSecondaryCache.getKeysByCommand(sOriginalCommand);
+ rSecondaryCache.removeKey(lSecondaryKeys[0]);
+ rPrimaryCache.setKeyCommandPair(lSecondaryKeys[0], sOriginalCommand);
+ }
+
+ rPrimaryCache.removeKey(aKeyEvent);
+ }
+
+ }
+ else
+ {
+ OUString sDelCommand = rSecondaryCache.getCommandByKey(aKeyEvent);
+ if (!sDelCommand.isEmpty())
+ rSecondaryCache.removeKey(aKeyEvent);
+ }
+}
+
+css::uno::Sequence< css::awt::KeyEvent > SAL_CALL XCUBasedAcceleratorConfiguration::getKeyEventsByCommand(const OUString& sCommand)
+{
+ if (sCommand.isEmpty())
+ throw css::lang::IllegalArgumentException(
+ "Empty command strings are not allowed here.",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 1);
+
+ SolarMutexGuard g;
+
+ AcceleratorCache& rPrimaryCache = impl_getCFG(true );
+ AcceleratorCache& rSecondaryCache = impl_getCFG(false);
+
+ if (!rPrimaryCache.hasCommand(sCommand) && !rSecondaryCache.hasCommand(sCommand))
+ throw css::container::NoSuchElementException(
+ OUString(),
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ AcceleratorCache::TKeyList lKeys = rPrimaryCache.getKeysByCommand(sCommand);
+
+ AcceleratorCache::TKeyList lSecondaryKeys = rSecondaryCache.getKeysByCommand(sCommand);
+ for (auto const& secondaryKey : lSecondaryKeys)
+ lKeys.push_back(secondaryKey);
+
+ return comphelper::containerToSequence(lKeys);
+}
+
+static AcceleratorCache::TKeyList::const_iterator lcl_getPreferredKey(const AcceleratorCache::TKeyList& lKeys)
+{
+ return std::find_if(lKeys.begin(), lKeys.end(), [](const css::awt::KeyEvent& rAWTKey) {
+ return !::svt::AcceleratorExecute::st_AWTKey2VCLKey(rAWTKey).GetName().isEmpty(); });
+}
+
+css::uno::Sequence< css::uno::Any > SAL_CALL XCUBasedAcceleratorConfiguration::getPreferredKeyEventsForCommandList(const css::uno::Sequence< OUString >& lCommandList)
+{
+ SolarMutexGuard g;
+
+ sal_Int32 i = 0;
+ sal_Int32 c = lCommandList.getLength();
+ css::uno::Sequence< css::uno::Any > lPreferredOnes (c); // don't pack list!
+ AcceleratorCache& rCache = impl_getCFG(true);
+
+ auto lPreferredOnesRange = asNonConstRange(lPreferredOnes);
+ for (i=0; i<c; ++i)
+ {
+ const OUString& rCommand = lCommandList[i];
+ if (rCommand.isEmpty())
+ throw css::lang::IllegalArgumentException(
+ "Empty command strings are not allowed here.",
+ static_cast< ::cppu::OWeakObject* >(this),
+ static_cast<sal_Int16>(i));
+
+ if (!rCache.hasCommand(rCommand))
+ continue;
+
+ AcceleratorCache::TKeyList lKeys = rCache.getKeysByCommand(rCommand);
+ if ( lKeys.empty() )
+ continue;
+
+ AcceleratorCache::TKeyList::const_iterator pPreferredKey = lcl_getPreferredKey(lKeys);
+ if (pPreferredKey != lKeys.end ())
+ {
+ css::uno::Any& rAny = lPreferredOnesRange[i];
+ rAny <<= *pPreferredKey;
+ }
+ }
+
+ return lPreferredOnes;
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::removeCommandFromAllKeyEvents(const OUString& sCommand)
+{
+ if (sCommand.isEmpty())
+ throw css::lang::IllegalArgumentException(
+ "Empty command strings are not allowed here.",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 0);
+
+ SolarMutexGuard g;
+
+ AcceleratorCache& rPrimaryCache = impl_getCFG(true, true );
+ AcceleratorCache& rSecondaryCache = impl_getCFG(false, true);
+
+ if (!rPrimaryCache.hasCommand(sCommand) && !rSecondaryCache.hasCommand(sCommand))
+ throw css::container::NoSuchElementException(
+ "Command does not exists inside this container.",
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ if (rPrimaryCache.hasCommand(sCommand))
+ rPrimaryCache.removeCommand(sCommand);
+ if (rSecondaryCache.hasCommand(sCommand))
+ rSecondaryCache.removeCommand(sCommand);
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::reload()
+{
+ SAL_INFO( "fwk.accelerators", "XCUBasedAcceleratorConfiguration::reload()" );
+
+ SolarMutexGuard g;
+
+ bool bPreferred;
+ css::uno::Reference< css::container::XNameAccess > xAccess;
+
+ bPreferred = true;
+ m_aPrimaryReadCache = AcceleratorCache();
+ m_pPrimaryWriteCache.reset();
+ m_xCfg->getByName(CFG_ENTRY_PRIMARY) >>= xAccess;
+ impl_ts_load(bPreferred, xAccess); // load the preferred keys
+
+ bPreferred = false;
+ m_aSecondaryReadCache = AcceleratorCache();
+ m_pSecondaryWriteCache.reset();
+ m_xCfg->getByName(CFG_ENTRY_SECONDARY) >>= xAccess;
+ impl_ts_load(bPreferred, xAccess); // load the secondary keys
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::store()
+{
+ SAL_INFO( "fwk.accelerators", "XCUBasedAcceleratorConfiguration::store()" );
+
+ SolarMutexGuard g;
+
+ bool bPreferred;
+
+ bPreferred = true;
+ // on-demand creation of the primary write cache
+ impl_getCFG(bPreferred, true);
+ impl_ts_save(bPreferred);
+
+ bPreferred = false;
+ // on-demand creation of the secondary write cache
+ impl_getCFG(bPreferred, true);
+ impl_ts_save(bPreferred);
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::storeToStorage(const css::uno::Reference< css::embed::XStorage >& xStorage)
+{
+ // use m_aCache + old AcceleratorXMLWriter to store data directly on storage given as parameter ...
+ if (!xStorage.is())
+ return;
+
+ tools::Long nOpenModes = css::embed::ElementModes::READWRITE;
+ css::uno::Reference< css::embed::XStorage > xAcceleratorTypeStorage = xStorage->openStorageElement("accelerator", nOpenModes);
+ if (!xAcceleratorTypeStorage.is())
+ return;
+
+ css::uno::Reference< css::io::XStream > xStream = xAcceleratorTypeStorage->openStreamElement("current", nOpenModes);
+ css::uno::Reference< css::io::XOutputStream > xOut;
+ if (xStream.is())
+ xOut = xStream->getOutputStream();
+ if (!xOut.is())
+ throw css::io::IOException(
+ "Could not open accelerator configuration for saving.",
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ // the original m_aCache has been split into primary cache and secondary cache...
+ // we should merge them before storing to storage
+ AcceleratorCache aCache;
+ {
+ SolarMutexGuard g;
+
+ if (m_pPrimaryWriteCache != nullptr)
+ aCache = *m_pPrimaryWriteCache;
+ else
+ aCache = m_aPrimaryReadCache;
+
+ AcceleratorCache::TKeyList lKeys;
+ if (m_pSecondaryWriteCache!=nullptr)
+ {
+ lKeys = m_pSecondaryWriteCache->getAllKeys();
+ for (auto const& lKey : lKeys)
+ aCache.setKeyCommandPair(lKey, m_pSecondaryWriteCache->getCommandByKey(lKey));
+ }
+ else
+ {
+ lKeys = m_aSecondaryReadCache.getAllKeys();
+ for (auto const& lKey : lKeys)
+ aCache.setKeyCommandPair(lKey, m_aSecondaryReadCache.getCommandByKey(lKey));
+ }
+ }
+
+ css::uno::Reference< css::io::XTruncate > xClearable(xOut, css::uno::UNO_QUERY_THROW);
+ xClearable->truncate();
+ css::uno::Reference< css::io::XSeekable > xSeek(xOut, css::uno::UNO_QUERY);
+ if (xSeek.is())
+ xSeek->seek(0);
+
+ css::uno::Reference< css::xml::sax::XWriter > xWriter = css::xml::sax::Writer::create(m_xContext);
+ xWriter->setOutputStream(xOut);
+
+ // write into the stream
+ css::uno::Reference< css::xml::sax::XDocumentHandler > xHandler(xWriter, css::uno::UNO_QUERY_THROW);
+ AcceleratorConfigurationWriter aWriter(aCache, xHandler);
+ aWriter.flush();
+}
+
+sal_Bool SAL_CALL XCUBasedAcceleratorConfiguration::isModified()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL XCUBasedAcceleratorConfiguration::isReadOnly()
+{
+ return false;
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::setStorage(const css::uno::Reference< css::embed::XStorage >& /*xStorage*/)
+{
+ SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::setStorage(): implement this HACK .-)");
+}
+
+sal_Bool SAL_CALL XCUBasedAcceleratorConfiguration::hasStorage()
+{
+ SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::hasStorage(): implement this HACK .-)");
+ return false;
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::addConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& /*xListener*/)
+{
+ SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::addConfigurationListener(): implement me");
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::removeConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& /*xListener*/)
+{
+ SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::removeConfigurationListener(): implement me");
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::reset()
+{
+ css::uno::Reference< css::container::XNamed > xNamed(m_xCfg, css::uno::UNO_QUERY);
+ OUString sConfig = xNamed->getName();
+ if ( sConfig == "Global" )
+ {
+ m_xCfg.set(
+ ::comphelper::ConfigurationHelper::openConfig( m_xContext, CFG_ENTRY_GLOBAL, ::comphelper::EConfigurationModes::AllLocales ),
+ css::uno::UNO_QUERY );
+ XCUBasedAcceleratorConfiguration::reload();
+ }
+ else if ( sConfig == "Modules" )
+ {
+ m_xCfg.set(
+ ::comphelper::ConfigurationHelper::openConfig( m_xContext, CFG_ENTRY_MODULES, ::comphelper::EConfigurationModes::AllLocales ),
+ css::uno::UNO_QUERY );
+ XCUBasedAcceleratorConfiguration::reload();
+ }
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::addResetListener(const css::uno::Reference< css::form::XResetListener >& /*xListener*/)
+{
+ SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::addResetListener(): implement me");
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::removeResetListener(const css::uno::Reference< css::form::XResetListener >& /*xListener*/)
+{
+ SAL_INFO("fwk.accelerators", "XCUBasedAcceleratorConfiguration::removeResetListener(): implement me");
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::changesOccurred(const css::util::ChangesEvent& aEvent)
+{
+ SAL_INFO( "fwk.accelerators", "XCUBasedAcceleratorConfiguration::changesOccurred()" );
+
+ css::uno::Reference< css::container::XHierarchicalNameAccess > xHAccess;
+ aEvent.Base >>= xHAccess;
+ if (! xHAccess.is ())
+ return;
+
+ css::util::ChangesEvent aReceivedEvents( aEvent );
+ const sal_Int32 c = aReceivedEvents.Changes.getLength();
+ for (sal_Int32 i=0; i<c; ++i)
+ {
+ const css::util::ElementChange& aChange = aReceivedEvents.Changes[i];
+
+ // Only path of form "PrimaryKeys/Modules/Module['<module_name>']/Key['<command_url>']/Command[<locale>]" will
+ // be interesting for use. Sometimes short path values are given also by the broadcaster ... but they must be ignored :-)
+ // So we try to split the path into 3 parts (module isn't important here, because we already know it ... because
+ // these instance is bound to a specific module configuration ... or it''s the global configuration where no module is given at all.
+
+ OUString sOrgPath;
+ OUString sPath;
+ OUString sKey;
+
+ aChange.Accessor >>= sOrgPath;
+ sPath = sOrgPath;
+ OUString sPrimarySecondary = ::utl::extractFirstFromConfigurationPath(sPath, &sPath);
+ OUString sGlobalModules = ::utl::extractFirstFromConfigurationPath(sPath, &sPath);
+
+ if ( sGlobalModules == CFG_ENTRY_GLOBAL )
+ {
+ sKey = ::utl::extractFirstFromConfigurationPath(sPath, &sPath);
+ if ( !sKey.isEmpty() && !sPath.isEmpty() )
+ reloadChanged(sPrimarySecondary, sGlobalModules, OUString(), sKey);
+ }
+ else if ( sGlobalModules == CFG_ENTRY_MODULES )
+ {
+ OUString sModule = ::utl::extractFirstFromConfigurationPath(sPath, &sPath);
+ sKey = ::utl::extractFirstFromConfigurationPath(sPath, &sPath);
+
+ if ( !sKey.isEmpty() && !sPath.isEmpty() )
+ {
+ reloadChanged(sPrimarySecondary, sGlobalModules, sModule, sKey);
+ }
+ }
+ }
+}
+
+void SAL_CALL XCUBasedAcceleratorConfiguration::disposing(const css::lang::EventObject& /*aSource*/)
+{
+}
+
+void XCUBasedAcceleratorConfiguration::impl_ts_load( bool bPreferred, const css::uno::Reference< css::container::XNameAccess >& xCfg )
+{
+ AcceleratorCache aReadCache;
+ css::uno::Reference< css::container::XNameAccess > xAccess;
+ if ( m_sGlobalOrModules == "Global" )
+ xCfg->getByName(CFG_ENTRY_GLOBAL) >>= xAccess;
+ else if ( m_sGlobalOrModules == "Modules" )
+ {
+ css::uno::Reference< css::container::XNameAccess > xModules;
+ xCfg->getByName(CFG_ENTRY_MODULES) >>= xModules;
+ xModules->getByName(m_sModuleCFG) >>= xAccess;
+ }
+
+ const OUString sIsoLang = impl_ts_getLocale();
+ static const OUStringLiteral sDefaultLocale(u"en-US");
+
+ css::uno::Reference< css::container::XNameAccess > xKey;
+ css::uno::Reference< css::container::XNameAccess > xCommand;
+ if (xAccess.is())
+ {
+ css::uno::Sequence< OUString > lKeys = xAccess->getElementNames();
+ sal_Int32 nKeys = lKeys.getLength();
+ for ( sal_Int32 i=0; i<nKeys; ++i )
+ {
+ OUString sKey = lKeys[i];
+ xAccess->getByName(sKey) >>= xKey;
+ xKey->getByName(CFG_PROP_COMMAND) >>= xCommand;
+
+ const css::uno::Sequence< OUString > lLocales = xCommand->getElementNames();
+ ::std::vector< OUString > aLocales { lLocales.begin(), lLocales.end() };
+
+ OUString sLocale;
+ for (auto const& locale : aLocales)
+ {
+ if ( locale == sIsoLang )
+ {
+ sLocale = locale;
+ break;
+ }
+ }
+
+ if (sLocale.isEmpty())
+ {
+ for (auto const& locale : aLocales)
+ {
+ if ( locale == sDefaultLocale )
+ {
+ sLocale = locale;
+ break;
+ }
+ }
+
+ if (sLocale.isEmpty())
+ continue;
+ }
+
+ OUString sCommand;
+ xCommand->getByName(sLocale) >>= sCommand;
+ if (sCommand.isEmpty())
+ continue;
+
+ css::awt::KeyEvent aKeyEvent;
+
+ sal_Int32 nIndex = 0;
+ std::u16string_view sKeyCommand = o3tl::getToken(sKey, 0, '_', nIndex);
+ aKeyEvent.KeyCode = KeyMapping::get().mapIdentifierToCode(OUString::Concat("KEY_") + sKeyCommand);
+
+ const sal_Int32 nToken = 4;
+ bool bValid = true;
+ sal_Int32 k;
+ for (k = 0; k < nToken; ++k)
+ {
+ if (nIndex < 0)
+ break;
+
+ std::u16string_view sToken = o3tl::getToken(sKey, 0, '_', nIndex);
+ if (sToken.empty())
+ {
+ bValid = false;
+ break;
+ }
+
+ if ( sToken == u"SHIFT" )
+ aKeyEvent.Modifiers |= css::awt::KeyModifier::SHIFT;
+ else if ( sToken == u"MOD1" )
+ aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD1;
+ else if ( sToken == u"MOD2" )
+ aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD2;
+ else if ( sToken == u"MOD3" )
+ aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD3;
+ else
+ {
+ bValid = false;
+ break;
+ }
+ }
+
+ if ( !aReadCache.hasKey(aKeyEvent) && bValid && k<nToken)
+ aReadCache.setKeyCommandPair(aKeyEvent, sCommand);
+ }
+ }
+
+ if (bPreferred)
+ m_aPrimaryReadCache = std::move(aReadCache);
+ else
+ m_aSecondaryReadCache = std::move(aReadCache);
+}
+
+void XCUBasedAcceleratorConfiguration::impl_ts_save(bool bPreferred)
+{
+ if (bPreferred)
+ {
+ AcceleratorCache::TKeyList lPrimaryReadKeys = m_aPrimaryReadCache.getAllKeys();
+ AcceleratorCache::TKeyList lPrimaryWriteKeys = m_pPrimaryWriteCache->getAllKeys();
+
+ for (auto const& primaryReadKey : lPrimaryReadKeys)
+ {
+ if (!m_pPrimaryWriteCache->hasKey(primaryReadKey))
+ removeKeyFromConfiguration(primaryReadKey, true);
+ }
+
+ for (auto const& primaryWriteKey : lPrimaryWriteKeys)
+ {
+ OUString sCommand = m_pPrimaryWriteCache->getCommandByKey(primaryWriteKey);
+ if (!m_aPrimaryReadCache.hasKey(primaryWriteKey))
+ {
+ insertKeyToConfiguration(primaryWriteKey, sCommand, true);
+ }
+ else
+ {
+ OUString sReadCommand = m_aPrimaryReadCache.getCommandByKey(primaryWriteKey);
+ if (sReadCommand != sCommand)
+ insertKeyToConfiguration(primaryWriteKey, sCommand, true);
+ }
+ }
+
+ // take over all changes into the original container
+ SolarMutexGuard g;
+ // coverity[check_after_deref] - confusing but correct
+ if (m_pPrimaryWriteCache)
+ {
+ m_aPrimaryReadCache = *m_pPrimaryWriteCache;
+ m_pPrimaryWriteCache.reset();
+ }
+ }
+
+ else
+ {
+ AcceleratorCache::TKeyList lSecondaryReadKeys = m_aSecondaryReadCache.getAllKeys();
+ AcceleratorCache::TKeyList lSecondaryWriteKeys = m_pSecondaryWriteCache->getAllKeys();
+
+ for (auto const& secondaryReadKey : lSecondaryReadKeys)
+ {
+ if (!m_pSecondaryWriteCache->hasKey(secondaryReadKey))
+ removeKeyFromConfiguration(secondaryReadKey, false);
+ }
+
+ for (auto const& secondaryWriteKey : lSecondaryWriteKeys)
+ {
+ OUString sCommand = m_pSecondaryWriteCache->getCommandByKey(secondaryWriteKey);
+ if (!m_aSecondaryReadCache.hasKey(secondaryWriteKey))
+ {
+ insertKeyToConfiguration(secondaryWriteKey, sCommand, false);
+ }
+ else
+ {
+ OUString sReadCommand = m_aSecondaryReadCache.getCommandByKey(secondaryWriteKey);
+ if (sReadCommand != sCommand)
+ insertKeyToConfiguration(secondaryWriteKey, sCommand, false);
+ }
+ }
+
+ // take over all changes into the original container
+ SolarMutexGuard g;
+ // coverity[check_after_deref] - confusing but correct
+ if (m_pSecondaryWriteCache)
+ {
+ m_aSecondaryReadCache = *m_pSecondaryWriteCache;
+ m_pSecondaryWriteCache.reset();
+ }
+ }
+
+ ::comphelper::ConfigurationHelper::flush(m_xCfg);
+}
+
+void XCUBasedAcceleratorConfiguration::insertKeyToConfiguration( const css::awt::KeyEvent& aKeyEvent, const OUString& sCommand, const bool bPreferred )
+{
+ css::uno::Reference< css::container::XNameAccess > xAccess;
+ css::uno::Reference< css::container::XNameContainer > xContainer;
+ css::uno::Reference< css::lang::XSingleServiceFactory > xFac;
+ css::uno::Reference< css::uno::XInterface > xInst;
+
+ if ( bPreferred )
+ m_xCfg->getByName(CFG_ENTRY_PRIMARY) >>= xAccess;
+ else
+ m_xCfg->getByName(CFG_ENTRY_SECONDARY) >>= xAccess;
+
+ if ( m_sGlobalOrModules == CFG_ENTRY_GLOBAL )
+ xAccess->getByName(CFG_ENTRY_GLOBAL) >>= xContainer;
+ else if ( m_sGlobalOrModules == CFG_ENTRY_MODULES )
+ {
+ css::uno::Reference< css::container::XNameContainer > xModules;
+ xAccess->getByName(CFG_ENTRY_MODULES) >>= xModules;
+ if ( !xModules->hasByName(m_sModuleCFG) )
+ {
+ xFac.set(xModules, css::uno::UNO_QUERY);
+ xInst = xFac->createInstance();
+ xModules->insertByName(m_sModuleCFG, css::uno::Any(xInst));
+ }
+ xModules->getByName(m_sModuleCFG) >>= xContainer;
+ }
+
+ const OUString sKey = lcl_getKeyString(aKeyEvent);
+ css::uno::Reference< css::container::XNameAccess > xKey;
+ css::uno::Reference< css::container::XNameContainer > xCommand;
+ if ( !xContainer->hasByName(sKey) )
+ {
+ xFac.set(xContainer, css::uno::UNO_QUERY);
+ xInst = xFac->createInstance();
+ xContainer->insertByName(sKey, css::uno::Any(xInst));
+ }
+ xContainer->getByName(sKey) >>= xKey;
+
+ xKey->getByName(CFG_PROP_COMMAND) >>= xCommand;
+ OUString sLocale = impl_ts_getLocale();
+ if ( !xCommand->hasByName(sLocale) )
+ xCommand->insertByName(sLocale, css::uno::Any(sCommand));
+ else
+ xCommand->replaceByName(sLocale, css::uno::Any(sCommand));
+}
+
+void XCUBasedAcceleratorConfiguration::removeKeyFromConfiguration( const css::awt::KeyEvent& aKeyEvent, const bool bPreferred )
+{
+ css::uno::Reference< css::container::XNameAccess > xAccess;
+ css::uno::Reference< css::container::XNameContainer > xContainer;
+
+ if ( bPreferred )
+ m_xCfg->getByName(CFG_ENTRY_PRIMARY) >>= xAccess;
+ else
+ m_xCfg->getByName(CFG_ENTRY_SECONDARY) >>= xAccess;
+
+ if ( m_sGlobalOrModules == CFG_ENTRY_GLOBAL )
+ xAccess->getByName(CFG_ENTRY_GLOBAL) >>= xContainer;
+ else if ( m_sGlobalOrModules == CFG_ENTRY_MODULES )
+ {
+ css::uno::Reference< css::container::XNameAccess > xModules;
+ xAccess->getByName(CFG_ENTRY_MODULES) >>= xModules;
+ if ( !xModules->hasByName(m_sModuleCFG) )
+ return;
+ xModules->getByName(m_sModuleCFG) >>= xContainer;
+ }
+
+ const OUString sKey = lcl_getKeyString(aKeyEvent);
+ xContainer->removeByName(sKey);
+}
+
+void XCUBasedAcceleratorConfiguration::reloadChanged( const OUString& sPrimarySecondary, std::u16string_view sGlobalModules, const OUString& sModule, const OUString& sKey )
+{
+ css::uno::Reference< css::container::XNameAccess > xAccess;
+ css::uno::Reference< css::container::XNameContainer > xContainer;
+
+ m_xCfg->getByName(sPrimarySecondary) >>= xAccess;
+ if ( sGlobalModules == CFG_ENTRY_GLOBAL )
+ xAccess->getByName(CFG_ENTRY_GLOBAL) >>= xContainer;
+ else
+ {
+ css::uno::Reference< css::container::XNameAccess > xModules;
+ xAccess->getByName(CFG_ENTRY_MODULES) >>= xModules;
+ if ( !xModules->hasByName(sModule) )
+ return;
+ xModules->getByName(sModule) >>= xContainer;
+ }
+
+ css::awt::KeyEvent aKeyEvent;
+
+ sal_Int32 nIndex = 0;
+ std::u16string_view sKeyIdentifier = o3tl::getToken(sKey, 0, '_', nIndex);
+ aKeyEvent.KeyCode = KeyMapping::get().mapIdentifierToCode(OUString::Concat("KEY_") + sKeyIdentifier);
+
+ const int nToken = 4;
+ for (sal_Int32 i = 0; i < nToken; ++i)
+ {
+ if ( nIndex < 0 )
+ break;
+
+ std::u16string_view sToken = o3tl::getToken(sKey, 0, '_', nIndex);
+ if ( sToken == u"SHIFT" )
+ aKeyEvent.Modifiers |= css::awt::KeyModifier::SHIFT;
+ else if ( sToken == u"MOD1" )
+ aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD1;
+ else if ( sToken == u"MOD2" )
+ aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD2;
+ else if ( sToken == u"MOD3" )
+ aKeyEvent.Modifiers |= css::awt::KeyModifier::MOD3;
+ }
+
+ css::uno::Reference< css::container::XNameAccess > xKey;
+ css::uno::Reference< css::container::XNameAccess > xCommand;
+ OUString sCommand;
+
+ if (xContainer->hasByName(sKey))
+ {
+ OUString sLocale = impl_ts_getLocale();
+ xContainer->getByName(sKey) >>= xKey;
+ xKey->getByName(CFG_PROP_COMMAND) >>= xCommand;
+ xCommand->getByName(sLocale) >>= sCommand;
+ }
+
+ if ( sPrimarySecondary == CFG_ENTRY_PRIMARY )
+ {
+ if (sCommand.isEmpty())
+ m_aPrimaryReadCache.removeKey(aKeyEvent);
+ else
+ m_aPrimaryReadCache.setKeyCommandPair(aKeyEvent, sCommand);
+ }
+ else if ( sPrimarySecondary == CFG_ENTRY_SECONDARY )
+ {
+ if (sCommand.isEmpty())
+ m_aSecondaryReadCache.removeKey(aKeyEvent);
+ else
+ m_aSecondaryReadCache.setKeyCommandPair(aKeyEvent, sCommand);
+ }
+}
+
+AcceleratorCache& XCUBasedAcceleratorConfiguration::impl_getCFG(bool bPreferred, bool bWriteAccessRequested)
+{
+ SolarMutexGuard g;
+
+ if (bPreferred)
+ {
+ //create copy of our readonly-cache, if write access is forced ... but
+ //not still possible!
+ if ( bWriteAccessRequested && !m_pPrimaryWriteCache )
+ {
+ m_pPrimaryWriteCache.reset(new AcceleratorCache(m_aPrimaryReadCache));
+ }
+
+ // in case, we have a writeable cache, we use it for reading too!
+ // Otherwise the API user can't find its own changes...
+ if (m_pPrimaryWriteCache)
+ return *m_pPrimaryWriteCache;
+ else
+ return m_aPrimaryReadCache;
+ }
+
+ else
+ {
+ //create copy of our readonly-cache, if write access is forced ... but
+ //not still possible!
+ if ( bWriteAccessRequested && !m_pSecondaryWriteCache )
+ {
+ m_pSecondaryWriteCache.reset(new AcceleratorCache(m_aSecondaryReadCache));
+ }
+
+ // in case, we have a writeable cache, we use it for reading too!
+ // Otherwise the API user can't find its own changes...
+ if (m_pSecondaryWriteCache)
+ return *m_pSecondaryWriteCache;
+ else
+ return m_aSecondaryReadCache;
+ }
+}
+
+OUString XCUBasedAcceleratorConfiguration::impl_ts_getLocale() const
+{
+ OUString sISOLocale = officecfg::Setup::L10N::ooLocale::get();
+
+ if (sISOLocale.isEmpty())
+ return "en-US";
+ return sISOLocale;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/accelerators/documentacceleratorconfiguration.cxx b/framework/source/accelerators/documentacceleratorconfiguration.cxx
new file mode 100644
index 000000000..c86895f0d
--- /dev/null
+++ b/framework/source/accelerators/documentacceleratorconfiguration.cxx
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <accelerators/acceleratorconfiguration.hxx>
+#include <accelerators/presethandler.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace framework;
+
+constexpr OUStringLiteral RESOURCETYPE_ACCELERATOR = u"accelerator";
+
+namespace {
+
+/**
+ implements a read/write access to a document
+ based accelerator configuration.
+ */
+
+typedef ::cppu::ImplInheritanceHelper<
+ XMLBasedAcceleratorConfiguration,
+ css::lang::XServiceInfo> DocumentAcceleratorConfiguration_BASE;
+
+class DocumentAcceleratorConfiguration : public DocumentAcceleratorConfiguration_BASE
+{
+private:
+
+ /** points to the root storage of the outside document,
+ where we can read/save our configuration data. */
+ css::uno::Reference< css::embed::XStorage > m_xDocumentRoot;
+
+public:
+
+ /** initialize this instance and fill the internal cache.
+
+ @param xSMGR
+ reference to a uno service manager, which is used internally.
+ */
+ DocumentAcceleratorConfiguration(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Sequence< css::uno::Any >& lArguments);
+
+ virtual ~DocumentAcceleratorConfiguration() override;
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.DocumentAcceleratorConfiguration";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.ui.DocumentAcceleratorConfiguration"};
+ }
+
+ // XUIConfigurationStorage
+ virtual void SAL_CALL setStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) override;
+
+ virtual sal_Bool SAL_CALL hasStorage() override;
+
+ /** read all data into the cache. */
+ void fillCache();
+};
+
+DocumentAcceleratorConfiguration::DocumentAcceleratorConfiguration(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Sequence< css::uno::Any >& lArguments)
+ : DocumentAcceleratorConfiguration_BASE(xContext)
+{
+ SolarMutexGuard g;
+ css::uno::Reference<css::embed::XStorage> xRoot;
+ if (lArguments.getLength() == 1 && (lArguments[0] >>= xRoot))
+ {
+ m_xDocumentRoot = xRoot;
+ }
+ else
+ {
+ ::comphelper::SequenceAsHashMap lArgs(lArguments);
+ m_xDocumentRoot = lArgs.getUnpackedValueOrDefault(
+ "DocumentRoot",
+ css::uno::Reference< css::embed::XStorage >());
+ }
+}
+
+DocumentAcceleratorConfiguration::~DocumentAcceleratorConfiguration()
+{
+ m_aPresetHandler.removeStorageListener(this);
+}
+
+void SAL_CALL DocumentAcceleratorConfiguration::setStorage(const css::uno::Reference< css::embed::XStorage >& xStorage)
+{
+ // Attention! xStorage must be accepted too, if it's NULL !
+
+ bool bForgetOldStorages;
+ {
+ SolarMutexGuard g;
+ bForgetOldStorages = m_xDocumentRoot.is();
+ m_xDocumentRoot = xStorage;
+ }
+
+ if (bForgetOldStorages)
+ /* forget all currently cached data AND(!) forget all currently used storages. */
+ m_aPresetHandler.forgetCachedStorages();
+
+ if (xStorage.is())
+ fillCache();
+}
+
+sal_Bool SAL_CALL DocumentAcceleratorConfiguration::hasStorage()
+{
+ SolarMutexGuard g;
+ return m_xDocumentRoot.is();
+}
+
+void DocumentAcceleratorConfiguration::fillCache()
+{
+ css::uno::Reference< css::embed::XStorage > xDocumentRoot;
+ {
+ SolarMutexGuard g;
+ xDocumentRoot = m_xDocumentRoot;
+ }
+
+ // Sometimes we must live without a document root.
+ // E.g. if the document is readonly ...
+ if (!xDocumentRoot.is())
+ return;
+
+ // get current office locale ... but don't cache it.
+ // Otherwise we must be listener on the configuration layer
+ // which seems to superfluous for this small implementation .-)
+ LanguageTag aLanguageTag( impl_ts_getLocale());
+
+ // May be the current document does not contain any
+ // accelerator config? Handle it gracefully :-)
+ try
+ {
+ // Note: The used preset class is threadsafe by itself ... and live if we live!
+ // We do not need any mutex here.
+
+ // open the folder, where the configuration exists
+ m_aPresetHandler.connectToResource(
+ PresetHandler::E_DOCUMENT,
+ RESOURCETYPE_ACCELERATOR,
+ u"",
+ xDocumentRoot,
+ aLanguageTag);
+
+ DocumentAcceleratorConfiguration::reload();
+ m_aPresetHandler.addStorageListener(this);
+ }
+ catch(const css::uno::Exception&)
+ {}
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_DocumentAcceleratorConfiguration_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &arguments)
+{
+ rtl::Reference<DocumentAcceleratorConfiguration> inst = new DocumentAcceleratorConfiguration(context, arguments);
+ css::uno::XInterface *acquired_inst = cppu::acquire(inst.get());
+
+ inst->fillCache();
+
+ return acquired_inst;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/accelerators/globalacceleratorconfiguration.cxx b/framework/source/accelerators/globalacceleratorconfiguration.cxx
new file mode 100644
index 000000000..e54a05a03
--- /dev/null
+++ b/framework/source/accelerators/globalacceleratorconfiguration.cxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <accelerators/acceleratorconfiguration.hxx>
+#include <accelerators/keymapping.hxx>
+#include <helper/mischelper.hxx>
+
+#include <com/sun/star/util/XChangesNotifier.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/ref.hxx>
+
+using namespace framework;
+
+namespace {
+
+/**
+ implements a read/write access to the global
+ accelerator configuration.
+ */
+typedef ::cppu::ImplInheritanceHelper<
+ XCUBasedAcceleratorConfiguration,
+ css::lang::XServiceInfo > GlobalAcceleratorConfiguration_BASE;
+class GlobalAcceleratorConfiguration : public GlobalAcceleratorConfiguration_BASE
+{
+public:
+
+ /** initialize this instance and fill the internal cache.
+
+ @param xSMGR
+ reference to a uno service manager, which is used internally.
+ */
+ explicit GlobalAcceleratorConfiguration(const css::uno::Reference< css::uno::XComponentContext >& xContext);
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.GlobalAcceleratorConfiguration";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.ui.GlobalAcceleratorConfiguration"};
+ }
+
+ /// This has to be called after when the instance is acquire()'d.
+ void fillCache();
+
+private:
+
+ /** helper to listen for configuration changes without ownership cycle problems */
+ css::uno::Reference< css::util::XChangesListener > m_xCfgListener;
+};
+
+GlobalAcceleratorConfiguration::GlobalAcceleratorConfiguration(const css::uno::Reference< css::uno::XComponentContext >& xContext)
+ : GlobalAcceleratorConfiguration_BASE(xContext)
+{
+ // force keyboard string registration.
+ KeyMapping::get();
+}
+
+void GlobalAcceleratorConfiguration::fillCache()
+{
+ /** read all data into the cache. */
+
+#if 0
+ // get current office locale ... but don't cache it.
+ // Otherwise we must be listener on the configuration layer
+ // which seems to superfluous for this small implementation .-)
+ // XXX: what is this good for? it was a comphelper::Locale but unused
+ LanguageTag aLanguageTag(m_sLocale);
+#endif
+
+ // May be there exists no accelerator config? Handle it gracefully :-)
+ try
+ {
+ m_sGlobalOrModules = CFG_ENTRY_GLOBAL;
+ XCUBasedAcceleratorConfiguration::reload();
+
+ css::uno::Reference< css::util::XChangesNotifier > xBroadcaster(m_xCfg, css::uno::UNO_QUERY_THROW);
+ m_xCfgListener = new WeakChangesListener(this);
+ xBroadcaster->addChangesListener(m_xCfgListener);
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ {}
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_GlobalAcceleratorConfiguration_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ rtl::Reference<GlobalAcceleratorConfiguration> xGAC = new GlobalAcceleratorConfiguration(context);
+ xGAC->fillCache();
+ return cppu::acquire(xGAC.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/accelerators/keymapping.cxx b/framework/source/accelerators/keymapping.cxx
new file mode 100644
index 000000000..a0d33e7b4
--- /dev/null
+++ b/framework/source/accelerators/keymapping.cxx
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <accelerators/keymapping.hxx>
+
+#include <com/sun/star/awt/Key.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <o3tl/string_view.hxx>
+
+namespace framework
+{
+
+// helper
+
+KeyMapping::KeyIdentifierInfo const KeyMapping::KeyIdentifierMap[] =
+{
+ {css::awt::Key::NUM0 , "KEY_0" },
+ {css::awt::Key::NUM1 , "KEY_1" },
+ {css::awt::Key::NUM2 , "KEY_2" },
+ {css::awt::Key::NUM3 , "KEY_3" },
+ {css::awt::Key::NUM4 , "KEY_4" },
+ {css::awt::Key::NUM5 , "KEY_5" },
+ {css::awt::Key::NUM6 , "KEY_6" },
+ {css::awt::Key::NUM7 , "KEY_7" },
+ {css::awt::Key::NUM8 , "KEY_8" },
+ {css::awt::Key::NUM9 , "KEY_9" },
+ {css::awt::Key::A , "KEY_A" },
+ {css::awt::Key::B , "KEY_B" },
+ {css::awt::Key::C , "KEY_C" },
+ {css::awt::Key::D , "KEY_D" },
+ {css::awt::Key::E , "KEY_E" },
+ {css::awt::Key::F , "KEY_F" },
+ {css::awt::Key::G , "KEY_G" },
+ {css::awt::Key::H , "KEY_H" },
+ {css::awt::Key::I , "KEY_I" },
+ {css::awt::Key::J , "KEY_J" },
+ {css::awt::Key::K , "KEY_K" },
+ {css::awt::Key::L , "KEY_L" },
+ {css::awt::Key::M , "KEY_M" },
+ {css::awt::Key::N , "KEY_N" },
+ {css::awt::Key::O , "KEY_O" },
+ {css::awt::Key::P , "KEY_P" },
+ {css::awt::Key::Q , "KEY_Q" },
+ {css::awt::Key::R , "KEY_R" },
+ {css::awt::Key::S , "KEY_S" },
+ {css::awt::Key::T , "KEY_T" },
+ {css::awt::Key::U , "KEY_U" },
+ {css::awt::Key::V , "KEY_V" },
+ {css::awt::Key::W , "KEY_W" },
+ {css::awt::Key::X , "KEY_X" },
+ {css::awt::Key::Y , "KEY_Y" },
+ {css::awt::Key::Z , "KEY_Z" },
+ {css::awt::Key::F1 , "KEY_F1" },
+ {css::awt::Key::F2 , "KEY_F2" },
+ {css::awt::Key::F3 , "KEY_F3" },
+ {css::awt::Key::F4 , "KEY_F4" },
+ {css::awt::Key::F5 , "KEY_F5" },
+ {css::awt::Key::F6 , "KEY_F6" },
+ {css::awt::Key::F7 , "KEY_F7" },
+ {css::awt::Key::F8 , "KEY_F8" },
+ {css::awt::Key::F9 , "KEY_F9" },
+ {css::awt::Key::F10 , "KEY_F10" },
+ {css::awt::Key::F11 , "KEY_F11" },
+ {css::awt::Key::F12 , "KEY_F12" },
+ {css::awt::Key::F13 , "KEY_F13" },
+ {css::awt::Key::F14 , "KEY_F14" },
+ {css::awt::Key::F15 , "KEY_F15" },
+ {css::awt::Key::F16 , "KEY_F16" },
+ {css::awt::Key::F17 , "KEY_F17" },
+ {css::awt::Key::F18 , "KEY_F18" },
+ {css::awt::Key::F19 , "KEY_F19" },
+ {css::awt::Key::F20 , "KEY_F20" },
+ {css::awt::Key::F21 , "KEY_F21" },
+ {css::awt::Key::F22 , "KEY_F22" },
+ {css::awt::Key::F23 , "KEY_F23" },
+ {css::awt::Key::F24 , "KEY_F24" },
+ {css::awt::Key::F25 , "KEY_F25" },
+ {css::awt::Key::F26 , "KEY_F26" },
+ {css::awt::Key::DOWN , "KEY_DOWN" },
+ {css::awt::Key::UP , "KEY_UP" },
+ {css::awt::Key::LEFT , "KEY_LEFT" },
+ {css::awt::Key::RIGHT , "KEY_RIGHT" },
+ {css::awt::Key::HOME , "KEY_HOME" },
+ {css::awt::Key::END , "KEY_END" },
+ {css::awt::Key::PAGEUP , "KEY_PAGEUP" },
+ {css::awt::Key::PAGEDOWN , "KEY_PAGEDOWN" },
+ {css::awt::Key::RETURN , "KEY_RETURN" },
+ {css::awt::Key::ESCAPE , "KEY_ESCAPE" },
+ {css::awt::Key::TAB , "KEY_TAB" },
+ {css::awt::Key::BACKSPACE , "KEY_BACKSPACE" },
+ {css::awt::Key::SPACE , "KEY_SPACE" },
+ {css::awt::Key::INSERT , "KEY_INSERT" },
+ {css::awt::Key::DELETE , "KEY_DELETE" },
+ {css::awt::Key::ADD , "KEY_ADD" },
+ {css::awt::Key::SUBTRACT , "KEY_SUBTRACT" },
+ {css::awt::Key::MULTIPLY , "KEY_MULTIPLY" },
+ {css::awt::Key::DIVIDE , "KEY_DIVIDE" },
+ {css::awt::Key::POINT , "KEY_POINT" },
+ {css::awt::Key::COMMA , "KEY_COMMA" },
+ {css::awt::Key::LESS , "KEY_LESS" },
+ {css::awt::Key::GREATER , "KEY_GREATER" },
+ {css::awt::Key::EQUAL , "KEY_EQUAL" },
+ {css::awt::Key::OPEN , "KEY_OPEN" },
+ {css::awt::Key::CUT , "KEY_CUT" },
+ {css::awt::Key::COPY , "KEY_COPY" },
+ {css::awt::Key::PASTE , "KEY_PASTE" },
+ {css::awt::Key::UNDO , "KEY_UNDO" },
+ {css::awt::Key::REPEAT , "KEY_REPEAT" },
+ {css::awt::Key::FIND , "KEY_FIND" },
+ {css::awt::Key::PROPERTIES , "KEY_PROPERTIES" },
+ {css::awt::Key::FRONT , "KEY_FRONT" },
+ {css::awt::Key::CONTEXTMENU , "KEY_CONTEXTMENU"},
+ {css::awt::Key::HELP , "KEY_HELP" },
+ {css::awt::Key::MENU , "KEY_MENU" },
+ {css::awt::Key::HANGUL_HANJA , "KEY_HANGUL_HANJA"},
+ {css::awt::Key::DECIMAL , "KEY_DECIMAL" },
+ {css::awt::Key::TILDE , "KEY_TILDE" },
+ {css::awt::Key::QUOTELEFT , "KEY_QUOTELEFT" },
+ {css::awt::Key::BRACKETLEFT , "KEY_BRACKETLEFT" },
+ {css::awt::Key::BRACKETRIGHT , "KEY_BRACKETRIGHT" },
+ {css::awt::Key::SEMICOLON , "KEY_SEMICOLON" },
+ {css::awt::Key::QUOTERIGHT , "KEY_QUOTERIGHT" },
+ {0 , "" } // mark the end of this array!
+};
+
+KeyMapping::KeyMapping()
+{
+ sal_Int32 i = 0;
+ while(KeyIdentifierMap[i].Code != 0)
+ {
+ OUString sIdentifier = OUString::createFromAscii(KeyIdentifierMap[i].Identifier);
+ sal_Int16 nCode = KeyIdentifierMap[i].Code;
+
+ m_lIdentifierHash[sIdentifier] = nCode;
+ m_lCodeHash [nCode] = sIdentifier;
+
+ ++i;
+ }
+}
+
+KeyMapping & KeyMapping::get() {
+ static KeyMapping KEYS;
+ return KEYS;
+}
+
+sal_uInt16 KeyMapping::mapIdentifierToCode(const OUString& sIdentifier)
+{
+ Identifier2CodeHash::const_iterator pIt = m_lIdentifierHash.find(sIdentifier);
+ if (pIt != m_lIdentifierHash.end())
+ return pIt->second;
+
+ // It's not well known identifier - but may be a pure key code formatted as string...
+ // Check and convert it!
+ sal_uInt16 nCode = 0;
+ if (!KeyMapping::impl_st_interpretIdentifierAsPureKeyCode(sIdentifier, nCode))
+ throw css::lang::IllegalArgumentException(
+ "Can not map given identifier to a valid key code value.",
+ css::uno::Reference< css::uno::XInterface >(),
+ 0);
+
+ return nCode;
+}
+
+OUString KeyMapping::mapCodeToIdentifier(sal_uInt16 nCode)
+{
+ Code2IdentifierHash::const_iterator pIt = m_lCodeHash.find(nCode);
+ if (pIt != m_lCodeHash.end())
+ return pIt->second;
+
+ // If we have no well known identifier - use the pure code value!
+ return OUString::number(nCode);
+}
+
+bool KeyMapping::impl_st_interpretIdentifierAsPureKeyCode(std::u16string_view sIdentifier,
+ sal_uInt16& rCode )
+{
+ sal_Int32 nCode = o3tl::toInt32(sIdentifier);
+ if (nCode > 0)
+ {
+ rCode = static_cast<sal_uInt16>(nCode);
+ return true;
+ }
+
+ // 0 is normally an error of the called method toInt32() ...
+ // But we must be aware, that the identifier is "0"!
+ rCode = 0;
+ return sIdentifier == u"0";
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/accelerators/moduleacceleratorconfiguration.cxx b/framework/source/accelerators/moduleacceleratorconfiguration.cxx
new file mode 100644
index 000000000..c8ce39fec
--- /dev/null
+++ b/framework/source/accelerators/moduleacceleratorconfiguration.cxx
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <accelerators/acceleratorconfiguration.hxx>
+#include <helper/mischelper.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/util/XChangesNotifier.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+using namespace framework;
+
+namespace {
+
+/**
+ implements a read/write access to a module
+ dependent accelerator configuration.
+ */
+typedef ::cppu::ImplInheritanceHelper<
+ XCUBasedAcceleratorConfiguration,
+ css::lang::XServiceInfo > ModuleAcceleratorConfiguration_BASE;
+
+class ModuleAcceleratorConfiguration : public ModuleAcceleratorConfiguration_BASE
+{
+private:
+ /** identify the application module, where this accelerator
+ configuration cache should work on. */
+ OUString m_sModule;
+
+public:
+
+ /** initialize this instance and fill the internal cache.
+
+ @param xSMGR
+ reference to a uno service manager, which is used internally.
+ */
+ ModuleAcceleratorConfiguration(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Sequence< css::uno::Any >& lArguments);
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.ModuleAcceleratorConfiguration";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.ui.ModuleAcceleratorConfiguration"};
+ }
+
+ /// This has to be called after when the instance is acquire()'d.
+ void fillCache();
+
+private:
+ /** helper to listen for configuration changes without ownership cycle problems */
+ css::uno::Reference< css::util::XChangesListener > m_xCfgListener;
+};
+
+ModuleAcceleratorConfiguration::ModuleAcceleratorConfiguration(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Sequence< css::uno::Any >& lArguments)
+ : ModuleAcceleratorConfiguration_BASE(xContext)
+{
+ SolarMutexGuard g;
+
+ OUString sModule;
+ if (lArguments.getLength() == 1 && (lArguments[0] >>= sModule))
+ {
+ m_sModule = sModule;
+ } else
+ {
+ ::comphelper::SequenceAsHashMap lArgs(lArguments);
+ m_sModule = lArgs.getUnpackedValueOrDefault("ModuleIdentifier", OUString());
+ // OUString sLocale = lArgs.getUnpackedValueOrDefault("Locale", OUString("x-default"));
+ }
+
+ if (m_sModule.isEmpty())
+ throw css::uno::RuntimeException(
+ "The module dependent accelerator configuration service was initialized with an empty module identifier!",
+ static_cast< ::cppu::OWeakObject* >(this));
+}
+
+void ModuleAcceleratorConfiguration::fillCache()
+{
+ {
+ SolarMutexGuard g;
+ m_sModuleCFG = m_sModule;
+ }
+
+#if 0
+ // get current office locale ... but don't cache it.
+ // Otherwise we must be listener on the configuration layer
+ // which seems to superfluous for this small implementation .-)
+ // XXX: what is this good for? it was a comphelper::Locale but unused
+ LanguageTag aLanguageTag(m_sLocale);
+#endif
+
+ // May be the current app module does not have any
+ // accelerator config? Handle it gracefully :-)
+ try
+ {
+ m_sGlobalOrModules = CFG_ENTRY_MODULES;
+ XCUBasedAcceleratorConfiguration::reload();
+
+ css::uno::Reference< css::util::XChangesNotifier > xBroadcaster(m_xCfg, css::uno::UNO_QUERY_THROW);
+ m_xCfgListener = new WeakChangesListener(this);
+ xBroadcaster->addChangesListener(m_xCfgListener);
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ {}
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_ModuleAcceleratorConfiguration_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &arguments)
+{
+ rtl::Reference<ModuleAcceleratorConfiguration> inst = new ModuleAcceleratorConfiguration(context, arguments);
+ css::uno::XInterface *acquired_inst = cppu::acquire(inst.get());
+
+ inst->fillCache();
+
+ return acquired_inst;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/accelerators/presethandler.cxx b/framework/source/accelerators/presethandler.cxx
new file mode 100644
index 000000000..7bd487684
--- /dev/null
+++ b/framework/source/accelerators/presethandler.cxx
@@ -0,0 +1,727 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <accelerators/presethandler.hxx>
+
+#include <classes/fwkresid.hxx>
+
+#include <strings.hrc>
+
+#include <com/sun/star/configuration/CorruptedUIConfigurationException.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/FileSystemStorageFactory.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/util/thePathSettings.hpp>
+
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <i18nlangtag/languagetag.hxx>
+
+const ::sal_Int32 ID_CORRUPT_UICONFIG_SHARE = 1;
+const ::sal_Int32 ID_CORRUPT_UICONFIG_USER = 2;
+const ::sal_Int32 ID_CORRUPT_UICONFIG_GENERAL = 3;
+
+namespace framework
+{
+
+namespace {
+
+/** @short because a concurrent access to the same storage from different implementations
+ isn't supported, we have to share it with others.
+
+ @descr This struct is allegedly shared and must be used within a
+ synchronized section. But it isn't.
+ */
+struct TSharedStorages final
+{
+ StorageHolder m_lStoragesShare;
+ StorageHolder m_lStoragesUser;
+
+ TSharedStorages()
+ {};
+};
+
+/** @short provides access to the:
+ a) shared root storages
+ b) shared "inbetween" storages
+ of the share and user layer. */
+struct SharedStorages: public rtl::Static<TSharedStorages, SharedStorages> {};
+
+}
+
+PresetHandler::PresetHandler(css::uno::Reference< css::uno::XComponentContext > xContext)
+ : m_xContext(std::move(xContext))
+ , m_eConfigType(E_GLOBAL)
+{
+}
+
+PresetHandler::PresetHandler(const PresetHandler& rCopy)
+{
+ m_xContext = rCopy.m_xContext;
+ m_eConfigType = rCopy.m_eConfigType;
+ m_xWorkingStorageShare = rCopy.m_xWorkingStorageShare;
+ m_xWorkingStorageNoLang = rCopy.m_xWorkingStorageNoLang;
+ m_xWorkingStorageUser = rCopy.m_xWorkingStorageUser;
+ m_lDocumentStorages = rCopy.m_lDocumentStorages;
+ m_sRelPathShare = rCopy.m_sRelPathShare;
+ m_sRelPathUser = rCopy.m_sRelPathUser;
+}
+
+PresetHandler::~PresetHandler()
+{
+ m_xWorkingStorageShare.clear();
+ m_xWorkingStorageNoLang.clear();
+ m_xWorkingStorageUser.clear();
+
+ /* #i46497#
+ Don't call forgetCachedStorages() here for shared storages.
+ Because we opened different sub storages by using openPath().
+ And every already open path was reused and referenced (means it's
+ ref count was increased!)
+ So now we have to release our ref counts to these shared storages
+ only ... and not to free all used storages.
+ Otherwise we will disconnect all other open configuration access
+ objects which base on these storages.
+ */
+ auto & sharedStorages = SharedStorages::get();
+ sharedStorages.m_lStoragesShare.closePath(m_sRelPathShare);
+ sharedStorages.m_lStoragesUser.closePath (m_sRelPathUser );
+
+ /* On the other side closePath() is not needed for our special handled
+ document storage. Because it's not shared with others ... so we can
+ free it.
+ */
+ m_lDocumentStorages.forgetCachedStorages();
+}
+
+void PresetHandler::forgetCachedStorages()
+{
+ SolarMutexGuard g;
+
+ if (m_eConfigType == E_DOCUMENT)
+ {
+ m_xWorkingStorageShare.clear();
+ m_xWorkingStorageNoLang.clear();
+ m_xWorkingStorageUser.clear();
+ }
+
+ m_lDocumentStorages.forgetCachedStorages();
+}
+
+namespace {
+
+OUString lcl_getLocalizedMessage(::sal_Int32 nID)
+{
+ OUString sMessage("Unknown error.");
+
+ switch(nID)
+ {
+ case ID_CORRUPT_UICONFIG_SHARE :
+ sMessage = FwkResId(STR_CORRUPT_UICFG_SHARE);
+
+ break;
+
+ case ID_CORRUPT_UICONFIG_USER :
+ sMessage = FwkResId(STR_CORRUPT_UICFG_USER);
+ break;
+
+ case ID_CORRUPT_UICONFIG_GENERAL :
+ sMessage = FwkResId(STR_CORRUPT_UICFG_GENERAL);
+ break;
+ }
+
+ return sMessage;
+}
+
+void lcl_throwCorruptedUIConfigurationException(
+ css::uno::Any const & exception, sal_Int32 id)
+{
+ css::uno::Exception e;
+ bool ok = (exception >>= e);
+ OSL_ASSERT(ok);
+ throw css::configuration::CorruptedUIConfigurationException(
+ lcl_getLocalizedMessage(id),
+ css::uno::Reference< css::uno::XInterface >(),
+ exception.getValueTypeName() + ": \"" + e.Message + "\"");
+}
+
+}
+
+css::uno::Reference< css::embed::XStorage > PresetHandler::getOrCreateRootStorageShare()
+{
+ auto & sharedStorages = SharedStorages::get();
+ css::uno::Reference< css::embed::XStorage > xRoot = sharedStorages.m_lStoragesShare.getRootStorage();
+ if (xRoot.is())
+ return xRoot;
+
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ {
+ SolarMutexGuard g;
+ xContext = m_xContext;
+ }
+
+ css::uno::Reference< css::util::XPathSettings > xPathSettings =
+ css::util::thePathSettings::get( xContext );
+
+ OUString sShareLayer = xPathSettings->getBasePathShareLayer();
+
+ // "UIConfig" is a "multi path" ... use first part only here!
+ sal_Int32 nPos = sShareLayer.indexOf(';');
+ if (nPos > 0)
+ sShareLayer = sShareLayer.copy(0, nPos);
+
+ // Note: May be an user uses URLs without a final slash! Check it ...
+ nPos = sShareLayer.lastIndexOf('/');
+ if (nPos != sShareLayer.getLength()-1)
+ sShareLayer += "/";
+
+ sShareLayer += "soffice.cfg";
+ /*
+ // TODO remove me!
+ // Attention: This is temp. workaround ... We create a temp. storage file
+ // based of a system directory. This must be used so, till the storage implementation
+ // can work on directories too.
+ */
+ css::uno::Sequence< css::uno::Any > lArgs{
+ css::uno::Any(sShareLayer),
+ css::uno::Any(css::embed::ElementModes::READ | css::embed::ElementModes::NOCREATE)
+ };
+
+ css::uno::Reference< css::lang::XSingleServiceFactory > xStorageFactory = css::embed::FileSystemStorageFactory::create( xContext );
+ css::uno::Reference< css::embed::XStorage > xStorage;
+
+ try
+ {
+ xStorage.set(xStorageFactory->createInstanceWithArguments(lArgs), css::uno::UNO_QUERY_THROW);
+ }
+ catch(const css::uno::Exception&)
+ {
+ css::uno::Any ex(cppu::getCaughtException());
+ lcl_throwCorruptedUIConfigurationException(
+ ex, ID_CORRUPT_UICONFIG_SHARE);
+ }
+
+ sharedStorages.m_lStoragesShare.setRootStorage(xStorage);
+
+ return xStorage;
+}
+
+css::uno::Reference< css::embed::XStorage > PresetHandler::getOrCreateRootStorageUser()
+{
+ auto & sharedStorages = SharedStorages::get();
+ css::uno::Reference< css::embed::XStorage > xRoot = sharedStorages.m_lStoragesUser.getRootStorage();
+ if (xRoot.is())
+ return xRoot;
+
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ {
+ SolarMutexGuard g;
+ xContext = m_xContext;
+ }
+
+ css::uno::Reference< css::util::XPathSettings > xPathSettings =
+ css::util::thePathSettings::get( xContext );
+
+ OUString sUserLayer = xPathSettings->getBasePathUserLayer();
+
+ // Note: May be an user uses URLs without a final slash! Check it ...
+ sal_Int32 nPos = sUserLayer.lastIndexOf('/');
+ if (nPos != sUserLayer.getLength()-1)
+ sUserLayer += "/";
+
+ sUserLayer += "soffice.cfg"; // storage file
+
+ css::uno::Sequence< css::uno::Any > lArgs{ css::uno::Any(sUserLayer),
+ css::uno::Any(css::embed::ElementModes::READWRITE) };
+
+ css::uno::Reference< css::lang::XSingleServiceFactory > xStorageFactory = css::embed::FileSystemStorageFactory::create( xContext );
+ css::uno::Reference< css::embed::XStorage > xStorage;
+
+ try
+ {
+ xStorage.set(xStorageFactory->createInstanceWithArguments(lArgs), css::uno::UNO_QUERY_THROW);
+ }
+ catch(const css::uno::Exception&)
+ {
+ css::uno::Any ex(cppu::getCaughtException());
+ lcl_throwCorruptedUIConfigurationException(
+ ex, ID_CORRUPT_UICONFIG_USER);
+ }
+
+ sharedStorages.m_lStoragesUser.setRootStorage(xStorage);
+
+ return xStorage;
+}
+
+css::uno::Reference< css::embed::XStorage > PresetHandler::getWorkingStorageUser() const
+{
+ SolarMutexGuard g;
+ return m_xWorkingStorageUser;
+}
+
+css::uno::Reference< css::embed::XStorage > PresetHandler::getParentStorageShare()
+{
+ css::uno::Reference< css::embed::XStorage > xWorking;
+ {
+ SolarMutexGuard g;
+ xWorking = m_xWorkingStorageShare;
+ }
+
+ return SharedStorages::get().m_lStoragesShare.getParentStorage(xWorking);
+}
+
+css::uno::Reference< css::embed::XStorage > PresetHandler::getParentStorageUser()
+{
+ css::uno::Reference< css::embed::XStorage > xWorking;
+ {
+ SolarMutexGuard g;
+ xWorking = m_xWorkingStorageUser;
+ }
+
+ return SharedStorages::get().m_lStoragesUser.getParentStorage(xWorking);
+}
+
+void PresetHandler::connectToResource( PresetHandler::EConfigType eConfigType ,
+ std::u16string_view sResource ,
+ std::u16string_view sModule ,
+ const css::uno::Reference< css::embed::XStorage >& xDocumentRoot,
+ const LanguageTag& rLanguageTag )
+{
+ // TODO free all current open storages!
+
+ {
+ SolarMutexGuard g;
+ m_eConfigType = eConfigType;
+ }
+
+ css::uno::Reference< css::embed::XStorage > xShare;
+ css::uno::Reference< css::embed::XStorage > xNoLang;
+ css::uno::Reference< css::embed::XStorage > xUser;
+
+ // special case for documents
+ // use outside root storage, if we run in E_DOCUMENT mode!
+ if (eConfigType == E_DOCUMENT)
+ {
+ if (!xDocumentRoot.is())
+ throw css::uno::RuntimeException(
+ "There is valid root storage, where the UI configuration can work on.");
+ m_lDocumentStorages.setRootStorage(xDocumentRoot);
+ xShare = xDocumentRoot;
+ xUser = xDocumentRoot;
+ }
+ else
+ {
+ xShare = getOrCreateRootStorageShare();
+ xUser = getOrCreateRootStorageUser();
+ }
+
+ // #...#
+ try
+ {
+
+ // a) inside share layer we should not create any new structures... We have to use
+ // existing ones only!
+ // b) inside user layer we can (SOFT mode!) but sometimes we should not (HARD mode!)
+ // create new empty structures. We should prefer using of any existing structure.
+ sal_Int32 eShareMode = (css::embed::ElementModes::READ | css::embed::ElementModes::NOCREATE);
+ sal_Int32 eUserMode = css::embed::ElementModes::READWRITE;
+
+ OUStringBuffer sRelPathBuf(1024);
+ OUString sRelPathShare;
+ OUString sRelPathUser;
+ switch(eConfigType)
+ {
+ case E_GLOBAL :
+ {
+ sRelPathShare = OUString::Concat("global/") + sResource;
+ sRelPathUser = sRelPathShare;
+
+ xShare = impl_openPathIgnoringErrors(sRelPathShare, eShareMode, true );
+ xUser = impl_openPathIgnoringErrors(sRelPathUser , eUserMode , false);
+ }
+ break;
+
+ case E_MODULES :
+ {
+ sRelPathShare = OUString::Concat("modules/") + sModule + "/" + sResource;
+ sRelPathUser = sRelPathShare;
+
+ xShare = impl_openPathIgnoringErrors(sRelPathShare, eShareMode, true );
+ xUser = impl_openPathIgnoringErrors(sRelPathUser , eUserMode , false);
+ }
+ break;
+
+ case E_DOCUMENT :
+ {
+ // A document does not have a share layer in real.
+ // It has one layer only, and this one should be opened READ_WRITE.
+ // So we open the user layer here only and set the share layer equals to it .-)
+
+ sRelPathBuf.append(sResource);
+ sRelPathUser = sRelPathBuf.makeStringAndClear();
+ sRelPathShare = sRelPathUser;
+
+ try
+ {
+ xUser = m_lDocumentStorages.openPath(sRelPathUser , eUserMode );
+ xShare = xUser;
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { xShare.clear(); xUser.clear(); }
+ }
+ break;
+ }
+
+ // Non-localized global share
+ xNoLang = xShare;
+
+ if (
+ (rLanguageTag != LanguageTag(LANGUAGE_USER_PRIV_NOTRANSLATE)) && // localized level?
+ (eConfigType != E_DOCUMENT ) // no localization in document mode!
+ )
+ {
+ // First try to find the right localized set inside share layer.
+ // Fallbacks are allowed there.
+ OUString aShareLocale( rLanguageTag.getBcp47());
+ OUString sLocalizedSharePath(sRelPathShare);
+ bool bAllowFallbacks = true;
+ xShare = impl_openLocalizedPathIgnoringErrors(sLocalizedSharePath, eShareMode, true , aShareLocale, bAllowFallbacks);
+
+ // The try to locate the right sub dir inside user layer ... without using fallbacks!
+ // Normally the corresponding sub dir should be created matching the specified locale.
+ // Because we allow creation of storages inside user layer by default.
+ OUString aUserLocale( rLanguageTag.getBcp47());
+ OUString sLocalizedUserPath(sRelPathUser);
+ bAllowFallbacks = false;
+ xUser = impl_openLocalizedPathIgnoringErrors(sLocalizedUserPath, eUserMode, false, aUserLocale, bAllowFallbacks);
+
+ sRelPathShare = sLocalizedSharePath;
+ sRelPathUser = sLocalizedUserPath;
+ }
+
+ {
+ SolarMutexGuard g;
+ m_xWorkingStorageShare = xShare;
+ m_xWorkingStorageNoLang= xNoLang;
+ m_xWorkingStorageUser = xUser;
+ m_sRelPathShare = sRelPathShare;
+ m_sRelPathUser = sRelPathUser;
+ }
+
+ }
+ catch(const css::uno::Exception&)
+ {
+ css::uno::Any ex(cppu::getCaughtException());
+ lcl_throwCorruptedUIConfigurationException(
+ ex, ID_CORRUPT_UICONFIG_GENERAL);
+ }
+}
+
+void PresetHandler::copyPresetToTarget(std::u16string_view sPreset,
+ std::u16string_view sTarget)
+{
+ // don't check our preset list, if element exists
+ // We try to open it and forward all errors to the user!
+
+ css::uno::Reference< css::embed::XStorage > xWorkingShare;
+ css::uno::Reference< css::embed::XStorage > xWorkingNoLang;
+ css::uno::Reference< css::embed::XStorage > xWorkingUser;
+ {
+ SolarMutexGuard g;
+ xWorkingShare = m_xWorkingStorageShare;
+ xWorkingNoLang= m_xWorkingStorageNoLang;
+ xWorkingUser = m_xWorkingStorageUser;
+ }
+
+ // e.g. module without any config data ?!
+ if (
+ (!xWorkingShare.is()) ||
+ (!xWorkingUser.is() )
+ )
+ {
+ return;
+ }
+
+ OUString sPresetFile = OUString::Concat(sPreset) + ".xml";
+ OUString sTargetFile = OUString::Concat(sTarget) + ".xml";
+
+ // remove existing elements before you try to copy the preset to that location ...
+ // Otherwise w will get an ElementExistException inside copyElementTo()!
+ css::uno::Reference< css::container::XNameAccess > xCheckingUser(xWorkingUser, css::uno::UNO_QUERY_THROW);
+ if (xCheckingUser->hasByName(sTargetFile))
+ xWorkingUser->removeElement(sTargetFile);
+
+ xWorkingShare->copyElementTo(sPresetFile, xWorkingUser, sTargetFile);
+
+ // If our storages work in transacted mode, we have
+ // to commit all changes from bottom to top!
+ commitUserChanges();
+}
+
+css::uno::Reference< css::io::XStream > PresetHandler::openPreset(std::u16string_view sPreset)
+{
+ css::uno::Reference< css::embed::XStorage > xFolder;
+ {
+ SolarMutexGuard g;
+ xFolder = m_xWorkingStorageNoLang;
+ }
+
+ // e.g. module without any config data ?!
+ if (!xFolder.is())
+ return css::uno::Reference< css::io::XStream >();
+
+ OUString sFile = OUString::Concat(sPreset) + ".xml";
+
+ // inform user about errors (use original exceptions!)
+ css::uno::Reference< css::io::XStream > xStream = xFolder->openStreamElement(sFile, css::embed::ElementModes::READ);
+ return xStream;
+}
+
+css::uno::Reference< css::io::XStream > PresetHandler::openTarget(
+ std::u16string_view sTarget, sal_Int32 const nMode)
+{
+ css::uno::Reference< css::embed::XStorage > xFolder;
+ {
+ SolarMutexGuard g;
+ xFolder = m_xWorkingStorageUser;
+ }
+
+ // e.g. module without any config data ?!
+ if (!xFolder.is())
+ return css::uno::Reference< css::io::XStream >();
+
+ OUString const sFile(OUString::Concat(sTarget) + ".xml");
+
+ return xFolder->openStreamElement(sFile, nMode);
+}
+
+void PresetHandler::commitUserChanges()
+{
+ css::uno::Reference< css::embed::XStorage > xWorking;
+ EConfigType eCfgType;
+ {
+ SolarMutexGuard g;
+ xWorking = m_xWorkingStorageUser;
+ eCfgType = m_eConfigType;
+ }
+
+ // e.g. module without any config data ?!
+ if (!xWorking.is())
+ return;
+
+ OUString sPath;
+
+ switch(eCfgType)
+ {
+ case E_GLOBAL :
+ case E_MODULES :
+ {
+ auto & sharedStorages = SharedStorages::get();
+ sPath = sharedStorages.m_lStoragesUser.getPathOfStorage(xWorking);
+ sharedStorages.m_lStoragesUser.commitPath(sPath);
+ sharedStorages.m_lStoragesUser.notifyPath(sPath);
+ }
+ break;
+
+ case E_DOCUMENT :
+ {
+ sPath = m_lDocumentStorages.getPathOfStorage(xWorking);
+ m_lDocumentStorages.commitPath(sPath);
+ m_lDocumentStorages.notifyPath(sPath);
+ }
+ break;
+ }
+}
+
+void PresetHandler::addStorageListener(XMLBasedAcceleratorConfiguration* pListener)
+{
+ OUString sRelPath;
+ EConfigType eCfgType;
+ {
+ SolarMutexGuard g;
+ sRelPath = m_sRelPathUser; // use user path ... because we don't work directly on the share layer!
+ eCfgType = m_eConfigType;
+ }
+
+ if (sRelPath.isEmpty())
+ return;
+
+ switch(eCfgType)
+ {
+ case E_GLOBAL :
+ case E_MODULES :
+ {
+ SharedStorages::get().m_lStoragesUser.addStorageListener(pListener, sRelPath);
+ }
+ break;
+
+ case E_DOCUMENT :
+ {
+ m_lDocumentStorages.addStorageListener(pListener, sRelPath);
+ }
+ break;
+ }
+}
+
+void PresetHandler::removeStorageListener(XMLBasedAcceleratorConfiguration* pListener)
+{
+ OUString sRelPath;
+ EConfigType eCfgType;
+ {
+ SolarMutexGuard g;
+ sRelPath = m_sRelPathUser; // use user path ... because we don't work directly on the share layer!
+ eCfgType = m_eConfigType;
+ }
+
+ if (sRelPath.isEmpty())
+ return;
+
+ switch(eCfgType)
+ {
+ case E_GLOBAL :
+ case E_MODULES :
+ {
+ SharedStorages::get().m_lStoragesUser.removeStorageListener(pListener, sRelPath);
+ }
+ break;
+
+ case E_DOCUMENT :
+ {
+ m_lDocumentStorages.removeStorageListener(pListener, sRelPath);
+ }
+ break;
+ }
+}
+
+css::uno::Reference< css::embed::XStorage > PresetHandler::impl_openPathIgnoringErrors(const OUString& sPath ,
+ sal_Int32 eMode ,
+ bool bShare)
+{
+ css::uno::Reference< css::embed::XStorage > xPath;
+ try
+ {
+ if (bShare)
+ xPath = SharedStorages::get().m_lStoragesShare.openPath(sPath, eMode);
+ else
+ xPath = SharedStorages::get().m_lStoragesUser.openPath(sPath, eMode);
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { xPath.clear(); }
+ return xPath;
+}
+
+::std::vector< OUString >::const_iterator PresetHandler::impl_findMatchingLocalizedValue(
+ const ::std::vector< OUString >& lLocalizedValues,
+ OUString& rLanguageTag,
+ bool bAllowFallbacks )
+{
+ ::std::vector< OUString >::const_iterator pFound = lLocalizedValues.end();
+ if (bAllowFallbacks)
+ {
+ pFound = LanguageTag::getFallback(lLocalizedValues, rLanguageTag);
+ // if we found a valid locale ... take it over to our in/out parameter
+ // rLanguageTag
+ if (pFound != lLocalizedValues.end())
+ {
+ rLanguageTag = *pFound;
+ }
+ }
+ else
+ {
+ pFound = std::find(lLocalizedValues.begin(), lLocalizedValues.end(), rLanguageTag);
+ }
+
+ return pFound;
+}
+
+css::uno::Reference< css::embed::XStorage > PresetHandler::impl_openLocalizedPathIgnoringErrors(
+ OUString& sPath ,
+ sal_Int32 eMode ,
+ bool bShare ,
+ OUString& rLanguageTag ,
+ bool bAllowFallback)
+{
+ css::uno::Reference< css::embed::XStorage > xPath = impl_openPathIgnoringErrors(sPath, eMode, bShare);
+ ::std::vector< OUString > lSubFolders = impl_getSubFolderNames(xPath);
+ ::std::vector< OUString >::const_iterator pLocaleFolder = impl_findMatchingLocalizedValue(lSubFolders, rLanguageTag, bAllowFallback);
+
+ // no fallback ... creation not allowed => no storage
+ if (
+ (pLocaleFolder == lSubFolders.end() ) &&
+ ((eMode & css::embed::ElementModes::NOCREATE) == css::embed::ElementModes::NOCREATE)
+ )
+ return css::uno::Reference< css::embed::XStorage >();
+
+ // it doesn't matter, if there is a locale fallback or not
+ // If creation of storages is allowed, we do it anyway.
+ // Otherwise we have no acc config at all, which can make other trouble.
+ OUString sLocalizedPath = sPath + "/";
+ if (pLocaleFolder != lSubFolders.end())
+ sLocalizedPath += *pLocaleFolder;
+ else
+ sLocalizedPath += rLanguageTag;
+
+ css::uno::Reference< css::embed::XStorage > xLocalePath = impl_openPathIgnoringErrors(sLocalizedPath, eMode, bShare);
+
+ if (xLocalePath.is())
+ sPath = sLocalizedPath;
+ else
+ sPath.clear();
+
+ return xLocalePath;
+}
+
+::std::vector< OUString > PresetHandler::impl_getSubFolderNames(const css::uno::Reference< css::embed::XStorage >& xFolder)
+{
+ if (!xFolder.is())
+ return ::std::vector< OUString >();
+
+ ::std::vector< OUString > lSubFolders;
+ const css::uno::Sequence< OUString > lNames = xFolder->getElementNames();
+ const OUString* pNames = lNames.getConstArray();
+ sal_Int32 c = lNames.getLength();
+ sal_Int32 i = 0;
+
+ for (i=0; i<c; ++i)
+ {
+ try
+ {
+ if (xFolder->isStorageElement(pNames[i]))
+ lSubFolders.push_back(pNames[i]);
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ {}
+ }
+
+ return lSubFolders;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/accelerators/storageholder.cxx b/framework/source/accelerators/storageholder.cxx
new file mode 100644
index 000000000..c0a6106d7
--- /dev/null
+++ b/framework/source/accelerators/storageholder.cxx
@@ -0,0 +1,447 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <accelerators/storageholder.hxx>
+#include <accelerators/acceleratorconfiguration.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/embed/ElementModes.hpp>
+
+#include <com/sun/star/embed/XTransactedObject.hpp>
+
+#include <rtl/ustrbuf.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <algorithm>
+
+constexpr OUStringLiteral PATH_SEPARATOR = u"/";
+#define PATH_SEPARATOR_UNICODE u'/'
+
+namespace framework
+{
+
+StorageHolder::StorageHolder()
+{
+}
+
+StorageHolder::~StorageHolder()
+{
+ // TODO implement me
+ // dispose/clear etcpp.
+}
+
+void StorageHolder::forgetCachedStorages()
+{
+ std::unique_lock g(m_mutex);
+ for (auto & lStorage : m_lStorages)
+ {
+ TStorageInfo& rInfo = lStorage.second;
+ // TODO think about listener !
+ rInfo.Storage.clear();
+ }
+ m_lStorages.clear();
+}
+
+void StorageHolder::setRootStorage(const css::uno::Reference< css::embed::XStorage >& xRoot)
+{
+ std::unique_lock g(m_mutex);
+ m_xRoot = xRoot;
+}
+
+css::uno::Reference< css::embed::XStorage > StorageHolder::getRootStorage() const
+{
+ std::unique_lock g(m_mutex);
+ return m_xRoot;
+}
+
+css::uno::Reference< css::embed::XStorage > StorageHolder::openPath(const OUString& sPath ,
+ sal_Int32 nOpenMode)
+{
+ OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
+ std::vector<OUString> lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
+
+ // SAFE -> ----------------------------------
+ std::unique_lock aReadLock(m_mutex);
+ css::uno::Reference< css::embed::XStorage > xParent = m_xRoot;
+ aReadLock.unlock();
+ // <- SAFE ----------------------------------
+
+ css::uno::Reference< css::embed::XStorage > xChild;
+ OUString sRelPath;
+
+ for (auto const& lFolder : lFolders)
+ {
+ OUString sCheckPath (sRelPath + lFolder + PATH_SEPARATOR);
+
+ // SAFE -> ------------------------------
+ aReadLock.lock();
+
+ // If we found an already open storage ... we must increase
+ // its use count. Otherwise it will may be closed too early :-)
+ TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath);
+ TStorageInfo* pInfo = nullptr;
+ if (pCheck != m_lStorages.end())
+ {
+ pInfo = &(pCheck->second);
+ ++(pInfo->UseCount);
+ xChild = pInfo->Storage;
+
+ aReadLock.unlock();
+ // <- SAFE ------------------------------
+ }
+ else
+ {
+ aReadLock.unlock();
+ // <- SAFE ------------------------------
+
+ try
+ {
+ xChild = StorageHolder::openSubStorageWithFallback(xParent, lFolder, nOpenMode); // TODO think about delegating fallback decision to our own caller!
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ {
+ /* TODO URGENT!
+ in case we found some "already existing storages" on the path before and increased its UseCount ...
+ and now we will get an exception on creating a new sub storage ...
+ we must decrease all UseCounts, which was touched before. Otherwise these storages can't be closed!
+
+ Idea: Using of another structure member "PossibleUseCount" as vector of unique numbers.
+ Every thread use another unique number to identify all "owned candidates".
+ A flush method with the same unique number force increasing of the "UseCount" variable then
+ inside a synchronized block ...
+ */
+ throw;
+ }
+
+ std::unique_lock g(m_mutex);
+ pInfo = &(m_lStorages[sCheckPath]);
+ pInfo->Storage = xChild;
+ pInfo->UseCount = 1;
+ }
+
+ xParent = xChild;
+ sRelPath += lFolder + PATH_SEPARATOR;
+ }
+
+ // TODO think about return last storage as working storage ... but don't caching it inside this holder!
+ // => otherwise the same storage is may be commit more than once.
+
+ return xChild;
+}
+
+StorageHolder::TStorageList StorageHolder::getAllPathStorages(const OUString& sPath)
+{
+ OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
+ std::vector<OUString> lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
+
+ StorageHolder::TStorageList lStoragesOfPath;
+ OUString sRelPath;
+
+ std::unique_lock g(m_mutex);
+
+ for (auto const& lFolder : lFolders)
+ {
+ OUString sCheckPath (sRelPath + lFolder + PATH_SEPARATOR);
+
+ TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath);
+ if (pCheck == m_lStorages.end())
+ {
+ // at least one path element was not found
+ // Seems that this path isn't open ...
+ lStoragesOfPath.clear();
+ return lStoragesOfPath;
+ }
+
+ TStorageInfo& rInfo = pCheck->second;
+ lStoragesOfPath.push_back(rInfo.Storage);
+
+ sRelPath += lFolder + PATH_SEPARATOR;
+ }
+
+ return lStoragesOfPath;
+}
+
+void StorageHolder::commitPath(const OUString& sPath)
+{
+ StorageHolder::TStorageList lStorages = getAllPathStorages(sPath);
+
+ css::uno::Reference< css::embed::XTransactedObject > xCommit;
+ StorageHolder::TStorageList::reverse_iterator pIt;
+ for ( pIt = lStorages.rbegin(); // order of commit is important ... otherwise changes are not recognized!
+ pIt != lStorages.rend();
+ ++pIt )
+ {
+ xCommit.set(*pIt, css::uno::UNO_QUERY);
+ if (!xCommit.is())
+ continue;
+ xCommit->commit();
+ }
+
+ // SAFE -> ------------------------------
+ {
+ std::unique_lock aReadLock(m_mutex);
+ xCommit.set(m_xRoot, css::uno::UNO_QUERY);
+ }
+ // <- SAFE ------------------------------
+
+ if (xCommit.is())
+ xCommit->commit();
+}
+
+void StorageHolder::closePath(const OUString& rPath)
+{
+ OUString sNormedPath = StorageHolder::impl_st_normPath(rPath);
+ std::vector<OUString> lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
+
+ /* convert list of paths in the following way:
+ [0] = "path_1" => "path_1
+ [1] = "path_2" => "path_1/path_2"
+ [2] = "path_3" => "path_1/path_2/path_3"
+ */
+ OUString sParentPath;
+ for (auto & lFolder : lFolders)
+ {
+ OUString sCurrentRelPath(sParentPath + lFolder + PATH_SEPARATOR);
+ lFolder = sCurrentRelPath;
+ sParentPath = sCurrentRelPath;
+ }
+
+ std::unique_lock g(m_mutex);
+
+ std::vector<OUString>::reverse_iterator pIt2;
+ for ( pIt2 = lFolders.rbegin();
+ pIt2 != lFolders.rend();
+ ++pIt2 )
+ {
+ OUString sPath = *pIt2;
+ TPath2StorageInfo::iterator pPath = m_lStorages.find(sPath);
+ if (pPath == m_lStorages.end())
+ continue; // ???
+
+ TStorageInfo& rInfo = pPath->second;
+ --rInfo.UseCount;
+ if (rInfo.UseCount < 1)
+ {
+ rInfo.Storage.clear();
+ m_lStorages.erase(pPath);
+ }
+ }
+}
+
+void StorageHolder::notifyPath(const OUString& sPath)
+{
+ OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
+
+ std::unique_lock g(m_mutex);
+
+ TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
+ if (pIt1 == m_lStorages.end())
+ return;
+
+ TStorageInfo& rInfo = pIt1->second;
+ for (auto const& listener : rInfo.Listener)
+ {
+ if (listener)
+ listener->changesOccurred();
+ }
+}
+
+void StorageHolder::addStorageListener( XMLBasedAcceleratorConfiguration* pListener,
+ const OUString& sPath )
+{
+ OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
+
+ std::unique_lock g(m_mutex);
+
+ TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
+ if (pIt1 == m_lStorages.end())
+ return;
+
+ TStorageInfo& rInfo = pIt1->second;
+ TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener);
+ if (pIt2 == rInfo.Listener.end())
+ rInfo.Listener.push_back(pListener);
+}
+
+void StorageHolder::removeStorageListener( XMLBasedAcceleratorConfiguration* pListener,
+ const OUString& sPath )
+{
+ OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
+
+ std::unique_lock g(m_mutex);
+
+ TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
+ if (pIt1 == m_lStorages.end())
+ return;
+
+ TStorageInfo& rInfo = pIt1->second;
+ TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener);
+ if (pIt2 != rInfo.Listener.end())
+ rInfo.Listener.erase(pIt2);
+}
+
+OUString StorageHolder::getPathOfStorage(const css::uno::Reference< css::embed::XStorage >& xStorage)
+{
+ std::unique_lock g(m_mutex);
+
+ for (auto const& lStorage : m_lStorages)
+ {
+ const TStorageInfo& rInfo = lStorage.second;
+ if (rInfo.Storage == xStorage)
+ return lStorage.first;
+ }
+
+ return OUString();
+}
+
+css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const css::uno::Reference< css::embed::XStorage >& xChild)
+{
+ OUString sChildPath = getPathOfStorage(xChild);
+ return getParentStorage(sChildPath);
+}
+
+css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const OUString& sChildPath)
+{
+ // normed path = "a/b/c/" ... we search for "a/b/"
+ OUString sNormedPath = StorageHolder::impl_st_normPath(sChildPath);
+ std::vector<OUString> lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
+ sal_Int32 c = lFolders.size();
+
+ // a) "" => - => no parent
+ // b) "a/b/c/" => "a/b/" => return storage "a/b/"
+ // c) "a/" => "" => return root !
+
+ // a)
+ if (c < 1)
+ return css::uno::Reference< css::embed::XStorage >();
+
+ // SAFE -> ----------------------------------
+ {
+ std::unique_lock aReadLock(m_mutex);
+
+ // b)
+ if (c < 2)
+ return m_xRoot;
+
+ // c)
+ OUStringBuffer sParentPath(64);
+ sal_Int32 i = 0;
+ for (i = 0; i < c - 1; ++i)
+ {
+ sParentPath.append(lFolders[i] + PATH_SEPARATOR);
+ }
+
+ auto pParent = m_lStorages.find(sParentPath.makeStringAndClear());
+ if (pParent != m_lStorages.end())
+ return pParent->second.Storage;
+ }
+ // <- SAFE ----------------------------------
+
+ // ?
+ SAL_INFO("fwk", "StorageHolder::getParentStorage(): Unexpected situation. Cached storage item seems to be wrong.");
+ return css::uno::Reference< css::embed::XStorage >();
+}
+
+StorageHolder& StorageHolder::operator=(const StorageHolder& rCopy)
+{
+ std::unique_lock g(m_mutex);
+ m_xRoot = rCopy.m_xRoot;
+ m_lStorages = rCopy.m_lStorages;
+ return *this;
+}
+
+css::uno::Reference< css::embed::XStorage > StorageHolder::openSubStorageWithFallback(const css::uno::Reference< css::embed::XStorage >& xBaseStorage ,
+ const OUString& sSubStorage ,
+ sal_Int32 eOpenMode)
+{
+ // a) try it first with user specified open mode
+ // ignore errors ... but save it for later use!
+ try
+ {
+ css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eOpenMode);
+ if (xSubStorage.is())
+ return xSubStorage;
+ }
+ catch(const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch(const css::uno::Exception&)
+ {
+ // b) readonly already tried? => forward last error!
+ if ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE) // fallback possible ?
+ throw;
+ }
+
+ // b) readonly already tried, throw error
+ if ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE) // fallback possible ?
+ throw css::uno::Exception();
+
+ // c) try it readonly
+ // don't catch exception here! Outside code wish to know, if operation failed or not.
+ // Otherwise they work on NULL references ...
+ sal_Int32 eNewMode = (eOpenMode & ~css::embed::ElementModes::WRITE);
+ css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eNewMode);
+ if (xSubStorage.is())
+ return xSubStorage;
+
+ // d) no chance!
+ SAL_INFO("fwk", "openSubStorageWithFallback(): Unexpected situation! Got no exception for missing storage ...");
+ return css::uno::Reference< css::embed::XStorage >();
+}
+
+OUString StorageHolder::impl_st_normPath(const OUString& sPath)
+{
+ // path must start without "/" but end with "/"!
+
+ OUString sNormedPath = sPath;
+
+ // "/bla" => "bla" && "/" => "" (!)
+ (void)sNormedPath.startsWith(PATH_SEPARATOR, &sNormedPath);
+
+ // "/" => "" || "" => "" ?
+ if (sNormedPath.isEmpty())
+ return OUString();
+
+ // "bla" => "bla/"
+ if (sNormedPath.lastIndexOf(PATH_SEPARATOR_UNICODE) != (sNormedPath.getLength()-1))
+ sNormedPath += PATH_SEPARATOR;
+
+ return sNormedPath;
+}
+
+std::vector<OUString> StorageHolder::impl_st_parsePath(std::u16string_view sPath)
+{
+ std::vector<OUString> lToken;
+ sal_Int32 i = 0;
+ while (true)
+ {
+ OUString sToken( o3tl::getToken(sPath, 0, PATH_SEPARATOR_UNICODE, i) );
+ if (i < 0)
+ break;
+ lToken.push_back(sToken);
+ }
+ return lToken;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/classes/framecontainer.cxx b/framework/source/classes/framecontainer.cxx
new file mode 100644
index 000000000..a23e3633e
--- /dev/null
+++ b/framework/source/classes/framecontainer.cxx
@@ -0,0 +1,293 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/framecontainer.hxx>
+
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+
+#include <vcl/svapp.hxx>
+#include <comphelper/sequence.hxx>
+#include <sal/log.hxx>
+
+namespace framework
+{
+/**-***************************************************************************************************************
+ @short initialize an empty container
+ @descr The container will be empty then - special features (e.g. the async quit mechanism) are disabled.
+
+ @threadsafe not necessary - it's not a singleton
+ *****************************************************************************************************************/
+FrameContainer::FrameContainer()
+/*DEPRECATEME
+ , m_bAsyncQuit ( sal_False ) // default must be "disabled"!
+ , m_aAsyncCall ( LINK( this, FrameContainer, implts_asyncQuit ) )
+*/
+{
+}
+
+/**-***************************************************************************************************************
+ @short deinitialize may a filled container
+ @descr Special features (if the currently are running) will be disabled and we free all used other resources.
+
+ @threadsafe not necessary - it's not a singleton
+ *****************************************************************************************************************/
+FrameContainer::~FrameContainer()
+{
+ // Don't forget to free memory!
+ m_aContainer.clear();
+ m_xActiveFrame.clear();
+}
+
+/**-***************************************************************************************************************
+ @short append a new frame to the container
+ @descr We accept the incoming frame only, if it is a valid reference and doesn't exist already.
+
+ @param xFrame
+ frame, which should be added to this container
+ Must be a valid reference.
+
+ @threadsafe yes
+ *****************************************************************************************************************/
+void FrameContainer::append(const css::uno::Reference<css::frame::XFrame>& xFrame)
+{
+ if (xFrame.is() && !exist(xFrame))
+ {
+ SolarMutexGuard g;
+ m_aContainer.push_back(xFrame);
+ }
+}
+
+/**-***************************************************************************************************************
+ @short remove a frame from the container
+ @descr In case we remove the last frame and our internal special feature (the async quit mechanism)
+ was enabled by the desktop instance, we start it.
+
+ @param xFrame
+ frame, which should be deleted from this container
+ Must be a valid reference.
+
+ @threadsafe yes
+ *****************************************************************************************************************/
+void FrameContainer::remove(const css::uno::Reference<css::frame::XFrame>& xFrame)
+{
+ SolarMutexGuard g;
+
+ TFrameContainer::iterator aSearchedItem
+ = ::std::find(m_aContainer.begin(), m_aContainer.end(), xFrame);
+ if (aSearchedItem != m_aContainer.end())
+ {
+ m_aContainer.erase(aSearchedItem);
+
+ // If removed frame was the current active frame - reset state variable.
+ if (m_xActiveFrame == xFrame)
+ m_xActiveFrame.clear();
+ }
+}
+
+/**-***************************************************************************************************************
+ @short check if the given frame currently exist inside the container
+ @param xFrame
+ reference to the queried frame
+
+ @return <TRUE/> if frame is part of this container
+ <FALSE/> otherwise
+
+ @threadsafe yes
+ *****************************************************************************************************************/
+bool FrameContainer::exist(const css::uno::Reference<css::frame::XFrame>& xFrame) const
+{
+ SolarMutexGuard g;
+ return (::std::find(m_aContainer.begin(), m_aContainer.end(), xFrame) != m_aContainer.end());
+}
+
+/**-***************************************************************************************************************
+ @short delete all existing items of the container
+ @threadsafe yes
+ *****************************************************************************************************************/
+void FrameContainer::clear()
+{
+ SolarMutexGuard g;
+ // Clear the container ...
+ m_aContainer.clear();
+ // ... and don't forget to reset the active frame.
+ // It's a reference to a valid container-item.
+ // But no container item => no active frame!
+ m_xActiveFrame.clear();
+}
+
+/**-***************************************************************************************************************
+ @short returns count of all current existing frames
+ @deprecated This value can't be guaranteed for multithreading environments.
+ So it will be marked as deprecated and should be replaced by "getAllElements()".
+
+ @return the count of existing container items
+
+ @threadsafe yes
+ *****************************************************************************************************************/
+sal_uInt32 FrameContainer::getCount() const
+{
+ SolarMutexGuard g;
+ return static_cast<sal_uInt32>(m_aContainer.size());
+}
+
+/**-***************************************************************************************************************
+ @short returns one item of this container
+ @deprecated This value can't be guaranteed for multithreading environments.
+ So it will be marked as deprecated and should be replaced by "getAllElements()".
+
+ @param nIndex
+ a value between 0 and (getCount()-1) to address one container item
+
+ @return a reference to a frame inside the container, which match with given index
+
+ @threadsafe yes
+ *****************************************************************************************************************/
+css::uno::Reference<css::frame::XFrame> FrameContainer::operator[](sal_uInt32 nIndex) const
+{
+ css::uno::Reference<css::frame::XFrame> xFrame;
+ try
+ {
+ // Get element form container WITH automatic test of ranges!
+ // If index not valid, an out_of_range exception is thrown.
+ SolarMutexGuard g;
+ xFrame = m_aContainer.at(nIndex);
+ }
+ catch (const std::out_of_range&)
+ {
+ // The index is not valid for current container-content - we must handle this case!
+ // We can return the default value ...
+ SAL_INFO("fwk", "FrameContainer::operator[]: Exception caught: std::out_of_range");
+ }
+ return xFrame;
+}
+
+/**-***************************************************************************************************************
+ @short returns a snapshot of all currently existing frames inside this container
+ @descr Should be used to replace the deprecated functions getCount()/operator[]!
+
+ @return a list of all frame references inside this container
+
+ @threadsafe yes
+ *****************************************************************************************************************/
+css::uno::Sequence<css::uno::Reference<css::frame::XFrame>> FrameContainer::getAllElements() const
+{
+ SolarMutexGuard g;
+ return comphelper::containerToSequence(m_aContainer);
+}
+
+/**-***************************************************************************************************************
+ @short set the given frame as the new active one inside this container
+ @descr We accept this frame only, if it's already a part of this container.
+
+ @param xFrame
+ reference to the new active frame
+ Must be a valid reference and already part of this container.
+
+ @threadsafe yes
+ *****************************************************************************************************************/
+void FrameContainer::setActive(const css::uno::Reference<css::frame::XFrame>& xFrame)
+{
+ if (!xFrame.is() || exist(xFrame))
+ {
+ SolarMutexGuard g;
+ m_xActiveFrame = xFrame;
+ }
+}
+
+/**-***************************************************************************************************************
+ @short return the current active frame of this container
+ @descr Value can be null in case the frame was removed from the container and nobody
+ from outside decide which of all others should be the new one...
+
+ @return a reference to the current active frame
+ Value can be NULL!
+
+ @threadsafe yes
+ *****************************************************************************************************************/
+css::uno::Reference<css::frame::XFrame> FrameContainer::getActive() const
+{
+ SolarMutexGuard g;
+ return m_xActiveFrame;
+}
+
+/**-***************************************************************************************************************
+ @short implements a simple search based on current container items
+ @descr It can be used for findFrame() and implements a deep down search.
+
+ @param sName
+ target name, which is searched
+
+ @return reference to the found frame or NULL if not.
+
+ @threadsafe yes
+ *****************************************************************************************************************/
+css::uno::Reference<css::frame::XFrame>
+FrameContainer::searchOnAllChildrens(const OUString& sName) const
+{
+ SolarMutexGuard g;
+ // Step over all child frames. But if direct child isn't the right one search on his children first - before
+ // you go to next direct child of this container!
+ css::uno::Reference<css::frame::XFrame> xSearchedFrame;
+ for (auto const& container : m_aContainer)
+ {
+ if (container->getName() == sName)
+ {
+ xSearchedFrame = container;
+ break;
+ }
+ else
+ {
+ xSearchedFrame = container->findFrame(sName, css::frame::FrameSearchFlag::CHILDREN);
+ if (xSearchedFrame.is())
+ break;
+ }
+ }
+ return xSearchedFrame;
+}
+
+/**-***************************************************************************************************************
+ @short implements a simple search based on current container items
+ @descr It can be used for findFrame() and search on members of this container only!
+
+ @param sName
+ target name, which is searched
+
+ @return reference to the found frame or NULL if not.
+
+ @threadsafe yes
+ *****************************************************************************************************************/
+css::uno::Reference<css::frame::XFrame>
+FrameContainer::searchOnDirectChildrens(std::u16string_view sName) const
+{
+ SolarMutexGuard g;
+ css::uno::Reference<css::frame::XFrame> xSearchedFrame;
+ for (auto const& container : m_aContainer)
+ {
+ if (container->getName() == sName)
+ {
+ xSearchedFrame = container;
+ break;
+ }
+ }
+ return xSearchedFrame;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/classes/taskcreator.cxx b/framework/source/classes/taskcreator.cxx
new file mode 100644
index 000000000..151322e1e
--- /dev/null
+++ b/framework/source/classes/taskcreator.cxx
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <classes/taskcreator.hxx>
+#include <services.h>
+#include <taskcreatordefs.hxx>
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/TaskCreator.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <utility>
+
+namespace framework{
+
+/*-****************************************************************************************************
+ @short initialize instance with necessary information
+ @descr We need a valid uno service manager to create or instantiate new services.
+ All other information to create frames or tasks come in on right interface methods.
+
+ @param xContext
+ points to the valid uno service manager
+*//*-*****************************************************************************************************/
+TaskCreator::TaskCreator( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : m_xContext (std::move( xContext ))
+{
+}
+
+/*-****************************************************************************************************
+ @short deinitialize instance
+ @descr We should release all used resource which are not needed any longer.
+*//*-*****************************************************************************************************/
+TaskCreator::~TaskCreator()
+{
+}
+
+/*-****************************************************************************************************
+ TODO document me
+*//*-*****************************************************************************************************/
+css::uno::Reference< css::frame::XFrame > TaskCreator::createTask( const OUString& sName, const utl::MediaDescriptor& rDescriptor )
+{
+ css::uno::Reference< css::lang::XSingleServiceFactory > xCreator;
+
+ try
+ {
+ xCreator.set( m_xContext->getServiceManager()->createInstanceWithContext(IMPLEMENTATIONNAME_FWK_TASKCREATOR, m_xContext), css::uno::UNO_QUERY_THROW);
+ }
+ catch(const css::uno::Exception&)
+ {}
+
+ // no catch here ... without a task creator service we can't open ANY document window within the office.
+ // That's IMHO not a good idea. Then we should accept the stacktrace showing us the real problem.
+ // BTW: The used fallback creator service (IMPLEMENTATIONNAME_FWK_TASKCREATOR) is implemented in the same
+ // library then these class here ... Why we should not be able to create it ?
+ if ( ! xCreator.is())
+ xCreator = css::frame::TaskCreator::create(m_xContext);
+
+ css::uno::Sequence< css::uno::Any > lArgs(6);
+ auto plArgs = lArgs.getArray();
+ css::beans::NamedValue aArg;
+
+ aArg.Name = ARGUMENT_PARENTFRAME;
+ aArg.Value <<= css::uno::Reference< css::frame::XFrame >( css::frame::Desktop::create( m_xContext ), css::uno::UNO_QUERY_THROW);
+ plArgs[0] <<= aArg;
+
+ aArg.Name = ARGUMENT_CREATETOPWINDOW;
+ aArg.Value <<= true;
+ plArgs[1] <<= aArg;
+
+ aArg.Name = ARGUMENT_MAKEVISIBLE;
+ aArg.Value <<= false;
+ plArgs[2] <<= aArg;
+
+ aArg.Name = ARGUMENT_SUPPORTPERSISTENTWINDOWSTATE;
+ aArg.Value <<= true;
+ plArgs[3] <<= aArg;
+
+ aArg.Name = ARGUMENT_FRAMENAME;
+ aArg.Value <<= sName;
+ plArgs[4] <<= aArg;
+
+ bool bHidden
+ = rDescriptor.getUnpackedValueOrDefault("HiddenForConversion", false);
+ aArg.Name = "HiddenForConversion";
+ aArg.Value <<= bHidden;
+ plArgs[5] <<= aArg;
+
+ css::uno::Reference< css::frame::XFrame > xTask(xCreator->createInstanceWithArguments(lArgs), css::uno::UNO_QUERY_THROW);
+ return xTask;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/closedispatcher.cxx b/framework/source/dispatch/closedispatcher.cxx
new file mode 100644
index 000000000..a6ff39d45
--- /dev/null
+++ b/framework/source/dispatch/closedispatcher.cxx
@@ -0,0 +1,612 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/closedispatcher.hxx>
+#include <pattern/frame.hxx>
+#include <framework/framelistanalyzer.hxx>
+#include <services.h>
+
+#include <com/sun/star/bridge/BridgeFactory.hpp>
+#include <com/sun/star/bridge/XBridgeFactory2.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/CommandGroup.hpp>
+#include <com/sun/star/frame/StartModule.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/document/XActionLockable.hpp>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <osl/diagnose.h>
+#include <utility>
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/syswin.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace com::sun::star;
+
+namespace framework{
+
+#ifdef fpf
+ #error "Who uses \"fpf\" as define. It will overwrite my namespace alias ..."
+#endif
+namespace fpf = ::framework::pattern::frame;
+
+constexpr OUStringLiteral URL_CLOSEDOC = u".uno:CloseDoc";
+constexpr OUStringLiteral URL_CLOSEWIN = u".uno:CloseWin";
+const char URL_CLOSEFRAME[] = ".uno:CloseFrame";
+
+CloseDispatcher::CloseDispatcher(css::uno::Reference< css::uno::XComponentContext > xContext ,
+ const css::uno::Reference< css::frame::XFrame >& xFrame ,
+ std::u16string_view sTarget)
+ : m_xContext(std::move(xContext))
+ , m_aAsyncCallback(
+ new vcl::EventPoster(LINK(this, CloseDispatcher, impl_asyncCallback)))
+ , m_eOperation(E_CLOSE_DOC)
+ , m_pSysWindow(nullptr)
+{
+ uno::Reference<frame::XFrame> xTarget = static_impl_searchRightTargetFrame(xFrame, sTarget);
+ m_xCloseFrame = xTarget;
+
+ // Try to retrieve the system window instance of the closing frame.
+ uno::Reference<awt::XWindow> xWindow = xTarget->getContainerWindow();
+ if (xWindow.is())
+ {
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ if (pWindow->IsSystemWindow())
+ m_pSysWindow = dynamic_cast<SystemWindow*>(pWindow.get());
+ }
+}
+
+CloseDispatcher::~CloseDispatcher()
+{
+ SolarMutexGuard g;
+ m_aAsyncCallback.reset();
+ m_pSysWindow.reset();
+}
+
+void SAL_CALL CloseDispatcher::dispatch(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
+{
+ dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >());
+}
+
+css::uno::Sequence< sal_Int16 > SAL_CALL CloseDispatcher::getSupportedCommandGroups()
+{
+ return css::uno::Sequence< sal_Int16 >{css::frame::CommandGroup::VIEW, css::frame::CommandGroup::DOCUMENT};
+}
+
+css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL CloseDispatcher::getConfigurableDispatchInformation(sal_Int16 nCommandGroup)
+{
+ if (nCommandGroup == css::frame::CommandGroup::VIEW)
+ {
+ /* Attention: Don't add .uno:CloseFrame here. Because it's not really
+ a configurable feature ... and further it does not have
+ a valid UIName entry inside the GenericCommands.xcu ... */
+ css::uno::Sequence< css::frame::DispatchInformation > lViewInfos{
+ { URL_CLOSEWIN, css::frame::CommandGroup::VIEW }
+ };
+ return lViewInfos;
+ }
+ else if (nCommandGroup == css::frame::CommandGroup::DOCUMENT)
+ {
+ css::uno::Sequence< css::frame::DispatchInformation > lDocInfos{
+ { URL_CLOSEDOC, css::frame::CommandGroup::DOCUMENT }
+ };
+ return lDocInfos;
+ }
+
+ return css::uno::Sequence< css::frame::DispatchInformation >();
+}
+
+void SAL_CALL CloseDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
+ const css::util::URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL CloseDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
+ const css::util::URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL CloseDispatcher::dispatchWithNotification(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ // SAFE -> ----------------------------------
+ SolarMutexClearableGuard aWriteLock;
+
+ // This reference indicates, that we were already called before and
+ // our asynchronous process was not finished yet.
+ // We have to reject double calls. Otherwise we risk,
+ // that we try to close an already closed resource...
+ // And it is no problem to do nothing then. The UI user will try it again, if
+ // non of these jobs was successful.
+ if (m_xSelfHold.is())
+ {
+ aWriteLock.clear();
+ // <- SAFE ------------------------------
+
+ implts_notifyResultListener(
+ xListener,
+ css::frame::DispatchResultState::DONTKNOW,
+ css::uno::Any());
+ return;
+ }
+
+ // First we have to check, if this dispatcher is used right. Means if valid URLs are used.
+ // If not - we have to break this operation. But an optional listener must be informed.
+ // BTW: We save the information about the requested operation. Because
+ // we need it later.
+ if ( aURL.Complete == URL_CLOSEDOC )
+ m_eOperation = E_CLOSE_DOC;
+ else if ( aURL.Complete == URL_CLOSEWIN )
+ m_eOperation = E_CLOSE_WIN;
+ else if ( aURL.Complete == URL_CLOSEFRAME )
+ m_eOperation = E_CLOSE_FRAME;
+ else
+ {
+ aWriteLock.clear();
+ // <- SAFE ------------------------------
+
+ implts_notifyResultListener(
+ xListener,
+ css::frame::DispatchResultState::FAILURE,
+ css::uno::Any());
+ return;
+ }
+
+ if (m_pSysWindow && m_pSysWindow->GetCloseHdl().IsSet())
+ {
+ // The closing frame has its own close handler. Call it instead.
+ m_pSysWindow->GetCloseHdl().Call(*m_pSysWindow);
+
+ aWriteLock.clear();
+ // <- SAFE ------------------------------
+
+ implts_notifyResultListener(
+ xListener,
+ css::frame::DispatchResultState::SUCCESS,
+ css::uno::Any());
+
+ return;
+ }
+
+ // OK - URLs are the right ones.
+ // But we can't execute synchronously :-)
+ // May we are called from a generic key-input handler,
+ // which isn't aware that this call kill its own environment...
+ // Do it asynchronous everytimes!
+
+ // But don't forget to hold ourselves alive.
+ // We are called back from an environment, which doesn't know a uno reference.
+ // They call us back by using our c++ interface.
+
+ m_xResultListener = xListener;
+ m_xSelfHold.set(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
+
+ aWriteLock.clear();
+ // <- SAFE ----------------------------------
+
+ bool bIsSynchron = false;
+ for (const css::beans::PropertyValue& rArg : lArguments )
+ {
+ if ( rArg.Name == "SynchronMode" )
+ {
+ rArg.Value >>= bIsSynchron;
+ break;
+ }
+ }
+
+ if ( bIsSynchron )
+ impl_asyncCallback(nullptr);
+ else
+ {
+ SolarMutexGuard g;
+ m_aAsyncCallback->Post();
+ }
+}
+
+/**
+ @short asynchronous callback
+ @descr We start all actions inside this object asynchronous
+ (see comments there).
+ Now we do the following:
+ - close all views to the same document, if needed and possible
+ - make the current frame empty
+ ! This step is necessary to handle errors during closing the
+ document inside the frame. May the document shows a dialog and
+ the user ignore it. Then the state of the office can be changed
+ during we try to close frame and document.
+ - check the environment (means count open frames - excluding our
+ current one)
+ - decide then, if we must close this frame only, establish the backing mode
+ or shutdown the whole application.
+*/
+IMPL_LINK_NOARG(CloseDispatcher, impl_asyncCallback, LinkParamNone*, void)
+{
+ try
+ {
+
+ // Allow calling of XController->suspend() everytimes.
+ // Dispatch is an UI functionality. We implement such dispatch object here.
+ // And further XController->suspend() was designed to bring an UI ...
+ bool bControllerSuspended = false;
+
+ bool bCloseAllViewsToo;
+ EOperation eOperation;
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ css::uno::Reference< css::frame::XFrame > xCloseFrame;
+ css::uno::Reference< css::frame::XDispatchResultListener > xListener;
+ {
+ SolarMutexGuard g;
+
+ // Closing of all views, related to the same document, is allowed
+ // only if the dispatched URL was ".uno:CloseDoc"!
+ bCloseAllViewsToo = (m_eOperation == E_CLOSE_DOC);
+
+ eOperation = m_eOperation;
+ xContext = m_xContext;
+ xCloseFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY);
+ xListener = m_xResultListener;
+ }
+
+ // frame already dead ?!
+ // Nothing to do !
+ if (! xCloseFrame.is())
+ return;
+
+ bool bCloseFrame = false;
+ bool bEstablishBackingMode = false;
+ bool bTerminateApp = false;
+
+ // Analyze the environment a first time.
+ // If we found some special cases, we can
+ // make some decisions earlier!
+ css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create(xContext), css::uno::UNO_QUERY_THROW);
+ FrameListAnalyzer aCheck1(xDesktop, xCloseFrame, FrameAnalyzerFlags::Help | FrameAnalyzerFlags::BackingComponent);
+
+ // Check for existing UNO connections.
+ // NOTE: There is a race between checking this and connections being created/destroyed before
+ // we close the frame / terminate the app.
+ css::uno::Reference<css::bridge::XBridgeFactory2> bridgeFac( css::bridge::BridgeFactory::create(xContext) );
+ bool bHasActiveConnections = bridgeFac->getExistingBridges().hasElements();
+
+ // a) If the current frame (where the close dispatch was requested for) does not have
+ // any parent frame ... it will close this frame only. Such frame isn't part of the
+ // global desktop tree ... and such frames are used as "implementation details" only.
+ // E.g. the live previews of our wizards doing such things. And then the owner of the frame
+ // is responsible for closing the application or accepting closing of the application
+ // by others.
+ if ( ! xCloseFrame->getCreator().is())
+ bCloseFrame = true;
+
+ // b) The help window can't disagree with any request.
+ // Because it doesn't implement a controller - it uses a window only.
+ // Further it can't be the last open frame - if we do all other things
+ // right inside this CloseDispatcher implementation.
+ // => close it!
+ else if (aCheck1.m_bReferenceIsHelp)
+ bCloseFrame = true;
+
+ // c) If we are already in "backing mode", we terminate the application, if no active UNO connections are found.
+ // If there is an active UNO connection, we only close the frame and leave the application alive.
+ // It doesn't matter, how many other frames (can be the help or hidden frames only) are open then.
+ else if (aCheck1.m_bReferenceIsBacking) {
+ if (bHasActiveConnections)
+ bCloseFrame = true;
+ else
+ bTerminateApp = true;
+ }
+
+ // d) Otherwise we have to: close all views to the same document, close the
+ // document inside our own frame and decide then again, what has to be done!
+ else
+ {
+ if (implts_prepareFrameForClosing(m_xCloseFrame, bCloseAllViewsToo, bControllerSuspended))
+ {
+ // OK; this frame is empty now.
+ // Check the environment again to decide, what is the next step.
+ FrameListAnalyzer aCheck2(xDesktop, xCloseFrame, FrameAnalyzerFlags::All);
+
+ // c1) there is as minimum 1 frame open, which is visible and contains a document
+ // different from our one. And it's not the help!
+ // => close our frame only - nothing else.
+ if (!aCheck2.m_lOtherVisibleFrames.empty())
+ bCloseFrame = true;
+ else
+
+ // c2) if we close the current view ... but not all other views
+ // to the same document, we must close the current frame only!
+ // Because implts_closeView() suspended this view only - does not
+ // close the frame.
+ if (
+ (!bCloseAllViewsToo ) &&
+ (!aCheck2.m_lModelFrames.empty())
+ )
+ bCloseFrame = true;
+
+ else
+ // c3) there is no other (visible) frame open ...
+ // The help module will be ignored everytimes!
+ // But we have to decide if we must terminate the
+ // application or establish the backing mode now.
+ // And that depends from the dispatched URL ...
+ {
+ if (eOperation == E_CLOSE_FRAME)
+ {
+ if (bHasActiveConnections)
+ bCloseFrame = true;
+ else
+ bTerminateApp = true;
+ }
+ else if( SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE) )
+ bEstablishBackingMode = true;
+ else if (bHasActiveConnections)
+ bCloseFrame = true;
+ else
+ bTerminateApp = true;
+ }
+ }
+ }
+
+ // Do it now ...
+ bool bSuccess = false;
+ if (bCloseFrame)
+ bSuccess = implts_closeFrame();
+ else if (bEstablishBackingMode)
+ #if defined MACOSX
+ {
+ // on mac close down, quickstarter keeps the process alive
+ // however if someone has shut down the quickstarter
+ // behave as any other platform
+
+ bool bQuickstarterRunning = false;
+ // get quickstart service
+ try
+ {
+ css::uno::Reference< css::beans::XFastPropertySet > xSet( xContext->getServiceManager()->createInstanceWithContext(IMPLEMENTATIONNAME_QUICKLAUNCHER, xContext), css::uno::UNO_QUERY_THROW );
+ css::uno::Any aVal( xSet->getFastPropertyValue( 0 ) );
+ bool bState = false;
+ if( aVal >>= bState )
+ bQuickstarterRunning = bState;
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+ bSuccess = bQuickstarterRunning ? implts_terminateApplication() : implts_establishBackingMode();
+ }
+ #else
+ bSuccess = implts_establishBackingMode();
+ #endif
+ else if (bTerminateApp)
+ bSuccess = implts_terminateApplication();
+
+ if ( ! bSuccess && bControllerSuspended )
+ {
+ css::uno::Reference< css::frame::XController > xController = xCloseFrame->getController();
+ if (xController.is())
+ xController->suspend(false);
+ }
+
+ // inform listener
+ sal_Int16 nState = css::frame::DispatchResultState::FAILURE;
+ if (bSuccess)
+ nState = css::frame::DispatchResultState::SUCCESS;
+ implts_notifyResultListener(xListener, nState, css::uno::Any());
+
+ SolarMutexGuard g;
+ // This method was called asynchronous from our main thread by using a pointer.
+ // We reached this method only, by using a reference to ourself :-)
+ // Further this member is used to detect still running and not yet finished
+ // asynchronous operations. So it's time now to release this reference.
+ // But hold it temp alive. Otherwise we die before we can finish this method really :-))
+ css::uno::Reference< css::uno::XInterface > xTempHold = m_xSelfHold;
+ m_xSelfHold.clear();
+ m_xResultListener.clear();
+ }
+ catch(const css::lang::DisposedException&)
+ {
+ }
+}
+
+bool CloseDispatcher::implts_prepareFrameForClosing(const css::uno::Reference< css::frame::XFrame >& xFrame,
+ bool bCloseAllOtherViewsToo,
+ bool& bControllerSuspended )
+{
+ // Frame already dead ... so this view is closed ... is closed ... is ... .-)
+ if (! xFrame.is())
+ return true;
+
+ // Close all views to the same document ... if forced to do so.
+ // But don't touch our own frame here!
+ // We must do so ... because the may be following controller->suspend()
+ // will show the "save/discard/cancel" dialog for the last view only!
+ if (bCloseAllOtherViewsToo)
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ {
+ SolarMutexGuard g;
+ xContext = m_xContext;
+ }
+
+ css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create( xContext ), css::uno::UNO_QUERY_THROW);
+ FrameListAnalyzer aCheck(xDesktop, xFrame, FrameAnalyzerFlags::All);
+
+ size_t c = aCheck.m_lModelFrames.size();
+ size_t i = 0;
+ for (i=0; i<c; ++i)
+ {
+ if (!fpf::closeIt(aCheck.m_lModelFrames[i]))
+ return false;
+ }
+ }
+
+ // Inform user about modified documents or still running jobs (e.g. printing).
+ {
+ css::uno::Reference< css::frame::XController > xController = xFrame->getController();
+ if (xController.is()) // some views don't uses a controller .-( (e.g. the help window)
+ {
+ bControllerSuspended = xController->suspend(true);
+ if (! bControllerSuspended)
+ return false;
+ }
+ }
+
+ // don't remove the component really by e.g. calling setComponent(null, null).
+ // It's enough to suspend the controller.
+ // If we close the frame later this controller doesn't show the same dialog again.
+ return true;
+}
+
+bool CloseDispatcher::implts_closeFrame()
+{
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ {
+ SolarMutexGuard g;
+ xFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY);
+ }
+
+ // frame already dead ? => so it's closed ... it's closed ...
+ if ( ! xFrame.is() )
+ return true;
+
+ // don't deliver ownership; our "UI user" will try it again if it failed.
+ // OK - he will get an empty frame then. But normally an empty frame
+ // should be closeable always :-)
+ if (!fpf::closeIt(xFrame))
+ return false;
+
+ {
+ SolarMutexGuard g;
+ m_xCloseFrame.clear();
+ }
+
+ return true;
+}
+
+bool CloseDispatcher::implts_establishBackingMode()
+{
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ {
+ SolarMutexGuard g;
+ xContext = m_xContext;
+ xFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY);
+ }
+
+ if (!xFrame.is())
+ return false;
+
+ css::uno::Reference < css::document::XActionLockable > xLock( xFrame, css::uno::UNO_QUERY );
+ if ( xLock.is() && xLock->isActionLocked() )
+ return false;
+
+ css::uno::Reference< css::awt::XWindow > xContainerWindow = xFrame->getContainerWindow();
+
+ css::uno::Reference< css::frame::XController > xStartModule = css::frame::StartModule::createWithParentWindow(
+ xContext, xContainerWindow);
+
+ // Attention: You MUST(!) call setComponent() before you call attachFrame().
+ css::uno::Reference< css::awt::XWindow > xBackingWin(xStartModule, css::uno::UNO_QUERY);
+ xFrame->setComponent(xBackingWin, xStartModule);
+ xStartModule->attachFrame(xFrame);
+ xContainerWindow->setVisible(true);
+
+ return true;
+}
+
+bool CloseDispatcher::implts_terminateApplication()
+{
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ {
+ SolarMutexGuard g;
+ xContext = m_xContext;
+ }
+
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
+
+ return xDesktop->terminate();
+}
+
+void CloseDispatcher::implts_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener,
+ sal_Int16 nState ,
+ const css::uno::Any& aResult )
+{
+ if (!xListener.is())
+ return;
+
+ css::frame::DispatchResultEvent aEvent(
+ css::uno::Reference< css::uno::XInterface >(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY),
+ nState,
+ aResult);
+
+ xListener->dispatchFinished(aEvent);
+}
+
+css::uno::Reference< css::frame::XFrame > CloseDispatcher::static_impl_searchRightTargetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame ,
+ std::u16string_view sTarget)
+{
+ if (o3tl::equalsIgnoreAsciiCase(sTarget, u"_self"))
+ return xFrame;
+
+ OSL_ENSURE(sTarget.empty(), "CloseDispatch used for unexpected target. Magic things will happen now .-)");
+
+ css::uno::Reference< css::frame::XFrame > xTarget = xFrame;
+ while(true)
+ {
+ // a) top frames will be closed
+ if (xTarget->isTop())
+ return xTarget;
+
+ // b) even child frame containing top level windows (e.g. query designer of database) will be closed
+ css::uno::Reference< css::awt::XWindow > xWindow = xTarget->getContainerWindow();
+ css::uno::Reference< css::awt::XTopWindow > xTopWindowCheck(xWindow, css::uno::UNO_QUERY);
+ if (xTopWindowCheck.is())
+ {
+ // b1) Note: Toolkit interface XTopWindow sometimes is used by real VCL-child-windows also .-)
+ // Be sure that these window is really a "top system window".
+ // Attention ! Checking Window->GetParent() isn't the right approach here.
+ // Because sometimes VCL create "implicit border windows" as parents even we created
+ // a simple XWindow using the toolkit only .-(
+ SolarMutexGuard aSolarLock;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->IsSystemWindow() )
+ return xTarget;
+ }
+
+ // c) try to find better results on parent frame
+ // If no parent frame exists (because this frame is used outside the desktop tree)
+ // the given frame must be used directly.
+ css::uno::Reference< css::frame::XFrame > xParent = xTarget->getCreator();
+ if ( ! xParent.is())
+ return xTarget;
+
+ // c1) check parent frame inside next loop ...
+ xTarget = xParent;
+ }
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/dispatchdisabler.cxx b/framework/source/dispatch/dispatchdisabler.cxx
new file mode 100644
index 000000000..2341d0dd6
--- /dev/null
+++ b/framework/source/dispatch/dispatchdisabler.cxx
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <services.h>
+#include <dispatch/dispatchdisabler.hxx>
+
+#include <com/sun/star/frame/DispatchDescriptor.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace css;
+using namespace framework;
+
+DispatchDisabler::DispatchDisabler(const uno::Reference< uno::XComponentContext >& )
+{
+}
+
+// XInitialization
+void SAL_CALL DispatchDisabler::initialize( const uno::Sequence< uno::Any >& aArguments )
+{
+ uno::Sequence< OUString > aDisabledURLs;
+ if( aArguments.hasElements() &&
+ ( aArguments[0] >>= aDisabledURLs ) )
+ {
+ for( OUString const & url : std::as_const(aDisabledURLs) )
+ maDisabledURLs.insert(url);
+ }
+}
+
+// XDispatchProvider
+uno::Reference< frame::XDispatch > SAL_CALL
+DispatchDisabler::queryDispatch( const util::URL& rURL,
+ const OUString& rTargetFrameName,
+ ::sal_Int32 nSearchFlags )
+{
+ // If present - disabled.
+ if( maDisabledURLs.find(rURL.Complete) != maDisabledURLs.end() ||
+ !mxSlave.is() )
+ return uno::Reference< frame::XDispatch >();
+ else
+ return mxSlave->queryDispatch(rURL, rTargetFrameName, nSearchFlags);
+}
+
+uno::Sequence< uno::Reference< frame::XDispatch > > SAL_CALL
+DispatchDisabler::queryDispatches( const uno::Sequence< frame::DispatchDescriptor >& rRequests )
+{
+ uno::Sequence< uno::Reference< frame::XDispatch > > aResult(rRequests.getLength());
+ auto aResultRange = asNonConstRange(aResult);
+ for( sal_Int32 i = 0; i < rRequests.getLength(); ++i )
+ aResultRange[i] = queryDispatch(rRequests[i].FeatureURL,
+ rRequests[i].FrameName,
+ rRequests[i].SearchFlags);
+ return aResult;
+}
+
+// XDispatchProviderInterceptor
+uno::Reference< frame::XDispatchProvider > SAL_CALL
+DispatchDisabler::getSlaveDispatchProvider()
+{
+ return mxSlave;
+}
+
+void SAL_CALL DispatchDisabler::setSlaveDispatchProvider( const uno::Reference< frame::XDispatchProvider >& xNewDispatchProvider )
+{
+ mxSlave = xNewDispatchProvider;
+}
+
+uno::Reference< frame::XDispatchProvider > SAL_CALL
+DispatchDisabler::getMasterDispatchProvider()
+{
+ return mxMaster;
+}
+void SAL_CALL
+DispatchDisabler::setMasterDispatchProvider( const uno::Reference< frame::XDispatchProvider >& xNewSupplier )
+{
+ mxMaster = xNewSupplier;
+}
+
+// XInterceptorInfo
+uno::Sequence< OUString > SAL_CALL
+ DispatchDisabler::getInterceptedURLs()
+{
+ uno::Sequence< OUString > aDisabledURLs(maDisabledURLs.size());
+ auto aDisabledURLsRange = asNonConstRange(aDisabledURLs);
+ sal_Int32 n = 0;
+ for (auto const& disabledURL : maDisabledURLs)
+ aDisabledURLsRange[n++] = disabledURL;
+ return aDisabledURLs;
+}
+
+// XElementAccess
+uno::Type SAL_CALL DispatchDisabler::getElementType()
+{
+ uno::Type aModuleType = cppu::UnoType<OUString>::get();
+ return aModuleType;
+}
+
+::sal_Bool SAL_CALL DispatchDisabler::hasElements()
+{
+ return !maDisabledURLs.empty();
+}
+
+// XNameAccess
+uno::Any SAL_CALL DispatchDisabler::getByName( const OUString& )
+{
+ return uno::Any();
+}
+
+uno::Sequence< OUString > SAL_CALL DispatchDisabler::getElementNames()
+{
+ return getInterceptedURLs();
+}
+
+sal_Bool SAL_CALL DispatchDisabler::hasByName( const OUString& rName )
+{
+ return maDisabledURLs.find(rName) != maDisabledURLs.end();
+}
+
+// XNameReplace
+void SAL_CALL DispatchDisabler::replaceByName( const OUString& rName, const uno::Any& aElement )
+{
+ removeByName( rName );
+ insertByName( rName, aElement );
+}
+
+// XNameContainer
+void DispatchDisabler::insertByName( const OUString& rName, const uno::Any& )
+{
+ maDisabledURLs.insert(rName);
+}
+
+void DispatchDisabler::removeByName( const OUString& rName )
+{
+ auto it = maDisabledURLs.find(rName);
+ if( it != maDisabledURLs.end() )
+ maDisabledURLs.erase(it);
+}
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL DispatchDisabler::getImplementationName()
+{
+ return "com.sun.star.comp.framework.services.DispatchDisabler";
+}
+
+sal_Bool SAL_CALL DispatchDisabler::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL DispatchDisabler::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.DispatchDisabler" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_DispatchDisabler_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::DispatchDisabler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/dispatchinformationprovider.cxx b/framework/source/dispatch/dispatchinformationprovider.cxx
new file mode 100644
index 000000000..a5a0bd698
--- /dev/null
+++ b/framework/source/dispatch/dispatchinformationprovider.cxx
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/dispatchinformationprovider.hxx>
+#include <dispatch/closedispatcher.hxx>
+
+#include <com/sun/star/frame/AppDispatchProvider.hpp>
+
+#include <comphelper/sequence.hxx>
+
+#include <unordered_map>
+#include <utility>
+
+namespace framework{
+
+DispatchInformationProvider::DispatchInformationProvider(css::uno::Reference< css::uno::XComponentContext > xContext ,
+ const css::uno::Reference< css::frame::XFrame >& xFrame)
+ : m_xContext (std::move(xContext ))
+ , m_xFrame (xFrame )
+{
+}
+
+DispatchInformationProvider::~DispatchInformationProvider()
+{
+}
+
+css::uno::Sequence< sal_Int16 > SAL_CALL DispatchInformationProvider::getSupportedCommandGroups()
+{
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > lProvider = implts_getAllSubProvider();
+ sal_Int32 c1 = lProvider.getLength();
+ sal_Int32 i1 = 0;
+
+ ::std::vector< sal_Int16 > lGroups;
+
+ for (i1=0; i1<c1; ++i1)
+ {
+ // ignore controller, which doesn't implement the right interface
+ css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider = lProvider[i1];
+ if (!xProvider.is())
+ continue;
+
+ const css::uno::Sequence< sal_Int16 > lProviderGroups = xProvider->getSupportedCommandGroups();
+ sal_Int32 c2 = lProviderGroups.getLength();
+ sal_Int32 i2 = 0;
+ for (i2=0; i2<c2; ++i2)
+ {
+ const sal_Int16& rGroup = lProviderGroups[i2];
+ ::std::vector< sal_Int16 >::const_iterator pGroup =
+ ::std::find(lGroups.begin(), lGroups.end(), rGroup);
+ if (pGroup == lGroups.end())
+ lGroups.push_back(rGroup);
+ }
+ }
+
+ return ::comphelper::containerToSequence(lGroups);
+}
+
+css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL DispatchInformationProvider::getConfigurableDispatchInformation(sal_Int16 nCommandGroup)
+{
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > lProvider = implts_getAllSubProvider();
+ sal_Int32 c1 = lProvider.getLength();
+ sal_Int32 i1 = 0;
+
+ std::unordered_map<OUString, css::frame::DispatchInformation> lInfos;
+
+ for (i1=0; i1<c1; ++i1)
+ {
+ try
+ {
+ // ignore controller, which doesn't implement the right interface
+ css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider = lProvider[i1];
+ if (!xProvider.is())
+ continue;
+
+ const css::uno::Sequence< css::frame::DispatchInformation > lProviderInfos = xProvider->getConfigurableDispatchInformation(nCommandGroup);
+ sal_Int32 c2 = lProviderInfos.getLength();
+ sal_Int32 i2 = 0;
+ for (i2=0; i2<c2; ++i2)
+ {
+ const css::frame::DispatchInformation& rInfo = lProviderInfos[i2];
+ auto pInfo = lInfos.find(rInfo.Command);
+ if (pInfo == lInfos.end())
+ lInfos[rInfo.Command] = rInfo;
+ }
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { continue; }
+ }
+
+ return comphelper::mapValuesToSequence(lInfos);
+}
+
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > DispatchInformationProvider::implts_getAllSubProvider()
+{
+ css::uno::Reference< css::frame::XFrame > xFrame(m_xFrame);
+ if (!xFrame.is())
+ return css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > >();
+
+ rtl::Reference<CloseDispatcher> xCloser = new CloseDispatcher(m_xContext, xFrame, u"_self"); // explicit "_self" ... not "" ... see implementation of close dispatcher itself!
+
+ css::uno::Reference< css::frame::XDispatchInformationProvider > xController (xFrame->getController() , css::uno::UNO_QUERY);
+ css::uno::Reference< css::frame::XDispatchInformationProvider > xAppDispatcher = css::frame::AppDispatchProvider::create(m_xContext);
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > lProvider{
+ xController, xCloser, xAppDispatcher
+ };
+
+ return lProvider;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/dispatchprovider.cxx b/framework/source/dispatch/dispatchprovider.cxx
new file mode 100644
index 000000000..8328e9c42
--- /dev/null
+++ b/framework/source/dispatch/dispatchprovider.cxx
@@ -0,0 +1,585 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/dispatchprovider.hxx>
+#include <loadenv/loadenv.hxx>
+#include <dispatch/loaddispatcher.hxx>
+#include <dispatch/closedispatcher.hxx>
+#include <dispatch/startmoduledispatcher.hxx>
+
+#include <pattern/window.hxx>
+#include <targets.h>
+#include "isstartmoduledispatch.hxx"
+
+#include <com/sun/star/frame/XDesktop.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/util/XCacheInfo.hpp>
+
+#include <rtl/ustring.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+#include <framework/dispatchhelper.hxx>
+
+namespace framework{
+
+/**
+ @short standard ctor/dtor
+ @descr These initialize a new instance of this class with needed information for work.
+ We hold a weakreference to our owner frame which start dispatches at us.
+ We can't use a normal reference because he hold a reference of us too ...
+ nobody can die so ...!
+
+ @seealso using at owner
+
+ @param rxContext
+ reference to servicemanager to create new services.
+ @param xFrame
+ reference to our owner frame.
+*/
+DispatchProvider::DispatchProvider( css::uno::Reference< css::uno::XComponentContext > xContext ,
+ const css::uno::Reference< css::frame::XFrame >& xFrame )
+ : m_xContext (std::move( xContext ))
+ , m_xFrame ( xFrame )
+{
+}
+
+/**
+ @short protected(!) dtor for deinitializing
+ @descr We made it protected to prevent using of us as base class instead as a member.
+ */
+DispatchProvider::~DispatchProvider()
+{
+}
+
+/**
+ @interface XDispatchProvider
+ @short search a dispatcher for given URL
+ @descr If no interceptor is set on owner, we search for right frame and dispatch URL to it.
+ If no frame was found, we do nothing.
+ But we don't do it directly here. We detect the type of our owner frame and calls
+ specialized queryDispatch() helper dependen from that. Because a Desktop handle some
+ requests in another way then a normal frame.
+
+ @param aURL
+ URL to dispatch.
+ @param sTargetFrameName
+ name of searched frame.
+ @param nSearchFlags
+ flags for searching.
+ @return A reference to a dispatch object for this URL (if someone was found!).
+
+ @threadsafe yes
+*/
+css::uno::Reference< css::frame::XDispatch > SAL_CALL DispatchProvider::queryDispatch( const css::util::URL& aURL ,
+ const OUString& sTargetFrameName ,
+ sal_Int32 nSearchFlags )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+
+ css::uno::Reference< css::frame::XFrame > xOwner(m_xFrame);
+
+ css::uno::Reference< css::frame::XDesktop > xDesktopCheck( xOwner, css::uno::UNO_QUERY );
+
+ if (xDesktopCheck.is())
+ xDispatcher = implts_queryDesktopDispatch(xOwner, aURL, sTargetFrameName, nSearchFlags);
+ else
+ xDispatcher = implts_queryFrameDispatch(xOwner, aURL, sTargetFrameName, nSearchFlags);
+
+ return xDispatcher;
+}
+
+/**
+ @interface XDispatchProvider
+ @short do the same like queryDispatch() ... but handle multiple dispatches at the same time
+ @descr It's an optimism. User give us a list of queries ... and we return a list of dispatcher.
+ If one of given queries couldn't be solved to a real existing dispatcher ...
+ we return a list with empty references in it! Order of both lists will be retained!
+
+ @seealso method queryDispatch()
+
+ @param lDescriptions
+ a list of all dispatch parameters for multiple requests
+ @return A reference a list of dispatch objects for these URLs - may with some <NULL/> values inside.
+
+ @threadsafe yes
+*/
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL DispatchProvider::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptions )
+{
+ // Create return list - which must have same size then the given descriptor
+ // It's not allowed to pack it!
+ sal_Int32 nCount = lDescriptions.getLength();
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount );
+ auto lDispatcherRange = asNonConstRange(lDispatcher);
+ // Step over all descriptors and try to get any dispatcher for it.
+ for( sal_Int32 i=0; i<nCount; ++i )
+ {
+ lDispatcherRange[i] = queryDispatch( lDescriptions[i].FeatureURL ,
+ lDescriptions[i].FrameName ,
+ lDescriptions[i].SearchFlags );
+ }
+
+ return lDispatcher;
+}
+
+/**
+ @short helper for queryDispatch()
+ @descr Every member of the frame tree (frame, desktop) must handle such request
+ in another way. So we implement different specialized methods for everyone.
+
+ @threadsafe yes
+ */
+css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_queryDesktopDispatch( const css::uno::Reference< css::frame::XFrame >& xDesktop ,
+ const css::util::URL& aURL ,
+ const OUString& sTargetFrameName ,
+ sal_Int32 nSearchFlags )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+
+ // ignore wrong requests which are not supported
+ if (
+ (sTargetFrameName==SPECIALTARGET_PARENT ) || // we have no parent by definition
+ (sTargetFrameName==SPECIALTARGET_BEAMER ) // beamer frames are allowed as child of tasks only -
+ // and they exist more than ones. We have no idea which our sub tasks is the right one
+ )
+ {
+ return nullptr;
+ }
+
+ // I) handle special cases which not right for using findFrame() first
+
+ // I.I) "_blank"
+ // It's not the right place to create a new task here - because we are queried for a dispatch object
+ // only, which can handle such request. Such dispatcher should create the required task on demand.
+ // Normally the functionality for "_blank" is provided by findFrame() - but that would create it directly
+ // here. that's why we must "intercept" here.
+
+ if (sTargetFrameName==SPECIALTARGET_BLANK)
+ {
+ if (implts_isLoadableContent(aURL))
+ xDispatcher = implts_getOrCreateDispatchHelper( E_BLANKDISPATCHER, xDesktop );
+ }
+
+ // I.II) "_default"
+ // This is a combination of search an empty task for recycling - or create a new one.
+
+ else if (sTargetFrameName==SPECIALTARGET_DEFAULT)
+ {
+ if (implts_isLoadableContent(aURL))
+ xDispatcher = implts_getOrCreateDispatchHelper( E_DEFAULTDISPATCHER, xDesktop );
+
+ if (isStartModuleDispatch(aURL))
+ xDispatcher = implts_getOrCreateDispatchHelper( E_STARTMODULEDISPATCHER, xDesktop );
+ }
+
+ // I.III) "_self", "", "_top"
+ // The desktop can't load any document - but he can handle some special protocols like "uno", "slot" ...
+ // Why is "top" here handled too? Because the desktop is the topest frame. Normally it's superfluous
+ // to use this target - but we can handle it in the same manner then "_self".
+
+ else if (
+ (sTargetFrameName==SPECIALTARGET_SELF) ||
+ (sTargetFrameName==SPECIALTARGET_TOP ) ||
+ (sTargetFrameName.isEmpty())
+ )
+ {
+ xDispatcher = implts_searchProtocolHandler(aURL);
+ }
+
+ // I.IV) no further special targets exist
+ // Now we have to search for the right target frame by calling findFrame() - but should provide our code
+ // against creation of a new task if no frame could be found.
+ // I said it before - it's allowed for dispatch() only.
+
+ else
+ {
+ sal_Int32 nRightFlags = nSearchFlags & ~css::frame::FrameSearchFlag::CREATE;
+
+ // try to find any existing target and ask him for his dispatcher
+ css::uno::Reference< css::frame::XFrame > xFoundFrame = xDesktop->findFrame(sTargetFrameName, nRightFlags);
+ if (xFoundFrame.is())
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xProvider( xFoundFrame, css::uno::UNO_QUERY );
+ xDispatcher = xProvider->queryDispatch(aURL,SPECIALTARGET_SELF,0);
+ }
+ // if it couldn't be found - but creation was allowed
+ // use special dispatcher for creation or forwarding to the browser
+ else if (nSearchFlags & css::frame::FrameSearchFlag::CREATE)
+ xDispatcher = implts_getOrCreateDispatchHelper( E_CREATEDISPATCHER, xDesktop, sTargetFrameName, nSearchFlags );
+ }
+
+ return xDispatcher;
+}
+
+css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_queryFrameDispatch( const css::uno::Reference< css::frame::XFrame >& xFrame ,
+ const css::util::URL& aURL ,
+ const OUString& sTargetFrameName ,
+ sal_Int32 nSearchFlags )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+
+ // 0) Some URLs are dispatched in a generic way (e.g. by the menu) using the default target "".
+ // But they are specified to use her own fix target. Detect such URLs here and use the correct target.
+
+ // I) handle special cases which not right for using findFrame() first
+
+ // I.I) "_blank", "_default"
+ // It's not the right place to create a new task here. Only the desktop can do that.
+ // Normally the functionality for "_blank" is provided by findFrame() - but that would create it directly
+ // here. that's why we must "intercept" here.
+
+ if (
+ (sTargetFrameName==SPECIALTARGET_BLANK ) ||
+ (sTargetFrameName==SPECIALTARGET_DEFAULT)
+ )
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
+ if (xParent.is())
+ xDispatcher = xParent->queryDispatch(aURL, sTargetFrameName, 0); // it's a special target - ignore search flags
+ }
+
+ // I.II) "_beamer"
+ // Special sub frame of a top frame only. Search or create it. ... OK it's currently a little bit HACKI.
+ // Only the sfx (means the controller) can create it.
+
+ else if (sTargetFrameName==SPECIALTARGET_BEAMER)
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xBeamer( xFrame->findFrame( SPECIALTARGET_BEAMER, css::frame::FrameSearchFlag::CHILDREN | css::frame::FrameSearchFlag::SELF ), css::uno::UNO_QUERY );
+ if (xBeamer.is())
+ {
+ xDispatcher = xBeamer->queryDispatch(aURL, SPECIALTARGET_SELF, 0);
+ }
+ else
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xController( xFrame->getController(), css::uno::UNO_QUERY );
+ if (xController.is())
+ // force using of special target - but use original search flags
+ // May the caller used the CREATE flag or not!
+ xDispatcher = xController->queryDispatch(aURL, SPECIALTARGET_BEAMER, nSearchFlags);
+ }
+ }
+
+ // I.IV) "_parent"
+ // Our parent frame (if it exist) should handle this URL.
+
+ else if (sTargetFrameName==SPECIALTARGET_PARENT)
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
+ if (xParent.is())
+ // SELF => we must address the parent directly... and not his parent or any other parent!
+ xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_SELF, 0);
+ }
+
+ // I.V) "_top"
+ // This request must be forwarded to any parent frame, till we reach a top frame.
+ // If no parent exist, we can handle itself.
+
+ else if (sTargetFrameName==SPECIALTARGET_TOP)
+ {
+ if (xFrame->isTop())
+ {
+ // If we are this top frame itself (means our owner frame)
+ // we should call ourself recursiv with a better target "_self".
+ // So we can share the same code! (see reaction for "_self" inside this method too.)
+ xDispatcher = queryDispatch(aURL,SPECIALTARGET_SELF,0);
+ }
+ else
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
+ // Normally if isTop() returned sal_False ... the parent frame MUST(!) exist ...
+ // But it seems to be better to check that here to prevent us against an access violation.
+ if (xParent.is())
+ xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_TOP, 0);
+ }
+ }
+
+ // I.VI) "_self", ""
+ // Our owner frame should handle this URL. But we can't do it for all of them.
+ // So we ask the internal set controller first. If he disagree we try to find a registered
+ // protocol handler. If this failed too - we check for a loadable content and in case of true
+ // we load it into the frame by returning specialized dispatch object.
+
+ else if (
+ (sTargetFrameName==SPECIALTARGET_SELF) ||
+ (sTargetFrameName.isEmpty())
+ )
+ {
+ // There exist a hard coded interception for special URLs.
+ if ( aURL.Complete == ".uno:CloseDoc" || aURL.Complete == ".uno:CloseWin" )
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
+ // In case the frame is not a top one, is not based on system window and has a parent,
+ // the parent frame should be queried for the correct dispatcher.
+ // See i93473
+ if (
+ !WindowHelper::isTopWindow(xFrame->getContainerWindow()) &&
+ !VCLUnoHelper::GetWindow(xFrame->getContainerWindow())->IsSystemWindow() &&
+ xParent.is()
+ )
+ xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_SELF, 0);
+ else
+ xDispatcher = implts_getOrCreateDispatchHelper( E_CLOSEDISPATCHER, xFrame );
+ }
+ else if ( aURL.Complete == ".uno:CloseFrame" )
+ xDispatcher = implts_getOrCreateDispatchHelper( E_CLOSEDISPATCHER, xFrame );
+
+ if ( ! xDispatcher.is())
+ {
+ // Ask our controller for his agreement for these dispatched URL ...
+ // because some URLs are internal and can be handled faster by SFX - which most is the current controller!
+ // But in case of e.g. the bibliography not all queries will be handled successfully here.
+ css::uno::Reference< css::frame::XDispatchProvider > xController( xFrame->getController(), css::uno::UNO_QUERY );
+ if (xController.is())
+ xDispatcher = xController->queryDispatch(aURL, SPECIALTARGET_SELF, 0);
+ }
+
+ // If controller has no fun to dispatch these URL - we must search another right dispatcher.
+ // Search for any registered protocol handler first.
+ if (!xDispatcher.is())
+ xDispatcher = implts_searchProtocolHandler(aURL);
+
+ // Not for controller - not for protocol handler
+ // It should be a loadable content - may be a file. Check it ...
+ // This check is necessary to found out, that
+ // support for some protocols isn't installed by user. May be
+ // "ftp" isn't available. So we suppress creation of our self dispatcher.
+ // The result will be clear. He can't handle it - but he would try it.
+ if (
+ ( ! xDispatcher.is() ) &&
+ ( implts_isLoadableContent(aURL) )
+ )
+ {
+ xDispatcher = implts_getOrCreateDispatchHelper( E_SELFDISPATCHER, xFrame );
+ }
+ }
+
+ // I.VII) no further special handlings exist
+ // Now we have to search for the right target frame by calling findFrame() - but should provide our code
+ // against creation of a new task if no frame could be found.
+ // I said it before - it's allowed for dispatch() only.
+
+ else
+ {
+ sal_Int32 nRightFlags = nSearchFlags & ~css::frame::FrameSearchFlag::CREATE;
+
+ // try to find any existing target and ask him for his dispatcher
+ css::uno::Reference< css::frame::XFrame > xFoundFrame = xFrame->findFrame(sTargetFrameName, nRightFlags);
+ if (xFoundFrame.is())
+ {
+ // Attention: Found target is our own owner frame!
+ // Don't ask him for his dispatcher. We know it already - it's our self dispatch helper.
+ // Otherwise we can start a never ending recursiv call. Why?
+ // Somewhere called our owner frame - he called some interceptor objects - and may by this dispatch provider
+ // is called. If wa use queryDispatch() on our owner frame again - we start this call stack again ... and again.
+ if (xFoundFrame==xFrame)
+ xDispatcher = implts_getOrCreateDispatchHelper( E_SELFDISPATCHER, xFrame );
+ else
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xProvider( xFoundFrame, css::uno::UNO_QUERY );
+ xDispatcher = xProvider->queryDispatch(aURL,SPECIALTARGET_SELF,0);
+ }
+ }
+ else
+ // if it couldn't be found - but creation was allowed
+ // forward request to the desktop.
+ // Note: The given target name must be used to set the name on new created task!
+ // Don't forward request by changing it to a special one e.g _blank.
+ // Use the CREATE flag only to prevent call against further searches.
+ // We already know it - the target must be created new.
+ if (nSearchFlags & css::frame::FrameSearchFlag::CREATE)
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
+ if (xParent.is())
+ xDispatcher = xParent->queryDispatch(aURL, sTargetFrameName, css::frame::FrameSearchFlag::CREATE);
+ }
+ }
+
+ return xDispatcher;
+}
+
+/**
+ @short search for a registered protocol handler and ask him for a dispatch object
+ @descr We search a suitable handler inside our cfg package org.openoffice.Office.ProtocolHandler.
+ If we found anyone, we create and initialize it. Initialize means: we set our owner frame on it
+ as context information. He can use it or leave it. Of course - we are aware of handler implementations,
+ which doesn't support initialization. It's an optional feature.
+
+ @param aURL
+ the dispatch URL for which may a handler is registered
+
+ @return A dispatch object if a handler was found and agree with the given URL or <NULL/> otherwise.
+
+ @threadsafe yes
+*/
+css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_searchProtocolHandler( const css::util::URL& aURL )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+ ProtocolHandler aHandler;
+
+ // This member is threadsafe by himself and lives if we live - we don't need any mutex here.
+ if (m_aProtocolHandlerCache.search(aURL,&aHandler))
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xHandler;
+ {
+ SolarMutexGuard g;
+
+ // create it
+ bool bInitialize = true;
+ try
+ {
+ // Only create the protocol handler instance once, the creation is expensive.
+ auto it = m_aProtocolHandlers.find(aHandler.m_sUNOName);
+ if (it == m_aProtocolHandlers.end())
+ {
+ xHandler.set(
+ css::uno::Reference<css::lang::XMultiServiceFactory>(m_xContext->getServiceManager(), css::uno::UNO_QUERY_THROW)
+ ->createInstance(aHandler.m_sUNOName),
+ css::uno::UNO_QUERY);
+
+ // Check if the handler explicitly requested to avoid caching.
+ css::uno::Reference<css::util::XCacheInfo> xCacheInfo(xHandler, css::uno::UNO_QUERY);
+ if (!xCacheInfo.is() || xCacheInfo->isCachingAllowed())
+ {
+ m_aProtocolHandlers.emplace(aHandler.m_sUNOName, xHandler);
+ }
+ }
+ else
+ {
+ xHandler = it->second;
+ bInitialize = false;
+ }
+ }
+ catch(const css::uno::Exception&) {}
+
+ // look if initialization is necessary
+ css::uno::Reference< css::lang::XInitialization > xInit( xHandler, css::uno::UNO_QUERY );
+ if (xInit.is() && bInitialize)
+ {
+ css::uno::Reference< css::frame::XFrame > xOwner( m_xFrame.get(), css::uno::UNO_QUERY );
+ SAL_WARN_IF(!xOwner.is(), "fwk", "DispatchProvider::implts_searchProtocolHandler(): Couldn't get reference to my owner frame. So I can't set may needed context information for this protocol handler.");
+ if (xOwner.is())
+ {
+ try
+ {
+ // but do it only, if all context information is OK
+ css::uno::Sequence< css::uno::Any > lContext{ css::uno::Any(xOwner) };
+ xInit->initialize(lContext);
+ }
+ catch(const css::uno::Exception&) {}
+ }
+ }
+ }
+
+ // ask for his (sub)dispatcher for the given URL
+ if (xHandler.is())
+ xDispatcher = xHandler->queryDispatch(aURL,SPECIALTARGET_SELF,0);
+ }
+
+ return xDispatcher;
+}
+
+/**
+ @short get or create new dispatch helper
+ @descr Sometimes we need some helper implementations to support dispatching of special URLs or commands.
+ But it's not a good idea to hold these services for the whole life time of this provider instance.
+ We should create it on demand...
+ That's why we implement this method. It return an already existing helper or create a new one otherwise.
+
+ @attention The parameter sTarget and nSearchFlags are defaulted to "" and 0!
+ Mostly it depends from the parameter eHelper is they are required or not.
+
+ @param eHelper
+ specify the requested dispatch helper
+ @param xOwner
+ the target of possible dispatch() call on created dispatch helper
+ @param sTarget
+ the target parameter of the original queryDispatch() request
+ @param nSearchFlags
+ the flags parameter of the original queryDispatch() request
+ @return A reference to a dispatch helper.
+
+ @threadsafe yes
+*/
+css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_getOrCreateDispatchHelper( EDispatchHelper eHelper ,
+ const css::uno::Reference< css::frame::XFrame >& xOwner ,
+ const OUString& sTarget ,
+ sal_Int32 nSearchFlags)
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatchHelper;
+
+ switch (eHelper)
+ {
+ case E_CREATEDISPATCHER :
+ xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, sTarget, nSearchFlags);
+ break;
+
+ case E_BLANKDISPATCHER :
+ {
+ if (xOwner.is())
+ xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_BLANK, 0);
+ }
+ break;
+
+ case E_DEFAULTDISPATCHER :
+ {
+ if (xOwner.is())
+ xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_DEFAULT, 0);
+ }
+ break;
+
+ case E_SELFDISPATCHER :
+ xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_SELF, 0);
+ break;
+
+ case E_CLOSEDISPATCHER :
+ xDispatchHelper = new CloseDispatcher( m_xContext, xOwner, sTarget );
+ break;
+
+ case E_STARTMODULEDISPATCHER :
+ xDispatchHelper = new StartModuleDispatcher( m_xContext );
+ break;
+ }
+
+ return xDispatchHelper;
+}
+
+/**
+ @short check URL for support by our used loader or handler
+ @descr If we must return our own dispatch helper implementations (self, blank, create dispatcher!)
+ we should be sure, that URL describe any loadable content. Otherwise slot/uno URLs
+ will be detected... but there exist nothing for real loading into a target frame!
+
+ @param aURL
+ URL which should be "detected"
+ @return <TRUE/> if somewhere could handle that - <FALSE/> otherwise.
+
+ @threadsafe yes
+*/
+bool DispatchProvider::implts_isLoadableContent( const css::util::URL& aURL )
+{
+ LoadEnv::EContentType eType = LoadEnv::classifyContent(aURL.Complete, css::uno::Sequence< css::beans::PropertyValue >());
+ return ( eType == LoadEnv::E_CAN_BE_LOADED );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/interceptionhelper.cxx b/framework/source/dispatch/interceptionhelper.cxx
new file mode 100644
index 000000000..57ab28ee4
--- /dev/null
+++ b/framework/source/dispatch/interceptionhelper.cxx
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/interceptionhelper.hxx>
+
+#include <com/sun/star/frame/XInterceptorInfo.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <osl/diagnose.h>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace com::sun::star;
+
+namespace framework{
+
+InterceptionHelper::InterceptionHelper(const css::uno::Reference< css::frame::XFrame >& xOwner,
+ css::uno::Reference< css::frame::XDispatchProvider > xSlave)
+ : m_xOwnerWeak (xOwner )
+ , m_xSlave (std::move(xSlave ))
+{
+}
+
+InterceptionHelper::~InterceptionHelper()
+{
+}
+
+css::uno::Reference< css::frame::XDispatch > SAL_CALL InterceptionHelper::queryDispatch(const css::util::URL& aURL ,
+ const OUString& sTargetFrameName,
+ sal_Int32 nSearchFlags )
+{
+ css::uno::Reference<css::frame::XDispatchProvider> xInterceptor;
+ // SAFE {
+ {
+ SolarMutexGuard aReadLock;
+
+ // a) first search an interceptor, which match to this URL by its URL pattern registration
+ // Note: if it return NULL - it does not mean an empty interceptor list automatically!
+ InterceptorList::const_iterator pIt = m_lInterceptionRegs.findByPattern(aURL.Complete);
+ if (pIt != m_lInterceptionRegs.end())
+ xInterceptor = pIt->xInterceptor;
+
+ // b) No match by registration - but a valid interceptor list.
+ // Find first interceptor w/o pattern, so we need to query it
+ if (!xInterceptor.is())
+ {
+ for (auto const& lInterceptionReg : m_lInterceptionRegs)
+ {
+ if (!lInterceptionReg.lURLPattern.hasElements())
+ {
+ // no pattern -> need to ask this guy!
+ xInterceptor = lInterceptionReg.xInterceptor;
+ break;
+ }
+ }
+ // if we didn't find any non-pattern interceptor, there's no-one
+ // registered for this command url (we already searched for matching
+ // patterns above)
+ }
+ // c) No registered interceptor => use our direct slave.
+ // This helper exist by design and must be valid everytimes ...
+ // But to be more feature proof - we should check that .-)
+ if (!xInterceptor.is() && m_xSlave.is())
+ xInterceptor = m_xSlave;
+ }
+ // } SAFE
+
+ css::uno::Reference< css::frame::XDispatch > xReturn;
+ if (xInterceptor.is())
+ xReturn = xInterceptor->queryDispatch(aURL, sTargetFrameName, nSearchFlags);
+ return xReturn;
+}
+
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL InterceptionHelper::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor )
+{
+ sal_Int32 c = lDescriptor.getLength();
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatches (c);
+ css::uno::Reference< css::frame::XDispatch >* pDispatches = lDispatches.getArray();
+ const css::frame::DispatchDescriptor* pDescriptor = lDescriptor.getConstArray();
+
+ for (sal_Int32 i=0; i<c; ++i)
+ pDispatches[i] = queryDispatch(pDescriptor[i].FeatureURL, pDescriptor[i].FrameName, pDescriptor[i].SearchFlags);
+
+ return lDispatches;
+}
+
+void SAL_CALL InterceptionHelper::registerDispatchProviderInterceptor(const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor)
+{
+ // reject incorrect calls of this interface method
+ css::uno::Reference< css::frame::XDispatchProvider > xThis(this);
+ if (!xInterceptor.is())
+ throw css::uno::RuntimeException("NULL references not allowed as in parameter", xThis);
+
+ // Fill a new info structure for new interceptor.
+ // Save his reference and try to get an additional URL/pattern list from him.
+ // If no list exist register these interceptor for all dispatch events with "*"!
+ InterceptorInfo aInfo;
+
+ aInfo.xInterceptor = xInterceptor;
+ css::uno::Reference< css::frame::XInterceptorInfo > xInfo(xInterceptor, css::uno::UNO_QUERY);
+ if (xInfo.is())
+ aInfo.lURLPattern = xInfo->getInterceptedURLs();
+ else
+ aInfo.lURLPattern = { "*" };
+
+ // SAFE {
+ SolarMutexClearableGuard aWriteLock;
+
+ // a) no interceptor at all - set this instance as master for given interceptor
+ // and set our slave as its slave - and put this interceptor to the list.
+ // Its place there doesn't matter. Because this list is currently empty.
+ if (m_lInterceptionRegs.empty())
+ {
+ xInterceptor->setMasterDispatchProvider(xThis );
+ xInterceptor->setSlaveDispatchProvider (m_xSlave);
+ m_lInterceptionRegs.push_back(aInfo);
+ }
+
+ // b) OK - there is at least one interceptor already registered.
+ // It's slave and it's master must be valid references ...
+ // because we created it.
+
+ // insert it before any other existing interceptor - means at the beginning of our list.
+ else
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xSlaveD = m_lInterceptionRegs.begin()->xInterceptor;
+ css::uno::Reference< css::frame::XDispatchProviderInterceptor > xSlaveI (xSlaveD , css::uno::UNO_QUERY);
+
+ xInterceptor->setMasterDispatchProvider(xThis );
+ xInterceptor->setSlaveDispatchProvider (xSlaveD );
+ xSlaveI->setMasterDispatchProvider (aInfo.xInterceptor);
+
+ m_lInterceptionRegs.push_front(aInfo);
+ }
+
+ css::uno::Reference< css::frame::XFrame > xOwner(m_xOwnerWeak.get(), css::uno::UNO_QUERY);
+
+ aWriteLock.clear();
+ // } SAFE
+
+ // Don't forget to send a frame action event "context changed".
+ // Any cached dispatch objects must be validated now!
+ if (xOwner.is())
+ xOwner->contextChanged();
+}
+
+void SAL_CALL InterceptionHelper::releaseDispatchProviderInterceptor(const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor)
+{
+ // reject wrong calling of this interface method
+ css::uno::Reference< css::frame::XDispatchProvider > xThis(this);
+ if (!xInterceptor.is())
+ throw css::uno::RuntimeException("NULL references not allowed as in parameter", xThis);
+
+ // SAFE {
+ SolarMutexClearableGuard aWriteLock;
+
+ // search this interceptor ...
+ // If it could be located inside cache -
+ // use its slave/master relations to update the interception list;
+ // set empty references for it as new master and slave;
+ // and release it from out cache.
+ InterceptorList::iterator pIt = m_lInterceptionRegs.findByReference(xInterceptor);
+ if (pIt != m_lInterceptionRegs.end())
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xSlaveD = xInterceptor->getSlaveDispatchProvider();
+ css::uno::Reference< css::frame::XDispatchProvider > xMasterD = xInterceptor->getMasterDispatchProvider();
+ css::uno::Reference< css::frame::XDispatchProviderInterceptor > xSlaveI (xSlaveD , css::uno::UNO_QUERY);
+ css::uno::Reference< css::frame::XDispatchProviderInterceptor > xMasterI (xMasterD , css::uno::UNO_QUERY);
+
+ if (xMasterI.is())
+ xMasterI->setSlaveDispatchProvider(xSlaveD);
+
+ if (xSlaveI.is())
+ {
+ try
+ {
+ xSlaveI->setMasterDispatchProvider(xMasterD);
+ }
+ catch (const lang::DisposedException&)
+ {
+ TOOLS_WARN_EXCEPTION("fwk.dispatch",
+ "InterceptionHelper::releaseDispatchProviderInterceptor: "
+ "xSlaveI is disposed: ");
+ }
+ }
+
+ xInterceptor->setSlaveDispatchProvider (css::uno::Reference< css::frame::XDispatchProvider >());
+ xInterceptor->setMasterDispatchProvider(css::uno::Reference< css::frame::XDispatchProvider >());
+
+ m_lInterceptionRegs.erase(pIt);
+ }
+
+ css::uno::Reference< css::frame::XFrame > xOwner(m_xOwnerWeak.get(), css::uno::UNO_QUERY);
+
+ aWriteLock.clear();
+ // } SAFE
+
+ // Don't forget to send a frame action event "context changed".
+ // Any cached dispatch objects must be validated now!
+ if (xOwner.is())
+ xOwner->contextChanged();
+}
+
+#define FORCE_DESTRUCTION_OF_INTERCEPTION_CHAIN
+void SAL_CALL InterceptionHelper::disposing(const css::lang::EventObject& aEvent)
+{
+ #ifdef FORCE_DESTRUCTION_OF_INTERCEPTION_CHAIN
+ // SAFE ->
+ SolarMutexResettableGuard aReadLock;
+
+ // check call... we accept such disposing calls only from our owner frame.
+ css::uno::Reference< css::frame::XFrame > xOwner(m_xOwnerWeak.get(), css::uno::UNO_QUERY);
+ if (aEvent.Source != xOwner)
+ return;
+
+ // Because every interceptor hold at least one reference to us ... and we destruct this list
+ // of interception objects ... we should hold ourself alive .-)
+ css::uno::Reference< css::frame::XDispatchProvider > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY_THROW);
+
+ // We need a full copy of all currently registered interceptor objects.
+ // Otherwise we can't iterate over this vector without the risk, that our iterator will be invalid.
+ // Because this vetor will be influenced by every deregistered interceptor.
+ InterceptionHelper::InterceptorList aCopy = m_lInterceptionRegs;
+
+ aReadLock.clear();
+ // <- SAFE
+
+ for (auto & elem : aCopy)
+ {
+ if (elem.xInterceptor.is())
+ {
+ css::uno::Reference< css::frame::XDispatchProviderInterceptor > xInterceptor(elem.xInterceptor, css::uno::UNO_QUERY_THROW);
+ releaseDispatchProviderInterceptor(xInterceptor);
+ elem.xInterceptor.clear();
+ }
+ }
+
+ aCopy.clear();
+
+ #if OSL_DEBUG_LEVEL > 0
+ // SAFE ->
+ aReadLock.reset();
+ if (!m_lInterceptionRegs.empty() )
+ OSL_FAIL("There are some pending interceptor objects, which seems to be registered during (!) the destruction of a frame.");
+ aReadLock.clear();
+ // <- SAFE
+ #endif // ODL_DEBUG_LEVEL>0
+
+ #endif // FORCE_DESTRUCTION_OF_INTERCEPTION_CHAIN
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/isstartmoduledispatch.hxx b/framework/source/dispatch/isstartmoduledispatch.hxx
new file mode 100644
index 000000000..a968a36f6
--- /dev/null
+++ b/framework/source/dispatch/isstartmoduledispatch.hxx
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/util/URL.hpp>
+
+namespace framework
+{
+inline bool isStartModuleDispatch(css::util::URL const& url)
+{
+ return url.Complete == ".uno:ShowStartModule";
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/loaddispatcher.cxx b/framework/source/dispatch/loaddispatcher.cxx
new file mode 100644
index 000000000..04ea5cf5c
--- /dev/null
+++ b/framework/source/dispatch/loaddispatcher.cxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/loaddispatcher.hxx>
+#include <loadenv/loadenvexception.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <utility>
+
+namespace framework{
+
+LoadDispatcher::LoadDispatcher(const css::uno::Reference< css::uno::XComponentContext >& xContext ,
+ const css::uno::Reference< css::frame::XFrame >& xOwnerFrame ,
+ OUString sTargetName ,
+ sal_Int32 nSearchFlags)
+ : m_xOwnerFrame (xOwnerFrame )
+ , m_sTarget (std::move(sTargetName ))
+ , m_nSearchFlags(nSearchFlags)
+ , m_aLoader (xContext )
+{
+}
+
+LoadDispatcher::~LoadDispatcher()
+{
+}
+
+void SAL_CALL LoadDispatcher::dispatchWithNotification(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ impl_dispatch( aURL, lArguments, xListener );
+}
+
+void SAL_CALL LoadDispatcher::dispatch(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
+{
+ impl_dispatch( aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >() );
+}
+
+css::uno::Any SAL_CALL LoadDispatcher::dispatchWithReturnValue( const css::util::URL& rURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
+{
+ return impl_dispatch( rURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >());
+}
+
+void SAL_CALL LoadDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
+ const css::util::URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL LoadDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
+ const css::util::URL& /*aURL*/ )
+{
+}
+
+css::uno::Any LoadDispatcher::impl_dispatch( const css::util::URL& rURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ // Attention: May be nobody outside hold such temp. dispatch object alive (because
+ // the container in which we resist isn't implemented threadsafe but updated by a timer
+ // and clear our reference...) we should hold us self alive!
+ css::uno::Reference< css::uno::XInterface > xThis(static_cast< css::frame::XNotifyingDispatch* >(this), css::uno::UNO_QUERY);
+
+ osl::MutexGuard g(m_mutex);
+
+ // We are the only client of this load env object... but
+ // may a dispatch request before is still in progress (?!).
+ // Then we should wait a little bit and block this new request.
+ // In case we run into the timeout, we should reject this new request
+ // and return "FAILED" as result. Otherwise we can start this new operation.
+ if (!m_aLoader.waitWhileLoading(2000)) // => 2 sec.
+ {
+ if (xListener.is())
+ xListener->dispatchFinished(
+ css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::DONTKNOW, css::uno::Any())); // DONTKNOW? ... not really started ... not really failed :-)
+ }
+
+ css::uno::Reference< css::frame::XFrame > xBaseFrame(m_xOwnerFrame.get(), css::uno::UNO_QUERY);
+ if (!xBaseFrame.is() && xListener.is())
+ xListener->dispatchFinished(
+ css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::FAILURE, css::uno::Any()));
+
+ // OK ... now the internal loader seems to be usable for new requests
+ // and our owner frame seems to be valid for such operations.
+ // Initialize it with all new but needed properties and start the loading.
+ css::uno::Reference< css::lang::XComponent > xComponent;
+ try
+ {
+ m_aLoader.startLoading( rURL.Complete, lArguments, xBaseFrame, m_sTarget, m_nSearchFlags, LoadEnvFeatures::AllowContentHandler | LoadEnvFeatures::WorkWithUI);
+ m_aLoader.waitWhileLoading(); // wait for ever!
+ xComponent = m_aLoader.getTargetComponent();
+
+ // TODO thinking about asynchronous operations and listener support
+ }
+ catch(const LoadEnvException& e)
+ {
+ SAL_WARN(
+ "fwk.dispatch",
+ "caught LoadEnvException " << +e.m_nID << " \"" << e.m_sMessage
+ << "\""
+ << (e.m_exOriginal.has<css::uno::Exception>()
+ ? (", " + e.m_exOriginal.getValueTypeName() + " \""
+ + e.m_exOriginal.get<css::uno::Exception>().Message
+ + "\"")
+ : OUString())
+ << " while dispatching <" << rURL.Complete << ">");
+ xComponent.clear();
+ }
+
+ if (xListener.is())
+ {
+ if (xComponent.is())
+ xListener->dispatchFinished(
+ css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::SUCCESS, css::uno::Any()));
+ else
+ xListener->dispatchFinished(
+ css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::FAILURE, css::uno::Any()));
+ }
+
+ // return the model - like loadComponentFromURL()
+ css::uno::Any aRet;
+ if ( xComponent.is () )
+ aRet <<= xComponent;
+
+ return aRet;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/mailtodispatcher.cxx b/framework/source/dispatch/mailtodispatcher.cxx
new file mode 100644
index 000000000..9149b9b40
--- /dev/null
+++ b/framework/source/dispatch/mailtodispatcher.cxx
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/mailtodispatcher.hxx>
+#include <services.h>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteException.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <utility>
+
+namespace framework{
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL MailToDispatcher::getImplementationName()
+{
+ return "com.sun.star.comp.framework.MailToDispatcher";
+}
+
+sal_Bool SAL_CALL MailToDispatcher::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL MailToDispatcher::getSupportedServiceNames()
+{
+ return { SERVICENAME_PROTOCOLHANDLER };
+}
+
+
+/**
+ @short standard ctor
+ @descr This initializes a new instance of this class with needed information for work.
+
+ @param rxContext
+ reference to uno servicemanager for creation of new services
+*/
+MailToDispatcher::MailToDispatcher( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : m_xContext (std::move( xContext ))
+{
+}
+
+/**
+ @short standard dtor
+*/
+MailToDispatcher::~MailToDispatcher()
+{
+}
+
+/**
+ @short decide if this dispatch implementation can be used for requested URL or not
+ @descr A protocol handler is registered for a URL pattern inside configuration and will
+ be asked by the generic dispatch mechanism inside framework, if he can handle this
+ special URL which match his registration. He can agree by returning of a valid dispatch
+ instance or disagree by returning <NULL/>.
+ We don't create new dispatch instances here really - we return THIS as result to handle it
+ at the same implementation.
+*/
+css::uno::Reference< css::frame::XDispatch > SAL_CALL MailToDispatcher::queryDispatch( const css::util::URL& aURL ,
+ const OUString& /*sTarget*/ ,
+ sal_Int32 /*nFlags*/ )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+ if (aURL.Complete.startsWith("mailto:"))
+ xDispatcher = this;
+ return xDispatcher;
+}
+
+/**
+ @short do the same like dispatch() but for multiple requests at the same time
+*/
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL MailToDispatcher::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor )
+{
+ sal_Int32 nCount = lDescriptor.getLength();
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount );
+ auto lDispatcherRange = asNonConstRange(lDispatcher);
+ for( sal_Int32 i=0; i<nCount; ++i )
+ {
+ lDispatcherRange[i] = queryDispatch(
+ lDescriptor[i].FeatureURL,
+ lDescriptor[i].FrameName,
+ lDescriptor[i].SearchFlags);
+ }
+ return lDispatcher;
+}
+
+/**
+ @short dispatch URL with arguments
+ @descr We use threadsafe internal method to do so. It returns a state value - but we ignore it.
+ Because we don't support status listener notifications here. Status events are not guaranteed -
+ and we call another service internally which doesn't return any notifications too.
+
+ @param aURL
+ mail URL which should be executed
+ @param lArguments
+ list of optional arguments for this mail request
+*/
+void SAL_CALL MailToDispatcher::dispatch( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/ )
+{
+ // dispatch() is an [oneway] call ... and may our user release his reference to us immediately.
+ // So we should hold us self alive till this call ends.
+ css::uno::Reference< css::frame::XNotifyingDispatch > xSelfHold(this);
+ implts_dispatch(aURL);
+ // No notification for status listener!
+}
+
+/**
+ @short dispatch with guaranteed notifications about success
+ @descr We use threadsafe internal method to do so. Return state of this function will be used
+ for notification if an optional listener is given.
+
+ @param aURL
+ mail URL which should be executed
+ @param lArguments
+ list of optional arguments for this mail request
+ @param xListener
+ reference to a valid listener for state events
+*/
+void SAL_CALL MailToDispatcher::dispatchWithNotification( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ // This class was designed to die by reference. And if user release his reference to us immediately after calling this method
+ // we can run into some problems. So we hold us self alive till this method ends.
+ // Another reason: We can use this reference as source of sending event at the end too.
+ css::uno::Reference< css::frame::XNotifyingDispatch > xThis(this);
+
+ bool bState = implts_dispatch(aURL);
+ if (xListener.is())
+ {
+ css::frame::DispatchResultEvent aEvent;
+ if (bState)
+ aEvent.State = css::frame::DispatchResultState::SUCCESS;
+ else
+ aEvent.State = css::frame::DispatchResultState::FAILURE;
+ aEvent.Source = xThis;
+
+ xListener->dispatchFinished( aEvent );
+ }
+}
+
+/**
+ @short threadsafe helper for dispatch calls
+ @descr We support two interfaces for the same process - dispatch URLs. That the reason for this internal
+ function. It implements the real dispatch operation and returns a state value which inform caller
+ about success. He can notify listener then by using this return value.
+
+ @param aURL
+ mail URL which should be executed
+
+ @return <TRUE/> if dispatch could be started successfully
+ Note: Our internal used shell executor doesn't return any state value - so we must
+ believe that call was successful.
+ <FALSE/> if necessary resource couldn't be created or an exception was thrown.
+*/
+bool MailToDispatcher::implts_dispatch( const css::util::URL& aURL )
+{
+ bool bSuccess = false;
+
+ css::uno::Reference< css::system::XSystemShellExecute > xSystemShellExecute = css::system::SystemShellExecute::create( m_xContext );
+
+ try
+ {
+ // start mail client
+ // Because there is no notification about success - we use case of
+ // no detected exception as SUCCESS - FAILED otherwise.
+ xSystemShellExecute->execute( aURL.Complete, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY );
+ bSuccess = true;
+ }
+ catch (const css::lang::IllegalArgumentException&)
+ {
+ }
+ catch (const css::system::SystemShellExecuteException&)
+ {
+ }
+
+ return bSuccess;
+}
+
+/**
+ @short add/remove listener for state events
+ @descr Because we use an external process to forward such mail URLs, and this process doesn't
+ return any notifications about success or failed state - we don't support such status
+ listener. We have no status to send.
+
+ @param xListener
+ reference to a valid listener for state events
+ @param aURL
+ URL about listener will be informed, if something occurred
+*/
+void SAL_CALL MailToDispatcher::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ ,
+ const css::util::URL& /*aURL*/ )
+{
+ // not supported yet
+}
+
+void SAL_CALL MailToDispatcher::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ ,
+ const css::util::URL& /*aURL*/ )
+{
+ // not supported yet
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_MailToDispatcher_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::MailToDispatcher(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/oxt_handler.cxx b/framework/source/dispatch/oxt_handler.cxx
new file mode 100644
index 000000000..df956c7ee
--- /dev/null
+++ b/framework/source/dispatch/oxt_handler.cxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/oxt_handler.hxx>
+#include <unotools/mediadescriptor.hxx>
+
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/task/XJobExecutor.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/long.hxx>
+#include <utility>
+
+namespace framework{
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL Oxt_Handler::getImplementationName()
+{
+ return "com.sun.star.comp.framework.OXTFileHandler";
+}
+
+sal_Bool SAL_CALL Oxt_Handler::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL Oxt_Handler::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ContentHandler" };
+}
+
+
+/*-************************************************************************************************************
+ @short standard ctor
+ @descr These initialize a new instance of this class with needed information for work.
+
+ @seealso using at owner
+
+ @param "xFactory", reference to service manager for creation of new services
+ @onerror Show an assertion and do nothing else.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+Oxt_Handler::Oxt_Handler( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : m_xContext (std::move( xContext ))
+{
+}
+
+/*-************************************************************************************************************
+ @short standard dtor
+*//*-*************************************************************************************************************/
+Oxt_Handler::~Oxt_Handler()
+{
+}
+
+/*-************************************************************************************************************
+ @interface css::frame::XDispatch
+
+ @short try to load audio file
+ @descr This method try to load given audio file by URL and play it. We use vcl/Sound class to do that.
+ Playing of sound is asynchron every time.
+
+ @attention We must hold us alive by ourself ... because we use async. vcl sound player ... but playing is started
+ in async interface call "dispatch()" too. And caller forget us immediately. But then our uno ref count
+ will decreased to 0 and will die. The only solution is to use own reference to our implementation.
+ But we do it for really started jobs only and release it during call back of vcl.
+
+ @seealso class vcl/Sound
+ @seealso method implts_PlayerNotify()
+
+ @param "aURL" , URL to dispatch.
+ @param "lArguments", list of optional arguments.
+ @onerror We do nothing.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+void SAL_CALL Oxt_Handler::dispatchWithNotification( const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ std::unique_lock g(m_mutex);
+
+ css::uno::Sequence< css::uno::Any > lParams{ css::uno::Any(aURL.Main) };
+
+ css::uno::Reference< css::uno::XInterface > xService = m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( "com.sun.star.deployment.ui.PackageManagerDialog", lParams, m_xContext );
+ css::uno::Reference< css::task::XJobExecutor > xExecutable( xService, css::uno::UNO_QUERY );
+ if ( xExecutable.is() )
+ xExecutable->trigger( OUString() );
+
+ if ( xListener.is() )
+ {
+ css::frame::DispatchResultEvent aEvent;
+ aEvent.State = css::frame::DispatchResultState::SUCCESS;
+ xListener->dispatchFinished( aEvent );
+ }
+}
+
+void SAL_CALL Oxt_Handler::dispatch( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
+{
+ dispatchWithNotification( aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >() );
+}
+
+/*-************************************************************************************************************
+ @interface css::document::XExtendedFilterDetection
+
+ @short try to detect file (given as argument included in "lDescriptor")
+ @descr We try to detect, if given file could be handled by this class and is a well known one.
+ If it is - we return right internal type name - otherwise we return nothing!
+ So call can search for another detect service and ask him too.
+
+ @attention a) We don't need any mutex here ... because we don't use any member!
+ b) Don't use internal player instance "m_pPlayer" to detect given sound file!
+ It's not necessary to do that ... and we can use temp. variable to do the same.
+ This way is easy - we don't must synchronize it with currently played sounds!
+ Another reason to do so ... We are a listener on our internal ma_Player object.
+ If you would call "IsSoundFile()" on this instance, he would call us back and
+ we make some unnecessary things ...
+ @param "lDescriptor", description of file to detect
+ @return Internal type name which match this file ... or nothing if it is unknown.
+
+ @onerror We return nothing.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+OUString SAL_CALL Oxt_Handler::detect( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor )
+{
+ // Our default is "nothing". So we can return it, if detection failed or file type is really unknown.
+ OUString sTypeName;
+
+ // Analyze given descriptor to find filename or input stream or...
+ utl::MediaDescriptor aDescriptor( lDescriptor );
+ OUString sURL = aDescriptor.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_URL, OUString() );
+
+ tools::Long nLength = sURL.getLength();
+ if ( ( nLength > 4 ) && sURL.matchIgnoreAsciiCase( ".oxt", nLength-4 ) )
+ {
+ // "IsSoundFile" differs between different "wav" and "au" file versions...
+ // couldn't return this information... because: it use the OS to detect it!
+ // I think we can than following ones:
+ // a) look for given extension of url to map our type decision HARD CODED!!!
+ // b) return preferred type every time... it's easy :-)
+ sTypeName = "oxt_OpenOffice_Extension";
+ aDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sTypeName;
+ aDescriptor >> lDescriptor;
+ }
+
+ // Return our decision.
+ return sTypeName;
+}
+
+} // namespace framework
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_Oxt_Handler_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::Oxt_Handler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/popupmenudispatcher.cxx b/framework/source/dispatch/popupmenudispatcher.cxx
new file mode 100644
index 000000000..3220ceece
--- /dev/null
+++ b/framework/source/dispatch/popupmenudispatcher.cxx
@@ -0,0 +1,269 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/popupmenudispatcher.hxx>
+#include <services.h>
+#include <properties.h>
+
+#include <com/sun/star/frame/XLayoutManager2.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/ui/XUIElement.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+namespace framework{
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::cppu;
+using namespace ::osl;
+
+PopupMenuDispatcher::PopupMenuDispatcher(
+ uno::Reference< XComponentContext > xContext )
+ : m_xContext (std::move( xContext ))
+ , m_bAlreadyDisposed ( false )
+ , m_bActivateListener ( false )
+{
+}
+
+PopupMenuDispatcher::~PopupMenuDispatcher()
+{
+ // Warn programmer if he forgot to dispose this instance.
+ // We must release all our references ...
+ // and a dtor isn't the best place to do that!
+}
+
+OUString SAL_CALL PopupMenuDispatcher::getImplementationName()
+{
+ return "com.sun.star.comp.framework.PopupMenuControllerDispatcher";
+}
+
+sal_Bool SAL_CALL PopupMenuDispatcher::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL PopupMenuDispatcher::getSupportedServiceNames()
+{
+ return { SERVICENAME_PROTOCOLHANDLER };
+}
+
+void SAL_CALL PopupMenuDispatcher::initialize( const css::uno::Sequence< css::uno::Any >& lArguments )
+{
+ css::uno::Reference< css::frame::XFrame > xFrame;
+
+ SolarMutexGuard g;
+ for (int a=0; a<lArguments.getLength(); ++a)
+ {
+ if (a==0)
+ {
+ lArguments[a] >>= xFrame;
+ m_xWeakFrame = xFrame;
+
+ m_bActivateListener = true;
+ uno::Reference< css::frame::XFrameActionListener > xFrameActionListener(this);
+ xFrame->addFrameActionListener( xFrameActionListener );
+ }
+ }
+}
+
+css::uno::Reference< css::frame::XDispatch >
+SAL_CALL PopupMenuDispatcher::queryDispatch(
+ const css::util::URL& rURL ,
+ const OUString& sTarget ,
+ sal_Int32 nFlags )
+{
+ if ( !rURL.Complete.startsWith( "vnd.sun.star.popup:" ) )
+ return {};
+
+ // --- SAFE ---
+ SolarMutexClearableGuard aGuard;
+ impl_RetrievePopupControllerQuery();
+ if ( !m_xUriRefFactory.is() )
+ m_xUriRefFactory = css::uri::UriReferenceFactory::create( m_xContext );
+
+ css::uno::Reference< css::container::XNameAccess > xPopupCtrlQuery( m_xPopupCtrlQuery );
+ aGuard.clear();
+ // --- SAFE ---
+
+ if ( !xPopupCtrlQuery.is() )
+ return {};
+
+ css::uno::Reference< css::frame::XDispatch > xDispatch;
+
+ try
+ {
+ // Just use the main part of the URL for popup menu controllers
+ sal_Int32 nSchemePart( 0 );
+ OUString aBaseURL( "vnd.sun.star.popup:" );
+ OUString aURL( rURL.Complete );
+
+ nSchemePart = aURL.indexOf( ':' );
+ if (( nSchemePart > 0 ) &&
+ ( aURL.getLength() > ( nSchemePart+1 )))
+ {
+ sal_Int32 nQueryPart = aURL.indexOf( '?', nSchemePart );
+ if ( nQueryPart > 0 )
+ aBaseURL += aURL.subView( nSchemePart+1, nQueryPart-(nSchemePart+1) );
+ else if ( nQueryPart == -1 )
+ aBaseURL += aURL.subView( nSchemePart+1 );
+ }
+
+ css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider;
+
+ // Find popup menu controller using the base URL
+ xPopupCtrlQuery->getByName( aBaseURL ) >>= xDispatchProvider;
+
+ // Ask popup menu dispatch provider for dispatch object
+ if ( xDispatchProvider.is() )
+ xDispatch = xDispatchProvider->queryDispatch( rURL, sTarget, nFlags );
+ }
+ catch ( const RuntimeException& )
+ {
+ throw;
+ }
+ catch ( const Exception& )
+ {
+ }
+ return xDispatch;
+}
+
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL
+PopupMenuDispatcher::queryDispatches(
+ const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor )
+{
+ sal_Int32 nCount = lDescriptor.getLength();
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount );
+ auto lDispatcherRange = asNonConstRange(lDispatcher);
+ for( sal_Int32 i=0; i<nCount; ++i )
+ {
+ lDispatcherRange[i] = queryDispatch(
+ lDescriptor[i].FeatureURL,
+ lDescriptor[i].FrameName,
+ lDescriptor[i].SearchFlags);
+ }
+ return lDispatcher;
+}
+
+void SAL_CALL PopupMenuDispatcher::dispatch( const URL& /*aURL*/, const Sequence< PropertyValue >& /*seqProperties*/ )
+{
+}
+
+void SAL_CALL PopupMenuDispatcher::addStatusListener( const uno::Reference< XStatusListener >& /*xControl*/,
+ const URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL PopupMenuDispatcher::removeStatusListener( const uno::Reference< XStatusListener >& /*xControl*/,
+ const URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL PopupMenuDispatcher::frameAction( const FrameActionEvent& aEvent )
+{
+ SolarMutexGuard g;
+ if (( aEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING ) ||
+ ( aEvent.Action == css::frame::FrameAction_COMPONENT_ATTACHED ))
+ {
+ // Reset query reference to requery it again next time
+ m_xPopupCtrlQuery.clear();
+ }
+}
+
+void SAL_CALL PopupMenuDispatcher::disposing( const EventObject& )
+{
+ SolarMutexGuard g;
+ // Safe impossible cases
+ SAL_WARN_IF( m_bAlreadyDisposed, "fwk", "MenuDispatcher::disposing(): Object already disposed .. don't call it again!" );
+
+ if( m_bAlreadyDisposed )
+ return;
+
+ m_bAlreadyDisposed = true;
+
+ if ( m_bActivateListener )
+ {
+ uno::Reference< XFrame > xFrame( m_xWeakFrame.get(), UNO_QUERY );
+ if ( xFrame.is() )
+ {
+ xFrame->removeFrameActionListener( uno::Reference< XFrameActionListener >(this) );
+ m_bActivateListener = false;
+ }
+ }
+
+ // Forget our factory.
+ m_xContext.clear();
+}
+
+void PopupMenuDispatcher::impl_RetrievePopupControllerQuery()
+{
+ if ( m_xPopupCtrlQuery.is() )
+ return;
+
+ css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
+ css::uno::Reference< css::frame::XFrame > xFrame( m_xWeakFrame );
+
+ if ( !xFrame.is() )
+ return;
+
+ css::uno::Reference< css::beans::XPropertySet > xPropSet( xFrame, css::uno::UNO_QUERY );
+ if ( !xPropSet.is() )
+ return;
+
+ try
+ {
+ xPropSet->getPropertyValue( FRAME_PROPNAME_ASCII_LAYOUTMANAGER ) >>= xLayoutManager;
+
+ if ( xLayoutManager.is() )
+ {
+ css::uno::Reference< css::ui::XUIElement > xMenuBar = xLayoutManager->getElement( "private:resource/menubar/menubar" );
+
+ m_xPopupCtrlQuery.set( xMenuBar, css::uno::UNO_QUERY );
+ }
+ }
+ catch ( const css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( const css::uno::Exception& )
+ {
+ }
+}
+
+} // namespace framework
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_PopupMenuDispatcher_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::PopupMenuDispatcher(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/servicehandler.cxx b/framework/source/dispatch/servicehandler.cxx
new file mode 100644
index 000000000..14dea0603
--- /dev/null
+++ b/framework/source/dispatch/servicehandler.cxx
@@ -0,0 +1,260 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/servicehandler.hxx>
+#include <services.h>
+
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/task/XJobExecutor.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <utility>
+
+namespace framework{
+
+constexpr OUStringLiteral PROTOCOL_VALUE = u"service:";
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL ServiceHandler::getImplementationName()
+{
+ return "com.sun.star.comp.framework.ServiceHandler";
+}
+
+sal_Bool SAL_CALL ServiceHandler::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL ServiceHandler::getSupportedServiceNames()
+{
+ return { SERVICENAME_PROTOCOLHANDLER };
+}
+
+
+/**
+ @short standard ctor
+ @descr This initializes a new instance of this class with needed information for work.
+
+ @param xFactory
+ reference to uno servicemanager for creation of new services
+*/
+ServiceHandler::ServiceHandler( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : m_xContext (std::move( xContext ))
+{
+}
+
+/**
+ @short standard dtor
+*/
+ServiceHandler::~ServiceHandler()
+{
+}
+
+/**
+ @short decide if this dispatch implementation can be used for requested URL or not
+ @descr A protocol handler is registered for a URL pattern inside configuration and will
+ be asked by the generic dispatch mechanism inside framework, if he can handle this
+ special URL which match his registration. He can agree by returning of a valid dispatch
+ instance or disagree by returning <NULL/>.
+ We don't create new dispatch instances here really - we return THIS as result to handle it
+ at the same implementation.
+*/
+css::uno::Reference< css::frame::XDispatch > SAL_CALL ServiceHandler::queryDispatch( const css::util::URL& aURL ,
+ const OUString& /*sTarget*/ ,
+ sal_Int32 /*nFlags*/ )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+ if (aURL.Complete.startsWith(PROTOCOL_VALUE))
+ xDispatcher = this;
+ return xDispatcher;
+}
+
+/**
+ @short do the same like dispatch() but for multiple requests at the same time
+*/
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL ServiceHandler::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor )
+{
+ sal_Int32 nCount = lDescriptor.getLength();
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount );
+ auto lDispatcherRange = asNonConstRange(lDispatcher);
+ for( sal_Int32 i=0; i<nCount; ++i )
+ {
+ lDispatcherRange[i] = queryDispatch(
+ lDescriptor[i].FeatureURL,
+ lDescriptor[i].FrameName,
+ lDescriptor[i].SearchFlags);
+ }
+ return lDispatcher;
+}
+
+/**
+ @short dispatch URL with arguments
+ @descr We use threadsafe internal method to do so. It returns a state value - but we ignore it.
+ Because we don't support status listener notifications here.
+
+ @param aURL
+ uno URL which should be executed
+ @param lArguments
+ list of optional arguments for this request
+*/
+void SAL_CALL ServiceHandler::dispatch( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/ )
+{
+ // dispatch() is an [oneway] call ... and may our user release his reference to us immediately.
+ // So we should hold us self alive till this call ends.
+ css::uno::Reference< css::frame::XNotifyingDispatch > xSelfHold(this);
+ implts_dispatch(aURL);
+ // No notification for status listener!
+}
+
+/**
+ @short dispatch with guaranteed notifications about success
+ @descr We use threadsafe internal method to do so. Return state of this function will be used
+ for notification if an optional listener is given.
+
+ @param aURL
+ uno URL which should be executed
+ @param lArguments
+ list of optional arguments for this request
+ @param xListener
+ optional listener for state events
+*/
+void SAL_CALL ServiceHandler::dispatchWithNotification( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ // This class was designed to die by reference. And if user release his reference to us immediately after calling this method
+ // we can run into some problems. So we hold us self alive till this method ends.
+ // Another reason: We can use this reference as source of sending event at the end too.
+ css::uno::Reference< css::frame::XNotifyingDispatch > xThis(this);
+
+ css::uno::Reference< css::uno::XInterface > xService = implts_dispatch(aURL);
+ if (xListener.is())
+ {
+ css::frame::DispatchResultEvent aEvent;
+ if (xService.is())
+ aEvent.State = css::frame::DispatchResultState::SUCCESS;
+ else
+ aEvent.State = css::frame::DispatchResultState::FAILURE;
+ aEvent.Result <<= xService; // may NULL for state=FAILED!
+ aEvent.Source = xThis;
+
+ xListener->dispatchFinished( aEvent );
+ }
+}
+
+/**
+ @short threadsafe helper for dispatch calls
+ @descr We support two interfaces for the same process - dispatch URLs. That the reason for this internal
+ function. It implements the real dispatch operation and returns a state value which inform caller
+ about success. He can notify listener then by using this return value.
+
+ @param aURL
+ uno URL which should be executed
+
+ @return <NULL/> if requested service couldn't be created successfully;
+ a valid reference otherwise. This return value can be used to indicate,
+ if dispatch was successful.
+*/
+css::uno::Reference< css::uno::XInterface > ServiceHandler::implts_dispatch( const css::util::URL& aURL )
+{
+ // extract service name and may optional given parameters from given URL
+ // and use it to create and start the component
+ OUString sServiceAndArguments = aURL.Complete.copy(PROTOCOL_VALUE.getLength());
+ OUString sServiceName;
+ OUString sArguments;
+
+ sal_Int32 nArgStart = sServiceAndArguments.indexOf('?');
+ if (nArgStart!=-1)
+ {
+ sServiceName = sServiceAndArguments.copy(0,nArgStart);
+ ++nArgStart; // ignore '?'!
+ sArguments = sServiceAndArguments.copy(nArgStart);
+ }
+ else
+ {
+ sServiceName = sServiceAndArguments;
+ }
+
+ if (sServiceName.isEmpty())
+ return css::uno::Reference< css::uno::XInterface >();
+
+ // If a service doesn't support an optional job executor interface - he can't get
+ // any given parameters!
+ // Because we can't know if we must call createInstanceWithArguments() or XJobExecutor::trigger() ...
+
+ css::uno::Reference< css::uno::XInterface > xService;
+ try
+ {
+ // => a) a service starts running inside his own ctor and we create it only
+ xService = m_xContext->getServiceManager()->createInstanceWithContext(sServiceName, m_xContext);
+ // or b) he implements the right interface and starts there (may with optional parameters)
+ css::uno::Reference< css::task::XJobExecutor > xExecutable(xService, css::uno::UNO_QUERY);
+ if (xExecutable.is())
+ xExecutable->trigger(sArguments);
+ }
+ // ignore all errors - inclusive runtime errors!
+ // E.g. a script based service (written in Python) could not be executed
+ // because it contains syntax errors, which was detected at runtime...
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("fwk.dispatch", "ignored");
+ xService.clear();
+ }
+
+ return xService;
+}
+
+/**
+ @short add/remove listener for state events
+ @descr We use an internal container to hold such registered listener. This container lives if we live.
+ And if call pass registration as non breakable transaction - we can accept the request without
+ any explicit lock. Because we share our mutex with this container.
+
+ @param xListener
+ reference to a valid listener for state events
+ @param aURL
+ URL about listener will be informed, if something occurred
+*/
+void SAL_CALL ServiceHandler::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ ,
+ const css::util::URL& /*aURL*/ )
+{
+ // not supported yet
+}
+
+void SAL_CALL ServiceHandler::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ ,
+ const css::util::URL& /*aURL*/ )
+{
+ // not supported yet
+}
+
+} // namespace framework
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_ServiceHandler_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::ServiceHandler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/startmoduledispatcher.cxx b/framework/source/dispatch/startmoduledispatcher.cxx
new file mode 100644
index 000000000..39ac70ee1
--- /dev/null
+++ b/framework/source/dispatch/startmoduledispatcher.cxx
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/startmoduledispatcher.hxx>
+
+#include <framework/framelistanalyzer.hxx>
+#include <targets.h>
+#include "isstartmoduledispatch.hxx"
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/StartModule.hpp>
+
+#include <unotools/moduleoptions.hxx>
+#include <utility>
+
+namespace framework{
+
+#ifdef fpf
+ #error "Who uses \"fpf\" as define. It will overwrite my namespace alias ..."
+#endif
+
+StartModuleDispatcher::StartModuleDispatcher(css::uno::Reference< css::uno::XComponentContext > xContext)
+ : m_xContext (std::move(xContext ))
+{
+}
+
+StartModuleDispatcher::~StartModuleDispatcher()
+{
+}
+
+void SAL_CALL StartModuleDispatcher::dispatch(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
+{
+ dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >());
+}
+
+void SAL_CALL StartModuleDispatcher::dispatchWithNotification(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ ::sal_Int16 nResult = css::frame::DispatchResultState::DONTKNOW;
+ if (isStartModuleDispatch(aURL))
+ {
+ nResult = css::frame::DispatchResultState::FAILURE;
+ if (implts_isBackingModePossible ())
+ {
+ implts_establishBackingMode ();
+ nResult = css::frame::DispatchResultState::SUCCESS;
+ }
+ }
+
+ implts_notifyResultListener(xListener, nResult, css::uno::Any());
+}
+
+css::uno::Sequence< ::sal_Int16 > SAL_CALL StartModuleDispatcher::getSupportedCommandGroups()
+{
+ return css::uno::Sequence< ::sal_Int16 >();
+}
+
+css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL StartModuleDispatcher::getConfigurableDispatchInformation(::sal_Int16 /*nCommandGroup*/)
+{
+ return css::uno::Sequence< css::frame::DispatchInformation >();
+}
+
+void SAL_CALL StartModuleDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
+ const css::util::URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL StartModuleDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
+ const css::util::URL& /*aURL*/ )
+{
+}
+
+bool StartModuleDispatcher::implts_isBackingModePossible()
+{
+ if ( ! SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE))
+ return false;
+
+ css::uno::Reference< css::frame::XFramesSupplier > xDesktop =
+ css::frame::Desktop::create( m_xContext );
+
+ FrameListAnalyzer aCheck(
+ xDesktop,
+ css::uno::Reference< css::frame::XFrame >(),
+ FrameAnalyzerFlags::Help | FrameAnalyzerFlags::BackingComponent);
+
+ bool bIsPossible = false;
+
+ if ( ! aCheck.m_xBackingComponent.is()
+ && aCheck.m_lOtherVisibleFrames.empty() )
+ {
+ bIsPossible = true;
+ }
+
+ return bIsPossible;
+}
+
+void StartModuleDispatcher::implts_establishBackingMode()
+{
+ css::uno::Reference< css::frame::XDesktop2> xDesktop = css::frame::Desktop::create( m_xContext );
+ css::uno::Reference< css::frame::XFrame > xFrame = xDesktop->findFrame(SPECIALTARGET_BLANK, 0);
+ css::uno::Reference< css::awt::XWindow > xContainerWindow = xFrame->getContainerWindow();
+
+ css::uno::Reference< css::frame::XController > xStartModule = css::frame::StartModule::createWithParentWindow(m_xContext, xContainerWindow);
+ css::uno::Reference< css::awt::XWindow > xComponentWindow(xStartModule, css::uno::UNO_QUERY);
+ xFrame->setComponent(xComponentWindow, xStartModule);
+ xStartModule->attachFrame(xFrame);
+ xContainerWindow->setVisible(true);
+}
+
+void StartModuleDispatcher::implts_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener,
+ ::sal_Int16 nState ,
+ const css::uno::Any& aResult )
+{
+ if ( ! xListener.is())
+ return;
+
+ css::frame::DispatchResultEvent aEvent(
+ css::uno::Reference< css::uno::XInterface >(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY),
+ nState,
+ aResult);
+
+ xListener->dispatchFinished(aEvent);
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/systemexec.cxx b/framework/source/dispatch/systemexec.cxx
new file mode 100644
index 000000000..c7b377842
--- /dev/null
+++ b/framework/source/dispatch/systemexec.cxx
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/systemexec.hxx>
+#include <services.h>
+
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <com/sun/star/util/PathSubstitution.hpp>
+#include <com/sun/star/util/XStringSubstitution.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <utility>
+
+namespace framework{
+
+constexpr OUStringLiteral PROTOCOL_VALUE = u"systemexecute:";
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL SystemExec::getImplementationName()
+{
+ return "com.sun.star.comp.framework.SystemExecute";
+}
+
+sal_Bool SAL_CALL SystemExec::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL SystemExec::getSupportedServiceNames()
+{
+ return { SERVICENAME_PROTOCOLHANDLER };
+}
+
+SystemExec::SystemExec( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : m_xContext (std::move( xContext ))
+{
+}
+
+SystemExec::~SystemExec()
+{
+}
+
+css::uno::Reference< css::frame::XDispatch > SAL_CALL SystemExec::queryDispatch( const css::util::URL& aURL ,
+ const OUString&,
+ sal_Int32 )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+ if (aURL.Complete.startsWith(PROTOCOL_VALUE))
+ xDispatcher = this;
+ return xDispatcher;
+}
+
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL SystemExec::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor )
+{
+ sal_Int32 nCount = lDescriptor.getLength();
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount );
+ auto lDispatcherRange = asNonConstRange(lDispatcher);
+ for( sal_Int32 i=0; i<nCount; ++i )
+ {
+ lDispatcherRange[i] = queryDispatch(
+ lDescriptor[i].FeatureURL,
+ lDescriptor[i].FrameName,
+ lDescriptor[i].SearchFlags);
+ }
+ return lDispatcher;
+}
+
+void SAL_CALL SystemExec::dispatch( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
+{
+ dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >());
+}
+
+void SAL_CALL SystemExec::dispatchWithNotification( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >&,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ // convert "systemexec:file:///c:/temp/test.html" => "file:///c:/temp/test.html"
+ sal_Int32 c = aURL.Complete.getLength()-PROTOCOL_VALUE.getLength();
+ if (c<1) // we don't check for valid URLs here! The system will show an error message ...
+ {
+ impl_notifyResultListener(xListener, css::frame::DispatchResultState::FAILURE);
+ return;
+ }
+ OUString sSystemURLWithVariables = aURL.Complete.copy(PROTOCOL_VALUE.getLength(), c);
+
+ // TODO check security settings ...
+
+ try
+ {
+ css::uno::Reference< css::util::XStringSubstitution > xPathSubst( css::util::PathSubstitution::create(m_xContext) );
+
+ OUString sSystemURL = xPathSubst->substituteVariables(sSystemURLWithVariables, true); // sal_True force an exception if unknown variables exists !
+
+ css::uno::Reference< css::system::XSystemShellExecute > xShell = css::system::SystemShellExecute::create( m_xContext );
+
+ xShell->execute(sSystemURL, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY);
+ impl_notifyResultListener(xListener, css::frame::DispatchResultState::SUCCESS);
+ }
+ catch(const css::uno::Exception&)
+ {
+ impl_notifyResultListener(xListener, css::frame::DispatchResultState::FAILURE);
+ }
+}
+
+void SAL_CALL SystemExec::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >&,
+ const css::util::URL& )
+{
+ // not supported yet
+}
+
+void SAL_CALL SystemExec::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >&,
+ const css::util::URL& )
+{
+ // not supported yet
+}
+
+void SystemExec::impl_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener,
+ const sal_Int16 nState )
+{
+ if (xListener.is())
+ {
+ css::frame::DispatchResultEvent aEvent;
+ aEvent.State = nState;
+ xListener->dispatchFinished(aEvent);
+ }
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_SystemExecute_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::SystemExec(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/windowcommanddispatch.cxx b/framework/source/dispatch/windowcommanddispatch.cxx
new file mode 100644
index 000000000..abad29dc7
--- /dev/null
+++ b/framework/source/dispatch/windowcommanddispatch.cxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/windowcommanddispatch.hxx>
+#include <targets.h>
+
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+#include <utility>
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/commandevent.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+namespace framework{
+
+WindowCommandDispatch::WindowCommandDispatch(css::uno::Reference< css::uno::XComponentContext > xContext ,
+ const css::uno::Reference< css::frame::XFrame >& xFrame)
+ : m_xContext (std::move(xContext ))
+ , m_xFrame (xFrame )
+ , m_xWindow (xFrame->getContainerWindow())
+{
+ impl_startListening();
+}
+
+WindowCommandDispatch::~WindowCommandDispatch()
+{
+ impl_stopListening();
+ m_xContext.clear();
+}
+
+void WindowCommandDispatch::impl_startListening()
+{
+ std::unique_lock aReadLock(m_mutex);
+ css::uno::Reference< css::awt::XWindow > xWindow( m_xWindow.get(), css::uno::UNO_QUERY );
+ aReadLock.unlock();
+
+ if ( ! xWindow.is())
+ return;
+
+ {
+ SolarMutexGuard aSolarLock;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ if ( ! pWindow)
+ return;
+
+ pWindow->AddEventListener( LINK(this, WindowCommandDispatch, impl_notifyCommand) );
+ }
+}
+
+void WindowCommandDispatch::impl_stopListening()
+{
+ std::unique_lock aReadLock(m_mutex);
+ css::uno::Reference< css::awt::XWindow > xWindow( m_xWindow.get(), css::uno::UNO_QUERY );
+ aReadLock.unlock();
+
+ if (!xWindow.is())
+ return;
+
+ {
+ SolarMutexGuard aSolarLock;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ if (!pWindow)
+ return;
+
+ pWindow->RemoveEventListener( LINK(this, WindowCommandDispatch, impl_notifyCommand) );
+
+ m_xWindow.clear();
+ }
+}
+
+IMPL_LINK(WindowCommandDispatch, impl_notifyCommand, VclWindowEvent&, rEvent, void)
+{
+ if (rEvent.GetId() == VclEventId::ObjectDying)
+ {
+ impl_stopListening();
+ return;
+ }
+ if (rEvent.GetId() != VclEventId::WindowCommand)
+ return;
+
+ const CommandEvent* pCommand = static_cast<CommandEvent*>(rEvent.GetData());
+ if (pCommand->GetCommand() != CommandEventId::ShowDialog)
+ return;
+
+ const CommandDialogData* pData = pCommand->GetDialogData();
+ if ( ! pData)
+ return;
+
+ const ShowDialogId nCommand = pData->GetDialogId();
+ OUString sCommand;
+
+ switch (nCommand)
+ {
+ case ShowDialogId::Preferences :
+ sCommand = ".uno:OptionsTreeDialog";
+ break;
+
+ case ShowDialogId::About :
+ sCommand = ".uno:About";
+ break;
+
+ default :
+ return;
+ }
+
+ // ignore all errors here. It's clicking a menu entry only ...
+ // The user will try it again, in case nothing happens .-)
+ try
+ {
+ // SYNCHRONIZED ->
+ std::unique_lock aReadLock(m_mutex);
+ css::uno::Reference< css::frame::XDispatchProvider > xProvider(m_xFrame.get(), css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext;
+ aReadLock.unlock();
+ // <- SYNCHRONIZED
+
+ // check provider ... we know it's weak reference only
+ if ( ! xProvider.is())
+ return;
+
+ css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(xContext));
+ css::util::URL aCommand;
+ aCommand.Complete = sCommand;
+ xParser->parseStrict(aCommand);
+
+ css::uno::Reference< css::frame::XDispatch > xDispatch = xProvider->queryDispatch(aCommand, SPECIALTARGET_SELF, 0);
+ if (xDispatch.is())
+ xDispatch->dispatch(aCommand, css::uno::Sequence< css::beans::PropertyValue >());
+ }
+ catch(const css::uno::Exception&)
+ {}
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/actiontriggercontainer.cxx b/framework/source/fwe/classes/actiontriggercontainer.cxx
new file mode 100644
index 000000000..360223e6e
--- /dev/null
+++ b/framework/source/fwe/classes/actiontriggercontainer.cxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <classes/actiontriggercontainer.hxx>
+#include <classes/actiontriggerpropertyset.hxx>
+#include <classes/actiontriggerseparatorpropertyset.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+
+namespace framework
+{
+
+ActionTriggerContainer::ActionTriggerContainer()
+{
+}
+
+ActionTriggerContainer::~ActionTriggerContainer()
+{
+}
+
+// XInterface
+Any SAL_CALL ActionTriggerContainer::queryInterface( const Type& aType )
+{
+ Any a = ::cppu::queryInterface(
+ aType ,
+ static_cast< XMultiServiceFactory* >(this),
+ static_cast< XServiceInfo* >(this),
+ static_cast< XTypeProvider* >(this));
+
+ if( a.hasValue() )
+ {
+ return a;
+ }
+
+ return PropertySetContainer::queryInterface( aType );
+}
+
+void ActionTriggerContainer::acquire() noexcept
+{
+ PropertySetContainer::acquire();
+}
+
+void ActionTriggerContainer::release() noexcept
+{
+ PropertySetContainer::release();
+}
+
+// XMultiServiceFactory
+Reference< XInterface > SAL_CALL ActionTriggerContainer::createInstance( const OUString& aServiceSpecifier )
+{
+ if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGER )
+ return static_cast<OWeakObject *>( new ActionTriggerPropertySet());
+ else if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGERCONTAINER )
+ return static_cast<OWeakObject *>( new ActionTriggerContainer());
+ else if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGERSEPARATOR )
+ return static_cast<OWeakObject *>( new ActionTriggerSeparatorPropertySet());
+ else
+ throw css::uno::RuntimeException("Unknown service specifier!", static_cast<OWeakObject *>(this) );
+}
+
+Reference< XInterface > SAL_CALL ActionTriggerContainer::createInstanceWithArguments( const OUString& ServiceSpecifier, const Sequence< Any >& /*Arguments*/ )
+{
+ return createInstance( ServiceSpecifier );
+}
+
+Sequence< OUString > SAL_CALL ActionTriggerContainer::getAvailableServiceNames()
+{
+ Sequence< OUString > aSeq{ SERVICENAME_ACTIONTRIGGER,
+ SERVICENAME_ACTIONTRIGGERCONTAINER,
+ SERVICENAME_ACTIONTRIGGERSEPARATOR };
+
+ return aSeq;
+}
+
+// XServiceInfo
+OUString SAL_CALL ActionTriggerContainer::getImplementationName()
+{
+ return IMPLEMENTATIONNAME_ACTIONTRIGGERCONTAINER;
+}
+
+sal_Bool SAL_CALL ActionTriggerContainer::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL ActionTriggerContainer::getSupportedServiceNames()
+{
+ Sequence< OUString > seqServiceNames { SERVICENAME_ACTIONTRIGGERCONTAINER };
+ return seqServiceNames;
+}
+
+// XTypeProvider
+Sequence< Type > SAL_CALL ActionTriggerContainer::getTypes()
+{
+ // Create a static typecollection ...
+ static ::cppu::OTypeCollection ourTypeCollection(
+ cppu::UnoType<XMultiServiceFactory>::get(),
+ cppu::UnoType<XIndexContainer>::get(),
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XTypeProvider>::get());
+
+ return ourTypeCollection.getTypes();
+}
+
+Sequence< sal_Int8 > SAL_CALL ActionTriggerContainer::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/actiontriggerpropertyset.cxx b/framework/source/fwe/classes/actiontriggerpropertyset.cxx
new file mode 100644
index 000000000..4592174df
--- /dev/null
+++ b/framework/source/fwe/classes/actiontriggerpropertyset.cxx
@@ -0,0 +1,371 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <classes/actiontriggerpropertyset.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <cppuhelper/proptypehlp.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::awt;
+
+//struct SAL_DLLPUBLIC_IMPORT ::cppu::OBroadcastHelperVar< OMultiTypeInterfaceContainerHelper, OMultiTypeInterfaceContainerHelper::keyType >;
+
+namespace {
+
+// Handles for properties
+// (PLEASE SORT THIS FIELD, IF YOU ADD NEW PROPERTIES!)
+// We use an enum to define these handles, to use all numbers from 0 to nn and
+// if you add someone, you don't must control this!
+// But don't forget to change values of follow defines, if you do something with this enum!
+enum EPROPERTIES
+{
+ HANDLE_COMMANDURL,
+ HANDLE_HELPURL,
+ HANDLE_IMAGE,
+ HANDLE_SUBCONTAINER,
+ HANDLE_TEXT,
+ PROPERTYCOUNT
+};
+
+}
+
+namespace framework
+{
+
+ActionTriggerPropertySet::ActionTriggerPropertySet()
+ : OBroadcastHelper ( m_aMutex )
+ , OPropertySetHelper ( *static_cast< OBroadcastHelper * >(this) )
+{
+}
+
+ActionTriggerPropertySet::~ActionTriggerPropertySet()
+{
+}
+
+// XInterface
+Any SAL_CALL ActionTriggerPropertySet::queryInterface( const Type& aType )
+{
+ Any a = ::cppu::queryInterface(
+ aType,
+ static_cast< XServiceInfo* >(this),
+ static_cast< XTypeProvider* >(this));
+
+ if( a.hasValue() )
+ return a;
+ else
+ {
+ a = OPropertySetHelper::queryInterface( aType );
+
+ if( a.hasValue() )
+ return a;
+ }
+
+ return OWeakObject::queryInterface( aType );
+}
+
+void SAL_CALL ActionTriggerPropertySet::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL ActionTriggerPropertySet::release() noexcept
+{
+ OWeakObject::release();
+}
+
+// XServiceInfo
+OUString SAL_CALL ActionTriggerPropertySet::getImplementationName()
+{
+ return IMPLEMENTATIONNAME_ACTIONTRIGGER;
+}
+
+sal_Bool SAL_CALL ActionTriggerPropertySet::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL ActionTriggerPropertySet::getSupportedServiceNames()
+{
+ Sequence<OUString> seqServiceNames { SERVICENAME_ACTIONTRIGGER };
+ return seqServiceNames;
+}
+
+// XTypeProvider
+Sequence< Type > SAL_CALL ActionTriggerPropertySet::getTypes()
+{
+ // Create a static typecollection ...
+ static ::cppu::OTypeCollection ourTypeCollection(
+ cppu::UnoType<XPropertySet>::get(),
+ cppu::UnoType<XFastPropertySet>::get(),
+ cppu::UnoType<XMultiPropertySet>::get(),
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XTypeProvider>::get());
+
+
+ return ourTypeCollection.getTypes();
+}
+
+Sequence< sal_Int8 > SAL_CALL ActionTriggerPropertySet::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+sal_Bool SAL_CALL ActionTriggerPropertySet::convertFastPropertyValue(
+ Any& aConvertedValue,
+ Any& aOldValue,
+ sal_Int32 nHandle,
+ const Any& aValue )
+{
+ // Check, if value of property will changed in method "setFastPropertyValue_NoBroadcast()".
+ // Return sal_True, if changed - else return sal_False.
+ // Attention: Method "impl_tryToChangeProperty()" can throw the IllegalArgumentException !!!
+ // Initialize return value with sal_False !!!
+ // (Handle can be invalid)
+ bool bReturn = false;
+
+ switch( nHandle )
+ {
+ case HANDLE_COMMANDURL:
+ bReturn = impl_tryToChangeProperty( m_aCommandURL, aValue, aOldValue, aConvertedValue );
+ break;
+
+ case HANDLE_HELPURL:
+ bReturn = impl_tryToChangeProperty( m_aHelpURL, aValue, aOldValue, aConvertedValue );
+ break;
+
+ case HANDLE_IMAGE:
+ bReturn = impl_tryToChangeProperty( m_xBitmap, aValue, aOldValue, aConvertedValue );
+ break;
+
+ case HANDLE_SUBCONTAINER:
+ bReturn = impl_tryToChangeProperty( m_xActionTriggerContainer, aValue, aOldValue, aConvertedValue );
+ break;
+
+ case HANDLE_TEXT:
+ bReturn = impl_tryToChangeProperty( m_aText, aValue, aOldValue, aConvertedValue );
+ break;
+ }
+
+ // Return state of operation.
+ return bReturn;
+}
+
+void SAL_CALL ActionTriggerPropertySet::setFastPropertyValue_NoBroadcast(
+ sal_Int32 nHandle, const Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ // Search for right handle ... and try to set property value.
+ switch( nHandle )
+ {
+ case HANDLE_COMMANDURL:
+ aValue >>= m_aCommandURL;
+ break;
+
+ case HANDLE_HELPURL:
+ aValue >>= m_aHelpURL;
+ break;
+
+ case HANDLE_IMAGE:
+ aValue >>= m_xBitmap;
+ break;
+
+ case HANDLE_SUBCONTAINER:
+ aValue >>= m_xActionTriggerContainer;
+ break;
+
+ case HANDLE_TEXT:
+ aValue >>= m_aText;
+ break;
+ }
+}
+
+void SAL_CALL ActionTriggerPropertySet::getFastPropertyValue(
+ Any& aValue, sal_Int32 nHandle ) const
+{
+ SolarMutexGuard aGuard;
+
+ // Search for right handle ... and try to get property value.
+ switch( nHandle )
+ {
+ case HANDLE_COMMANDURL:
+ aValue <<= m_aCommandURL;
+ break;
+
+ case HANDLE_HELPURL:
+ aValue <<= m_aHelpURL;
+ break;
+
+ case HANDLE_IMAGE:
+ aValue <<= m_xBitmap;
+ break;
+
+ case HANDLE_SUBCONTAINER:
+ aValue <<= m_xActionTriggerContainer;
+ break;
+
+ case HANDLE_TEXT:
+ aValue <<= m_aText;
+ break;
+ }
+}
+
+::cppu::IPropertyArrayHelper& SAL_CALL ActionTriggerPropertySet::getInfoHelper()
+{
+ // Define static member to give structure of properties to baseclass "OPropertySetHelper".
+ // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable.
+ // "true" say: Table is sorted by name.
+ static OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true );
+
+ return ourInfoHelper;
+}
+
+Reference< XPropertySetInfo > SAL_CALL ActionTriggerPropertySet::getPropertySetInfo()
+{
+ // Create structure of propertysetinfo for baseclass "OPropertySetHelper".
+ // (Use method "getInfoHelper()".)
+ static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+
+ return xInfo;
+}
+
+Sequence< Property > ActionTriggerPropertySet::impl_getStaticPropertyDescriptor()
+{
+ return
+ {
+ Property( "CommandURL" , HANDLE_COMMANDURL , cppu::UnoType<OUString>::get(), PropertyAttribute::TRANSIENT ),
+ Property( "HelpURL" , HANDLE_HELPURL , cppu::UnoType<OUString>::get(), PropertyAttribute::TRANSIENT ),
+ Property( "Image" , HANDLE_IMAGE , cppu::UnoType<XBitmap>::get(), PropertyAttribute::TRANSIENT ),
+ Property( "SubContainer" , HANDLE_SUBCONTAINER , cppu::UnoType<OUString>::get(), PropertyAttribute::TRANSIENT ),
+ Property( "Text" , HANDLE_TEXT , cppu::UnoType<XInterface>::get(), PropertyAttribute::TRANSIENT )
+ };
+}
+
+bool ActionTriggerPropertySet::impl_tryToChangeProperty(
+ const OUString& sCurrentValue ,
+ const Any& aNewValue ,
+ Any& aOldValue ,
+ Any& aConvertedValue )
+{
+ // Set default return value if method failed.
+ bool bReturn = false;
+ // Get new value from any.
+ // IllegalArgumentException() can be thrown!
+ OUString sValue;
+ convertPropertyValue( sValue, aNewValue );
+
+ // If value change ...
+ if( sValue != sCurrentValue )
+ {
+ // ... set information of change.
+ aOldValue <<= sCurrentValue;
+ aConvertedValue <<= sValue;
+ // Return OK - "value will be change ..."
+ bReturn = true;
+ }
+ else
+ {
+ // ... clear information of return parameter!
+ aOldValue.clear ();
+ aConvertedValue.clear ();
+ // Return NOTHING - "value will not be change ..."
+ bReturn = false;
+ }
+
+ return bReturn;
+}
+
+bool ActionTriggerPropertySet::impl_tryToChangeProperty(
+ const Reference< XBitmap >& aCurrentValue ,
+ const Any& aNewValue ,
+ Any& aOldValue ,
+ Any& aConvertedValue )
+{
+ // Set default return value if method failed.
+ bool bReturn = false;
+ // Get new value from any.
+ // IllegalArgumentException() can be thrown!
+ Reference< XBitmap > aValue;
+ convertPropertyValue( aValue, aNewValue );
+
+ // If value change ...
+ if( aValue != aCurrentValue )
+ {
+ // ... set information of change.
+ aOldValue <<= aCurrentValue;
+ aConvertedValue <<= aValue;
+ // Return OK - "value will be change ..."
+ bReturn = true;
+ }
+ else
+ {
+ // ... clear information of return parameter!
+ aOldValue.clear ();
+ aConvertedValue.clear ();
+ // Return NOTHING - "value will not be change ..."
+ bReturn = false;
+ }
+
+ return bReturn;
+}
+
+bool ActionTriggerPropertySet::impl_tryToChangeProperty(
+ const Reference< XInterface >& aCurrentValue ,
+ const Any& aNewValue ,
+ Any& aOldValue ,
+ Any& aConvertedValue )
+{
+ // Set default return value if method failed.
+ bool bReturn = false;
+ // Get new value from any.
+ // IllegalArgumentException() can be thrown!
+ Reference< XInterface > aValue;
+ convertPropertyValue( aValue, aNewValue );
+
+ // If value change ...
+ if( aValue != aCurrentValue )
+ {
+ // ... set information of change.
+ aOldValue <<= aCurrentValue;
+ aConvertedValue <<= aValue;
+ // Return OK - "value will be change ..."
+ bReturn = true;
+ }
+ else
+ {
+ // ... clear information of return parameter!
+ aOldValue.clear ();
+ aConvertedValue.clear ();
+ // Return NOTHING - "value will not be change ..."
+ bReturn = false;
+ }
+
+ return bReturn;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/actiontriggerseparatorpropertyset.cxx b/framework/source/fwe/classes/actiontriggerseparatorpropertyset.cxx
new file mode 100644
index 000000000..104d765f5
--- /dev/null
+++ b/framework/source/fwe/classes/actiontriggerseparatorpropertyset.cxx
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <classes/actiontriggerseparatorpropertyset.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <cppuhelper/proptypehlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::awt;
+
+namespace {
+
+// Handles for properties
+// (PLEASE SORT THIS FIELD, IF YOU ADD NEW PROPERTIES!)
+// We use an enum to define these handles, to use all numbers from 0 to nn and
+// if you add someone, you don't must control this!
+// But don't forget to change values of follow defines, if you do something with this enum!
+enum EPROPERTIES
+{
+ HANDLE_TYPE,
+ PROPERTYCOUNT
+};
+
+}
+
+namespace framework
+{
+
+ActionTriggerSeparatorPropertySet::ActionTriggerSeparatorPropertySet()
+ : OBroadcastHelper ( m_aMutex )
+ , OPropertySetHelper ( *static_cast< OBroadcastHelper * >(this) )
+ , m_nSeparatorType( 0 )
+{
+}
+
+ActionTriggerSeparatorPropertySet::~ActionTriggerSeparatorPropertySet()
+{
+}
+
+// XInterface
+Any SAL_CALL ActionTriggerSeparatorPropertySet::queryInterface( const Type& aType )
+{
+ Any a = ::cppu::queryInterface(
+ aType,
+ static_cast< XServiceInfo* >(this),
+ static_cast< XTypeProvider* >(this));
+
+ if( a.hasValue() )
+ return a;
+ else
+ {
+ a = OPropertySetHelper::queryInterface( aType );
+
+ if( a.hasValue() )
+ return a;
+ }
+
+ return OWeakObject::queryInterface( aType );
+}
+
+void ActionTriggerSeparatorPropertySet::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void ActionTriggerSeparatorPropertySet::release() noexcept
+{
+ OWeakObject::release();
+}
+
+// XServiceInfo
+OUString SAL_CALL ActionTriggerSeparatorPropertySet::getImplementationName()
+{
+ return IMPLEMENTATIONNAME_ACTIONTRIGGERSEPARATOR;
+}
+
+sal_Bool SAL_CALL ActionTriggerSeparatorPropertySet::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL ActionTriggerSeparatorPropertySet::getSupportedServiceNames()
+{
+ Sequence<OUString> seqServiceNames { SERVICENAME_ACTIONTRIGGERSEPARATOR };
+ return seqServiceNames;
+}
+
+// XTypeProvider
+Sequence< Type > SAL_CALL ActionTriggerSeparatorPropertySet::getTypes()
+{
+ // Create a static typecollection ...
+ static ::cppu::OTypeCollection ourTypeCollection(
+ cppu::UnoType<XPropertySet>::get(),
+ cppu::UnoType<XFastPropertySet>::get(),
+ cppu::UnoType<XMultiPropertySet>::get(),
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XTypeProvider>::get());
+
+ return ourTypeCollection.getTypes();
+}
+
+Sequence< sal_Int8 > SAL_CALL ActionTriggerSeparatorPropertySet::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+sal_Bool SAL_CALL ActionTriggerSeparatorPropertySet::convertFastPropertyValue(
+ Any& aConvertedValue,
+ Any& aOldValue,
+ sal_Int32 nHandle,
+ const Any& aValue )
+{
+ // Check, if value of property will changed in method "setFastPropertyValue_NoBroadcast()".
+ // Return sal_True, if changed - else return sal_False.
+ // Attention: Method "impl_tryToChangeProperty()" can throw the IllegalArgumentException !!!
+ // Initialize return value with sal_False !!!
+ // (Handle can be invalid)
+ bool bReturn = false;
+
+ switch( nHandle )
+ {
+ case HANDLE_TYPE:
+ bReturn = impl_tryToChangeProperty( m_nSeparatorType, aValue, aOldValue, aConvertedValue );
+ break;
+ }
+
+ // Return state of operation.
+ return bReturn;
+}
+
+void SAL_CALL ActionTriggerSeparatorPropertySet::setFastPropertyValue_NoBroadcast(
+ sal_Int32 nHandle, const Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ // Search for right handle ... and try to set property value.
+ switch( nHandle )
+ {
+ case HANDLE_TYPE:
+ aValue >>= m_nSeparatorType;
+ break;
+ }
+}
+
+void SAL_CALL ActionTriggerSeparatorPropertySet::getFastPropertyValue(
+ Any& aValue, sal_Int32 nHandle ) const
+{
+ SolarMutexGuard aGuard;
+
+ // Search for right handle ... and try to get property value.
+ switch( nHandle )
+ {
+ case HANDLE_TYPE:
+ aValue <<= m_nSeparatorType;
+ break;
+ }
+}
+
+::cppu::IPropertyArrayHelper& SAL_CALL ActionTriggerSeparatorPropertySet::getInfoHelper()
+{
+ // Define static member to give structure of properties to baseclass "OPropertySetHelper".
+ // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable.
+ // "true" indicates: Table is sorted by name.
+ static OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true );
+
+ return ourInfoHelper;
+}
+
+Reference< XPropertySetInfo > SAL_CALL ActionTriggerSeparatorPropertySet::getPropertySetInfo()
+{
+ // Create structure of propertysetinfo for baseclass "OPropertySetHelper".
+ // (Use method "getInfoHelper()".)
+ static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+
+ return xInfo;
+}
+
+Sequence< Property > ActionTriggerSeparatorPropertySet::impl_getStaticPropertyDescriptor()
+{
+ return
+ {
+ Property( "SeparatorType", HANDLE_TYPE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::TRANSIENT )
+ };
+}
+
+bool ActionTriggerSeparatorPropertySet::impl_tryToChangeProperty(
+ sal_Int16 aCurrentValue ,
+ const Any& aNewValue ,
+ Any& aOldValue ,
+ Any& aConvertedValue )
+{
+ // Set default return value if method failed.
+ bool bReturn = false;
+ // Get new value from any.
+ // IllegalArgumentException() can be thrown!
+ sal_Int16 aValue = 0;
+ convertPropertyValue( aValue, aNewValue );
+
+ // If value change ...
+ if( aValue != aCurrentValue )
+ {
+ // ... set information of change.
+ aOldValue <<= aCurrentValue;
+ aConvertedValue <<= aValue;
+ // Return OK - "value will be change ..."
+ bReturn = true;
+ }
+ else
+ {
+ // ... clear information of return parameter!
+ aOldValue.clear ();
+ aConvertedValue.clear ();
+ // Return NOTHING - "value will not be change ..."
+ bReturn = false;
+ }
+
+ return bReturn;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/addonmenu.cxx b/framework/source/fwe/classes/addonmenu.cxx
new file mode 100644
index 000000000..423a157f8
--- /dev/null
+++ b/framework/source/fwe/classes/addonmenu.cxx
@@ -0,0 +1,299 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <addonmenu.hxx>
+#include <framework/addonsoptions.hxx>
+#include <menuconfiguration.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/menu.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::beans;
+
+namespace framework
+{
+
+bool AddonMenuManager::HasAddonMenuElements()
+{
+ return AddonsOptions().HasAddonsMenu();
+}
+
+// Create the Add-Ons menu
+VclPtr<PopupMenu> AddonMenuManager::CreateAddonMenu( const Reference< XFrame >& rFrame )
+{
+ AddonsOptions aOptions;
+ VclPtr<PopupMenu> pAddonMenu;
+
+ const Sequence< Sequence< PropertyValue > >& rAddonMenuEntries = aOptions.GetAddonsMenu();
+ if ( rAddonMenuEntries.hasElements() )
+ {
+ sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
+ pAddonMenu = VclPtr<PopupMenu>::Create();
+ OUString aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier( rFrame );
+ AddonMenuManager::BuildMenu( pAddonMenu, MENU_APPEND, nUniqueMenuId, rAddonMenuEntries, rFrame, aModuleIdentifier );
+
+ // Don't return an empty Add-On menu
+ if ( pAddonMenu->GetItemCount() == 0 )
+ {
+ pAddonMenu.disposeAndClear();
+ }
+ }
+
+ return pAddonMenu;
+}
+
+// Returns the next insert position from nPos.
+sal_uInt16 AddonMenuManager::GetNextPos( sal_uInt16 nPos )
+{
+ return ( nPos == MENU_APPEND ) ? MENU_APPEND : ( nPos+1 );
+}
+
+static sal_uInt16 FindMenuId( Menu const * pMenu, std::u16string_view aCommand )
+{
+ sal_uInt16 nPos = 0;
+ OUString aCmd;
+ for ( nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
+ {
+ sal_uInt16 nId = pMenu->GetItemId( nPos );
+ aCmd = pMenu->GetItemCommand( nId );
+ if ( aCmd == aCommand )
+ return nId;
+ }
+
+ return USHRT_MAX;
+}
+
+// Merge the Add-Ons help menu items into the given menu bar at a defined pos
+void AddonMenuManager::MergeAddonHelpMenu( const Reference< XFrame >& rFrame,
+ MenuBar const * pMergeMenuBar )
+{
+ if ( !pMergeMenuBar )
+ return;
+
+ PopupMenu* pHelpMenu(nullptr);
+ sal_uInt16 nId = FindMenuId(pMergeMenuBar, u".uno:HelpMenu");
+ if ( nId != USHRT_MAX )
+ pHelpMenu = pMergeMenuBar->GetPopupMenu( nId );
+
+ if ( !pHelpMenu )
+ return;
+
+ // Add-Ons help menu items should be inserted after the "registration" menu item
+ sal_uInt16 nItemCount = pHelpMenu->GetItemCount();
+ sal_uInt16 nInsSepAfterPos = MENU_APPEND;
+ sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
+ AddonsOptions aOptions;
+
+ // try to detect the about menu item with the command URL
+ nId = FindMenuId(pHelpMenu, u".uno:About");
+ sal_uInt16 nInsPos = pHelpMenu->GetItemPos( nId );
+
+ const Sequence< Sequence< PropertyValue > >& rAddonHelpMenuEntries = aOptions.GetAddonsHelpMenu();
+
+ if ( nInsPos < nItemCount && pHelpMenu->GetItemType( nInsPos ) != MenuItemType::SEPARATOR )
+ nInsSepAfterPos = nInsPos;
+
+ OUString aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier(rFrame);
+ AddonMenuManager::BuildMenu( pHelpMenu, nInsPos, nUniqueMenuId, rAddonHelpMenuEntries, rFrame, aModuleIdentifier );
+
+ if ( pHelpMenu->GetItemCount() > nItemCount )
+ {
+ if ( nInsSepAfterPos < MENU_APPEND )
+ {
+ nInsSepAfterPos += ( pHelpMenu->GetItemCount() - nItemCount );
+ if ( pHelpMenu->GetItemType( nInsSepAfterPos ) != MenuItemType::SEPARATOR )
+ pHelpMenu->InsertSeparator(OString(), nInsSepAfterPos);
+ }
+ pHelpMenu->InsertSeparator(OString(), nItemCount);
+ }
+}
+
+// Merge the addon popup menus into the given menu bar at the provided pos.
+void AddonMenuManager::MergeAddonPopupMenus( const Reference< XFrame >& rFrame,
+ sal_uInt16 nMergeAtPos,
+ MenuBar* pMergeMenuBar )
+{
+ if ( !pMergeMenuBar )
+ return;
+
+ AddonsOptions aAddonsOptions;
+ sal_uInt16 nInsertPos = nMergeAtPos;
+
+ OUString aTitle;
+ OUString aURL;
+ OUString aTarget;
+ OUString aContext;
+ Sequence< Sequence< PropertyValue > > aAddonSubMenu;
+ sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
+
+ OUString aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier(rFrame);
+
+ const Sequence< Sequence< PropertyValue > >& rAddonMenuEntries = aAddonsOptions.GetAddonsMenuBarPart();
+ for ( const Sequence< PropertyValue >& rEntry : rAddonMenuEntries )
+ {
+ AddonMenuManager::GetMenuEntry( rEntry,
+ aTitle,
+ aURL,
+ aTarget,
+ aContext,
+ aAddonSubMenu );
+ if ( !aTitle.isEmpty() &&
+ !aURL.isEmpty() &&
+ aAddonSubMenu.hasElements() &&
+ AddonMenuManager::IsCorrectContext( aModuleIdentifier, aContext ))
+ {
+ sal_uInt16 nId = nUniqueMenuId++;
+ VclPtrInstance<PopupMenu> pAddonPopupMenu;
+
+ AddonMenuManager::BuildMenu( pAddonPopupMenu, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, aModuleIdentifier );
+
+ if ( pAddonPopupMenu->GetItemCount() > 0 )
+ {
+ pMergeMenuBar->InsertItem( nId, aTitle, MenuItemBits::NONE, OString(), nInsertPos++ );
+ pMergeMenuBar->SetPopupMenu( nId, pAddonPopupMenu );
+
+ // Store the command URL into the VCL menu bar for later identification
+ pMergeMenuBar->SetItemCommand( nId, aURL );
+ }
+ else
+ pAddonPopupMenu.disposeAndClear();
+ }
+ }
+}
+
+// Insert the menu and sub menu entries into pCurrentMenu with the aAddonMenuDefinition provided
+void AddonMenuManager::BuildMenu( PopupMenu* pCurrentMenu,
+ sal_uInt16 nInsPos,
+ sal_uInt16& nUniqueMenuId,
+ const Sequence< Sequence< PropertyValue > >& aAddonMenuDefinition,
+ const Reference< XFrame >& rFrame,
+ const OUString& rModuleIdentifier )
+{
+ Sequence< Sequence< PropertyValue > > aAddonSubMenu;
+ bool bInsertSeparator = false;
+ sal_uInt32 i = 0;
+ sal_uInt32 nElements = 0;
+ sal_uInt32 nCount = aAddonMenuDefinition.getLength();
+
+ OUString aTitle;
+ OUString aURL;
+ OUString aTarget;
+ OUString aContext;
+
+ for ( i = 0; i < nCount; ++i )
+ {
+ GetMenuEntry( aAddonMenuDefinition[i], aTitle, aURL, aTarget, aContext, aAddonSubMenu );
+
+ if ( !IsCorrectContext( rModuleIdentifier, aContext ) || ( aTitle.isEmpty() && aURL.isEmpty() ))
+ continue;
+
+ if ( aURL == "private:separator" )
+ bInsertSeparator = true;
+ else
+ {
+ VclPtr<PopupMenu> pSubMenu;
+ if ( aAddonSubMenu.hasElements() )
+ {
+ pSubMenu = VclPtr<PopupMenu>::Create();
+ AddonMenuManager::BuildMenu( pSubMenu, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, rModuleIdentifier );
+
+ // Don't create a menu item for an empty sub menu
+ if ( pSubMenu->GetItemCount() == 0 )
+ {
+ pSubMenu.disposeAndClear();
+ continue;
+ }
+ }
+
+ if ( bInsertSeparator && nElements > 0 )
+ {
+ // Insert a separator only when we insert a new element afterwards and we
+ // have already one before us
+ nElements = 0;
+ bInsertSeparator = false;
+ pCurrentMenu->InsertSeparator(OString(), nInsPos);
+ nInsPos = AddonMenuManager::GetNextPos( nInsPos );
+ }
+
+ sal_uInt16 nId = nUniqueMenuId++;
+ pCurrentMenu->InsertItem(nId, aTitle, MenuItemBits::NONE, OString(), nInsPos);
+ nInsPos = AddonMenuManager::GetNextPos( nInsPos );
+
+ ++nElements;
+
+ void* nAttributePtr = MenuAttributes::CreateAttribute(aTarget, OUString());
+ pCurrentMenu->SetUserValue(nId, nAttributePtr, MenuAttributes::ReleaseAttribute);
+ pCurrentMenu->SetItemCommand( nId, aURL );
+
+ if ( pSubMenu )
+ pCurrentMenu->SetPopupMenu( nId, pSubMenu );
+ }
+ }
+}
+
+// Retrieve the menu entry property values from a sequence
+void AddonMenuManager::GetMenuEntry( const Sequence< PropertyValue >& rAddonMenuEntry,
+ OUString& rTitle,
+ OUString& rURL,
+ OUString& rTarget,
+ OUString& rContext,
+ Sequence< Sequence< PropertyValue > >& rAddonSubMenu )
+{
+ // Reset submenu parameter
+ rAddonSubMenu = Sequence< Sequence< PropertyValue > >();
+
+ for ( const PropertyValue& rEntry : rAddonMenuEntry )
+ {
+ OUString aMenuEntryPropName = rEntry.Name;
+ if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_URL )
+ rEntry.Value >>= rURL;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_TITLE )
+ rEntry.Value >>= rTitle;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_TARGET )
+ rEntry.Value >>= rTarget;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_SUBMENU )
+ rEntry.Value >>= rAddonSubMenu;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_CONTEXT )
+ rEntry.Value >>= rContext;
+ }
+}
+
+// Check if the context string matches the provided xModel context
+bool AddonMenuManager::IsCorrectContext( std::u16string_view rModuleIdentifier, std::u16string_view rContext )
+{
+ if ( rContext.empty() )
+ return true;
+
+ if ( !rModuleIdentifier.empty() )
+ {
+ return rContext.find( rModuleIdentifier ) != std::u16string_view::npos;
+ }
+
+ return false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/addonsoptions.cxx b/framework/source/fwe/classes/addonsoptions.cxx
new file mode 100644
index 000000000..b97a7eac6
--- /dev/null
+++ b/framework/source/fwe/classes/addonsoptions.cxx
@@ -0,0 +1,1958 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <framework/addonsoptions.hxx>
+#include <o3tl/safeint.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/configitem.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <tools/stream.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <comphelper/getexpandeduri.hxx>
+#include <comphelper/processfactory.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/svapp.hxx>
+
+#include <algorithm>
+#include <string_view>
+#include <unordered_map>
+#include <vector>
+
+// namespaces
+
+using namespace ::std;
+using namespace ::utl;
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star;
+
+constexpr OUStringLiteral ROOTNODE_ADDONMENU = u"Office.Addons";
+constexpr OUStringLiteral PATHDELIMITER = u"/";
+constexpr OUStringLiteral SEPARATOR_URL = u"private:separator";
+
+#define PROPERTYNAME_URL ADDONSMENUITEM_STRING_URL
+#define PROPERTYNAME_TITLE ADDONSMENUITEM_STRING_TITLE
+#define PROPERTYNAME_TARGET ADDONSMENUITEM_STRING_TARGET
+#define PROPERTYNAME_IMAGEIDENTIFIER ADDONSMENUITEM_STRING_IMAGEIDENTIFIER
+#define PROPERTYNAME_CONTEXT ADDONSMENUITEM_STRING_CONTEXT
+#define PROPERTYNAME_SUBMENU ADDONSMENUITEM_STRING_SUBMENU
+
+constexpr OUStringLiteral IMAGES_NODENAME = u"UserDefinedImages";
+
+// The following order is mandatory. Please add properties at the end!
+#define INDEX_URL 0
+#define INDEX_TITLE 1
+#define INDEX_IMAGEIDENTIFIER 2
+#define INDEX_TARGET 3
+#define INDEX_CONTEXT 4
+#define INDEX_SUBMENU 5
+#define INDEX_CONTROLTYPE 6
+#define INDEX_WIDTH 7
+#define INDEX_ALIGN 8
+#define INDEX_AUTOSIZE 9
+#define INDEX_OWNERDRAW 10
+#define INDEX_MANDATORY 11
+#define INDEX_STYLE 12
+#define PROPERTYCOUNT_INDEX 13
+
+// The following order is mandatory. Please add properties at the end!
+#define PROPERTYCOUNT_MENUITEM 6
+#define OFFSET_MENUITEM_URL 0
+#define OFFSET_MENUITEM_TITLE 1
+#define OFFSET_MENUITEM_IMAGEIDENTIFIER 2
+#define OFFSET_MENUITEM_TARGET 3
+#define OFFSET_MENUITEM_CONTEXT 4
+#define OFFSET_MENUITEM_SUBMENU 5
+
+// The following order is mandatory. Please add properties at the end!
+#define PROPERTYCOUNT_POPUPMENU 4
+#define OFFSET_POPUPMENU_TITLE 0
+#define OFFSET_POPUPMENU_CONTEXT 1
+#define OFFSET_POPUPMENU_SUBMENU 2
+#define OFFSET_POPUPMENU_URL 3 // Used for property set
+
+// The following order is mandatory. Please add properties at the end!
+#define PROPERTYCOUNT_TOOLBARITEM 7
+#define OFFSET_TOOLBARITEM_URL 0
+#define OFFSET_TOOLBARITEM_TITLE 1
+#define OFFSET_TOOLBARITEM_IMAGEIDENTIFIER 2
+#define OFFSET_TOOLBARITEM_TARGET 3
+#define OFFSET_TOOLBARITEM_CONTEXT 4
+#define OFFSET_TOOLBARITEM_CONTROLTYPE 5
+#define OFFSET_TOOLBARITEM_WIDTH 6
+
+// The following order is mandatory. Please add properties at the end!
+#define PROPERTYCOUNT_NOTEBOOKBARITEM 8
+#define OFFSET_NOTEBOOKBARITEM_URL 0
+#define OFFSET_NOTEBOOKBARITEM_TITLE 1
+#define OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER 2
+#define OFFSET_NOTEBOOKBARITEM_TARGET 3
+#define OFFSET_NOTEBOOKBARITEM_CONTEXT 4
+#define OFFSET_NOTEBOOKBARITEM_CONTROLTYPE 5
+#define OFFSET_NOTEBOOKBARITEM_WIDTH 6
+#define OFFSET_NOTEBOOKBARITEM_STYLE 7
+
+// The following order is mandatory. Please add properties at the end!
+#define PROPERTYCOUNT_STATUSBARITEM 8
+#define OFFSET_STATUSBARITEM_URL 0
+#define OFFSET_STATUSBARITEM_TITLE 1
+#define OFFSET_STATUSBARITEM_CONTEXT 2
+#define OFFSET_STATUSBARITEM_ALIGN 3
+#define OFFSET_STATUSBARITEM_AUTOSIZE 4
+#define OFFSET_STATUSBARITEM_OWNERDRAW 5
+#define OFFSET_STATUSBARITEM_MANDATORY 6
+#define OFFSET_STATUSBARITEM_WIDTH 7
+
+// The following order is mandatory. Please add properties at the end!
+#define PROPERTYCOUNT_IMAGES 8
+#define PROPERTYCOUNT_EMBEDDED_IMAGES 2
+#define OFFSET_IMAGES_SMALL 0
+#define OFFSET_IMAGES_BIG 1
+#define OFFSET_IMAGES_SMALLHC 2
+#define OFFSET_IMAGES_BIGHC 3
+#define OFFSET_IMAGES_SMALL_URL 4
+#define OFFSET_IMAGES_BIG_URL 5
+#define OFFSET_IMAGES_SMALLHC_URL 6
+#define OFFSET_IMAGES_BIGHC_URL 7
+
+#define PROPERTYCOUNT_MERGE_MENUBAR 6
+#define OFFSET_MERGEMENU_MERGEPOINT 0
+#define OFFSET_MERGEMENU_MERGECOMMAND 1
+#define OFFSET_MERGEMENU_MERGECOMMANDPARAMETER 2
+#define OFFSET_MERGEMENU_MERGEFALLBACK 3
+#define OFFSET_MERGEMENU_MERGECONTEXT 4
+#define OFFSET_MERGEMENU_MENUITEMS 5
+
+#define PROPERTYCOUNT_MERGE_TOOLBAR 7
+#define OFFSET_MERGETOOLBAR_TOOLBAR 0
+#define OFFSET_MERGETOOLBAR_MERGEPOINT 1
+#define OFFSET_MERGETOOLBAR_MERGECOMMAND 2
+#define OFFSET_MERGETOOLBAR_MERGECOMMANDPARAMETER 3
+#define OFFSET_MERGETOOLBAR_MERGEFALLBACK 4
+#define OFFSET_MERGETOOLBAR_MERGECONTEXT 5
+#define OFFSET_MERGETOOLBAR_TOOLBARITEMS 6
+
+#define PROPERTYCOUNT_MERGE_NOTEBOOKBAR 7
+#define OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBAR 0
+#define OFFSET_MERGENOTEBOOKBAR_MERGEPOINT 1
+#define OFFSET_MERGENOTEBOOKBAR_MERGECOMMAND 2
+#define OFFSET_MERGENOTEBOOKBAR_MERGECOMMANDPARAMETER 3
+#define OFFSET_MERGENOTEBOOKBAR_MERGEFALLBACK 4
+#define OFFSET_MERGENOTEBOOKBAR_MERGECONTEXT 5
+#define OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBARITEMS 6
+
+#define PROPERTYCOUNT_MERGE_STATUSBAR 6
+#define OFFSET_MERGESTATUSBAR_MERGEPOINT 0
+#define OFFSET_MERGESTATUSBAR_MERGECOMMAND 1
+#define OFFSET_MERGESTATUSBAR_MERGECOMMANDPARAMETER 2
+#define OFFSET_MERGESTATUSBAR_MERGEFALLBACK 3
+#define OFFSET_MERGESTATUSBAR_MERGECONTEXT 4
+#define OFFSET_MERGESTATUSBAR_STATUSBARITEMS 5
+
+// private declarations!
+
+/*-****************************************************************************************************************
+ @descr struct to hold information about one menu entry.
+****************************************************************************************************************-*/
+
+namespace framework
+{
+
+class AddonsOptions_Impl : public ConfigItem
+{
+
+ // public methods
+
+ public:
+
+ // constructor / destructor
+
+ AddonsOptions_Impl();
+ virtual ~AddonsOptions_Impl() override;
+
+ // overridden methods of baseclass
+
+ /*-****************************************************************************************************
+ @short called for notify of configmanager
+ @descr This method is called from the ConfigManager before application ends or from the
+ PropertyChangeListener if the sub tree broadcasts changes. You must update your
+ internal values.
+
+ @seealso baseclass ConfigItem
+
+ @param "lPropertyNames" is the list of properties which should be updated.
+ *//*-*****************************************************************************************************/
+
+ virtual void Notify( const Sequence< OUString >& lPropertyNames ) override;
+
+ // public interface
+
+ /*-****************************************************************************************************
+ @short base implementation of public interface for "SvtDynamicMenuOptions"!
+ @descr These class is used as static member of "SvtDynamicMenuOptions" ...
+ => The code exist only for one time and isn't duplicated for every instance!
+ *//*-*****************************************************************************************************/
+
+ bool HasAddonsMenu () const;
+ sal_Int32 GetAddonsToolBarCount() const;
+ sal_Int32 GetAddonsNotebookBarCount() const;
+ const Sequence< Sequence< PropertyValue > >& GetAddonsMenu () const { return m_aCachedMenuProperties;}
+ const Sequence< Sequence< PropertyValue > >& GetAddonsMenuBarPart () const { return m_aCachedMenuBarPartProperties;}
+ const Sequence< Sequence< PropertyValue > >& GetAddonsToolBarPart ( sal_uInt32 nIndex ) const;
+ const Sequence< Sequence< PropertyValue > >& GetAddonsNotebookBarPart ( sal_uInt32 nIndex ) const;
+ OUString GetAddonsToolbarResourceName( sal_uInt32 nIndex ) const;
+ OUString GetAddonsNotebookBarResourceName( sal_uInt32 nIndex ) const;
+ const Sequence< Sequence< PropertyValue > >& GetAddonsHelpMenu () const { return m_aCachedHelpMenuProperties;}
+ BitmapEx GetImageFromURL( const OUString& aURL, bool bBig, bool bNoScale );
+ const MergeMenuInstructionContainer& GetMergeMenuInstructions() const { return m_aCachedMergeMenuInsContainer;}
+ bool GetMergeToolbarInstructions( const OUString& rToolbarName, MergeToolbarInstructionContainer& rToolbarInstructions ) const;
+ bool GetMergeNotebookBarInstructions( const OUString& rNotebookBarName, MergeNotebookBarInstructionContainer& rNotebookBarInstructions ) const;
+ const MergeStatusbarInstructionContainer& GetMergeStatusbarInstructions() const { return m_aCachedStatusbarMergingInstructions;}
+ void ReadConfigurationData();
+
+ private:
+ enum ImageSize
+ {
+ IMGSIZE_SMALL = 0,
+ IMGSIZE_BIG
+ };
+
+ struct OneImageEntry
+ {
+ BitmapEx aScaled; ///< cached scaled image
+ BitmapEx aImage; ///< original un-scaled image
+ OUString aURL; ///< URL in case it is not loaded yet
+ };
+
+ struct ImageEntry
+ {
+ // if the image is set, it was embedded in some way,
+ // otherwise we use the associated URL to load on demand
+
+ // accessed in this order
+ OneImageEntry aSizeEntry[2];
+ ImageEntry() {}
+ void addImage(ImageSize eSize, const BitmapEx &rImage);
+ void addImage(ImageSize eSize, const OUString &rURL);
+ };
+
+ typedef std::unordered_map< OUString, ImageEntry > ImageManager;
+ typedef std::unordered_map< OUString, sal_uInt32 > StringToIndexMap;
+ typedef std::vector< Sequence< Sequence< PropertyValue > > > AddonToolBars;
+ typedef std::vector< Sequence< Sequence< PropertyValue > > > AddonNotebookBars;
+ typedef std::unordered_map< OUString, MergeToolbarInstructionContainer > ToolbarMergingInstructions;
+ typedef std::unordered_map< OUString, MergeNotebookBarInstructionContainer > NotebookBarMergingInstructions;
+
+ /*-****************************************************************************************************
+ @short return list of key names of our configuration management which represent our module tree
+ @descr These methods return the current list of key names! We need it to get needed values from our
+ configuration management!
+ @param "nCount" , returns count of menu entries for "new"
+ @return A list of configuration key names is returned.
+ *//*-*****************************************************************************************************/
+
+ void ReadAddonMenuSet( Sequence< Sequence< PropertyValue > >& aAddonMenuSeq );
+ void ReadOfficeMenuBarSet( Sequence< Sequence< PropertyValue > >& aAddonOfficeMenuBarSeq );
+ void ReadOfficeToolBarSet( AddonToolBars& rAddonOfficeToolBars, std::vector< OUString >& rAddonOfficeToolBarResNames );
+ bool ReadToolBarItemSet( const OUString& rToolBarItemSetNodeName, Sequence< Sequence< PropertyValue > >& aAddonOfficeToolBarSeq );
+ void ReadOfficeNotebookBarSet( AddonNotebookBars& rAddonOfficeNotebookBars, std::vector< OUString >& rAddonOfficeNotebookBarResNames );
+ bool ReadNotebookBarItemSet( const OUString& rNotebookBarItemSetNodeName, Sequence< Sequence< PropertyValue > >& aAddonOfficeNotebookBarSeq );
+
+ void ReadOfficeHelpSet( Sequence< Sequence< PropertyValue > >& aAddonOfficeHelpMenuSeq );
+ void ReadImages( ImageManager& aImageManager );
+ void ReadMenuMergeInstructions( MergeMenuInstructionContainer& rContainer );
+ void ReadToolbarMergeInstructions( ToolbarMergingInstructions& rToolbarMergeMap );
+ void ReadNotebookBarMergeInstructions( NotebookBarMergingInstructions& rNotebookBarMergeMap );
+ void ReadStatusbarMergeInstructions( MergeStatusbarInstructionContainer& rContainer );
+
+ void ReadMergeMenuData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeMenu );
+ void ReadMergeToolbarData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeToolbarItems );
+ void ReadMergeNotebookBarData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeNotebookBarItems );
+ void ReadMergeStatusbarData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeStatusbar );
+ bool ReadMenuItem( std::u16string_view aMenuItemNodeName, Sequence< PropertyValue >& aMenuItem, bool bIgnoreSubMenu = false );
+ bool ReadPopupMenu( std::u16string_view aPopupMenuNodeName, Sequence< PropertyValue >& aPopupMenu );
+ void AppendPopupMenu( Sequence< PropertyValue >& aTargetPopupMenu, const Sequence< PropertyValue >& rSourcePopupMenu );
+ bool ReadToolBarItem( std::u16string_view aToolBarItemNodeName, Sequence< PropertyValue >& aToolBarItem );
+ bool ReadNotebookBarItem( std::u16string_view aNotebookBarItemNodeName, Sequence< PropertyValue >& aNotebookBarItem );
+
+ bool ReadStatusBarItem( std::u16string_view aStatusbarItemNodeName, Sequence< PropertyValue >& aStatusbarItem );
+ std::unique_ptr<ImageEntry> ReadImageData( std::u16string_view aImagesNodeName );
+ void ReadAndAssociateImages( const OUString& aURL, const OUString& aImageId );
+ BitmapEx ReadImageFromURL( const OUString& aURL );
+ bool HasAssociatedImages( const OUString& aURL );
+ void SubstituteVariables( OUString& aURL );
+
+ void ReadSubMenuEntries( const Sequence< OUString >& aSubMenuNodeNames, Sequence< Sequence< PropertyValue > >& rSubMenu );
+ OUString GeneratePrefixURL();
+
+ Sequence< OUString > GetPropertyNamesMenuItem( std::u16string_view aPropertyRootNode )
+ const;
+ Sequence< OUString > GetPropertyNamesPopupMenu( std::u16string_view aPropertyRootNode )
+ const;
+ Sequence< OUString > GetPropertyNamesToolBarItem( std::u16string_view aPropertyRootNode )
+ const;
+ Sequence< OUString > GetPropertyNamesNotebookBarItem( std::u16string_view aPropertyRootNode ) const;
+
+ Sequence< OUString > GetPropertyNamesStatusbarItem( std::u16string_view aPropertyRootNode ) const;
+ Sequence< OUString > GetPropertyNamesImages( std::u16string_view aPropertyRootNode ) const;
+ bool CreateImageFromSequence( BitmapEx& rImage, Sequence< sal_Int8 >& rBitmapDataSeq ) const;
+
+ DECL_LINK(NotifyEvent, void*, void);
+
+ virtual void ImplCommit() override;
+
+ // private member
+
+ private:
+ sal_Int32 m_nRootAddonPopupMenuId;
+ OUString m_aPropNames[PROPERTYCOUNT_INDEX];
+ OUString m_aPropImagesNames[PROPERTYCOUNT_IMAGES];
+ OUString m_aPropMergeMenuNames[PROPERTYCOUNT_MERGE_MENUBAR];
+ OUString m_aPropMergeToolbarNames[PROPERTYCOUNT_MERGE_TOOLBAR];
+ OUString m_aPropMergeNotebookBarNames[PROPERTYCOUNT_MERGE_NOTEBOOKBAR];
+ OUString m_aPropMergeStatusbarNames[PROPERTYCOUNT_MERGE_STATUSBAR];
+ OUString m_aPathDelimiter;
+ OUString m_aRootAddonPopupMenuURLPrexfix;
+ Sequence< Sequence< PropertyValue > > m_aCachedMenuProperties;
+ Sequence< Sequence< PropertyValue > > m_aCachedMenuBarPartProperties;
+ AddonToolBars m_aCachedToolBarPartProperties;
+ AddonNotebookBars m_aCachedNotebookBarPartProperties;
+ std::vector< OUString > m_aCachedToolBarPartResourceNames;
+ std::vector< OUString > m_aCachedNotebookBarPartResourceNames;
+ Sequence< Sequence< PropertyValue > > m_aCachedHelpMenuProperties;
+ ImageManager m_aImageManager;
+ Sequence< Sequence< PropertyValue > > m_aEmptyAddonToolBar;
+ Sequence< Sequence< PropertyValue > > m_aEmptyAddonNotebookBar;
+ MergeMenuInstructionContainer m_aCachedMergeMenuInsContainer;
+ ToolbarMergingInstructions m_aCachedToolbarMergingInstructions;
+ NotebookBarMergingInstructions m_aCachedNotebookBarMergingInstructions;
+ MergeStatusbarInstructionContainer m_aCachedStatusbarMergingInstructions;
+};
+
+void AddonsOptions_Impl::ImageEntry::addImage(ImageSize eSize, const BitmapEx& rImage)
+{
+ aSizeEntry[static_cast<int>(eSize)].aImage = rImage;
+}
+
+void AddonsOptions_Impl::ImageEntry::addImage(ImageSize eSize, const OUString &rURL)
+{
+ aSizeEntry[static_cast<int>(eSize)].aURL = rURL;
+}
+
+// constructor
+
+AddonsOptions_Impl::AddonsOptions_Impl()
+ // Init baseclasses first
+ : ConfigItem( ROOTNODE_ADDONMENU ),
+ m_nRootAddonPopupMenuId( 0 ),
+ m_aPathDelimiter( PATHDELIMITER ),
+ m_aRootAddonPopupMenuURLPrexfix( ADDONSPOPUPMENU_URL_PREFIX_STR )
+{
+ // initialize array with fixed property names
+ m_aPropNames[ INDEX_URL ] = PROPERTYNAME_URL;
+ m_aPropNames[ INDEX_TITLE ] = PROPERTYNAME_TITLE;
+ m_aPropNames[ INDEX_TARGET ] = PROPERTYNAME_TARGET;
+ m_aPropNames[ INDEX_IMAGEIDENTIFIER ] = PROPERTYNAME_IMAGEIDENTIFIER;
+ m_aPropNames[ INDEX_CONTEXT ] = PROPERTYNAME_CONTEXT;
+ m_aPropNames[ INDEX_SUBMENU ] = PROPERTYNAME_SUBMENU; // Submenu set!
+ m_aPropNames[ INDEX_CONTROLTYPE ] = "ControlType";
+ m_aPropNames[ INDEX_WIDTH ] = "Width";
+ m_aPropNames[ INDEX_ALIGN ] = "Alignment";
+ m_aPropNames[ INDEX_AUTOSIZE ] = "AutoSize";
+ m_aPropNames[ INDEX_OWNERDRAW ] = "OwnerDraw";
+ m_aPropNames[ INDEX_MANDATORY ] = "Mandatory";
+ m_aPropNames[ INDEX_STYLE ] = "Style";
+
+ // initialize array with fixed images property names
+ m_aPropImagesNames[ OFFSET_IMAGES_SMALL ] = "ImageSmall";
+ m_aPropImagesNames[ OFFSET_IMAGES_BIG ] = "ImageBig";
+ m_aPropImagesNames[ OFFSET_IMAGES_SMALLHC ] = "ImageSmallHC";
+ m_aPropImagesNames[ OFFSET_IMAGES_BIGHC ] = "ImageBigHC";
+ m_aPropImagesNames[ OFFSET_IMAGES_SMALL_URL ] = "ImageSmallURL";
+ m_aPropImagesNames[ OFFSET_IMAGES_BIG_URL ] = "ImageBigURL";
+ m_aPropImagesNames[ OFFSET_IMAGES_SMALLHC_URL ] = "ImageSmallHCURL";
+ m_aPropImagesNames[ OFFSET_IMAGES_BIGHC_URL ] = "ImageBigHCURL";
+
+ // initialize array with fixed merge menu property names
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGEPOINT ] = "MergePoint";
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECOMMAND ] = "MergeCommand";
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECOMMANDPARAMETER ] = "MergeCommandParameter";
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGEFALLBACK ] = "MergeFallback";
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECONTEXT ] = "MergeContext";
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MENUITEMS ] = "MenuItems";
+
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_TOOLBAR ] = "MergeToolBar";
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGEPOINT ] = "MergePoint";
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECOMMAND ] = "MergeCommand";
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECOMMANDPARAMETER ] = "MergeCommandParameter";
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGEFALLBACK ] = "MergeFallback";
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECONTEXT ] = "MergeContext";
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_TOOLBARITEMS ] = "ToolBarItems";
+
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBAR ] = "MergeNotebookBar";
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGEPOINT ] = "MergePoint";
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGECOMMAND ] = "MergeCommand";
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGECOMMANDPARAMETER ] = "MergeCommandParameter";
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGEFALLBACK ] = "MergeFallback";
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGECONTEXT ] = "MergeContext";
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBARITEMS ] = "NotebookBarItems";
+
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGEPOINT ] = "MergePoint";
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGECOMMAND ] = "MergeCommand";
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGECOMMANDPARAMETER ] = "MergeCommandParameter";
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGEFALLBACK ] = "MergeFallback";
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGECONTEXT ] = "MergeContext";
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_STATUSBARITEMS ] = "StatusBarItems";
+
+ ReadConfigurationData();
+
+ // Enable notification mechanism of our baseclass.
+ // We need it to get information about changes outside these class on our used configuration keys!
+ Sequence<OUString> aNotifySeq { "AddonUI" };
+ EnableNotification( aNotifySeq );
+}
+
+// destructor
+
+AddonsOptions_Impl::~AddonsOptions_Impl()
+{
+ assert(!IsModified()); // should have been committed
+}
+
+void AddonsOptions_Impl::ReadConfigurationData()
+{
+ // reset members to be read again from configuration
+ m_aCachedMenuProperties = Sequence< Sequence< PropertyValue > >();
+ m_aCachedMenuBarPartProperties = Sequence< Sequence< PropertyValue > >();
+ m_aCachedToolBarPartProperties = AddonToolBars();
+ m_aCachedNotebookBarPartProperties = AddonNotebookBars();
+ m_aCachedHelpMenuProperties = Sequence< Sequence< PropertyValue > >();
+ m_aCachedToolBarPartResourceNames.clear();
+ m_aCachedNotebookBarPartResourceNames.clear();
+ m_aImageManager = ImageManager();
+
+ ReadAddonMenuSet( m_aCachedMenuProperties );
+ ReadOfficeMenuBarSet( m_aCachedMenuBarPartProperties );
+ ReadOfficeToolBarSet( m_aCachedToolBarPartProperties, m_aCachedToolBarPartResourceNames );
+ ReadOfficeNotebookBarSet( m_aCachedNotebookBarPartProperties, m_aCachedNotebookBarPartResourceNames );
+
+ ReadOfficeHelpSet( m_aCachedHelpMenuProperties );
+ ReadImages( m_aImageManager );
+
+ m_aCachedMergeMenuInsContainer.clear();
+ m_aCachedToolbarMergingInstructions.clear();
+ m_aCachedNotebookBarMergingInstructions.clear();
+ m_aCachedStatusbarMergingInstructions.clear();
+
+ ReadMenuMergeInstructions( m_aCachedMergeMenuInsContainer );
+ ReadToolbarMergeInstructions( m_aCachedToolbarMergingInstructions );
+ ReadNotebookBarMergeInstructions( m_aCachedNotebookBarMergingInstructions );
+ ReadStatusbarMergeInstructions( m_aCachedStatusbarMergingInstructions );
+}
+
+// public method
+
+void AddonsOptions_Impl::Notify( const Sequence< OUString >& /*lPropertyNames*/ )
+{
+ Application::PostUserEvent(LINK(this, AddonsOptions_Impl, NotifyEvent));
+}
+
+// public method
+
+void AddonsOptions_Impl::ImplCommit()
+{
+ SAL_WARN("fwk", "AddonsOptions_Impl::ImplCommit(): Not implemented yet!");
+}
+
+// public method
+
+bool AddonsOptions_Impl::HasAddonsMenu() const
+{
+ return m_aCachedMenuProperties.hasElements();
+}
+
+// public method
+
+sal_Int32 AddonsOptions_Impl::GetAddonsToolBarCount() const
+{
+ return m_aCachedToolBarPartProperties.size();
+}
+
+// public method
+
+sal_Int32 AddonsOptions_Impl::GetAddonsNotebookBarCount() const
+{
+ return m_aCachedNotebookBarPartProperties.size();
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions_Impl::GetAddonsToolBarPart( sal_uInt32 nIndex ) const
+{
+ if ( /*nIndex >= 0 &&*/ nIndex < m_aCachedToolBarPartProperties.size() )
+ return m_aCachedToolBarPartProperties[nIndex];
+ else
+ return m_aEmptyAddonToolBar;
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions_Impl::GetAddonsNotebookBarPart( sal_uInt32 nIndex ) const
+{
+ if ( /*nIndex >= 0 &&*/ nIndex < m_aCachedNotebookBarPartProperties.size() )
+ return m_aCachedNotebookBarPartProperties[nIndex];
+ else
+ return m_aEmptyAddonNotebookBar;
+}
+
+// public method
+
+OUString AddonsOptions_Impl::GetAddonsToolbarResourceName( sal_uInt32 nIndex ) const
+{
+ if ( nIndex < m_aCachedToolBarPartResourceNames.size() )
+ return m_aCachedToolBarPartResourceNames[nIndex];
+ else
+ return OUString();
+}
+
+// public method
+
+OUString AddonsOptions_Impl::GetAddonsNotebookBarResourceName( sal_uInt32 nIndex ) const
+{
+ if ( nIndex < m_aCachedNotebookBarPartResourceNames.size() )
+ return m_aCachedNotebookBarPartResourceNames[nIndex];
+ else
+ return OUString();
+}
+
+// public method
+
+bool AddonsOptions_Impl::GetMergeToolbarInstructions(
+ const OUString& rToolbarName,
+ MergeToolbarInstructionContainer& rToolbarInstructions ) const
+{
+ ToolbarMergingInstructions::const_iterator pIter = m_aCachedToolbarMergingInstructions.find( rToolbarName );
+ if ( pIter != m_aCachedToolbarMergingInstructions.end() )
+ {
+ rToolbarInstructions = pIter->second;
+ return true;
+ }
+ else
+ return false;
+}
+
+// public method
+
+bool AddonsOptions_Impl::GetMergeNotebookBarInstructions(
+ const OUString& rNotebookBarName,
+ MergeNotebookBarInstructionContainer& rNotebookBarInstructions ) const
+{
+ NotebookBarMergingInstructions::const_iterator pIter = m_aCachedNotebookBarMergingInstructions.find( rNotebookBarName );
+ if ( pIter != m_aCachedNotebookBarMergingInstructions.end() )
+ {
+ rNotebookBarInstructions = pIter->second;
+ return true;
+ }
+ else
+ return false;
+}
+
+// public method
+
+static BitmapEx ScaleImage( const BitmapEx &rImage, bool bBig )
+{
+ Size aSize = ToolBox::GetDefaultImageSize(bBig ? ToolBoxButtonSize::Large : ToolBoxButtonSize::Small);
+ BitmapEx aScaleBmp(rImage);
+ SAL_INFO("fwk", "Addons: expensive scale image from "
+ << aScaleBmp.GetSizePixel() << " to " << aSize);
+ aScaleBmp.Scale(aSize, BmpScaleFlag::BestQuality);
+ return aScaleBmp;
+}
+
+BitmapEx AddonsOptions_Impl::GetImageFromURL( const OUString& aURL, bool bBig, bool bNoScale )
+{
+ BitmapEx aImage;
+
+ SAL_INFO("fwk", "Expensive: Addons GetImageFromURL " << aURL <<
+ " big " << (bBig?"big":"little") <<
+ " scale " << (bNoScale ? "noscale" : "scale"));
+
+ ImageManager::iterator pIter = m_aImageManager.find(aURL);
+ if ( pIter != m_aImageManager.end() )
+ {
+ ImageSize eSize = bBig ? IMGSIZE_BIG : IMGSIZE_SMALL;
+ int nIdx = static_cast<int>(eSize);
+ int nOtherIdx = nIdx ? 0 : 1;
+
+ OneImageEntry& rSizeEntry = pIter->second.aSizeEntry[nIdx];
+ OneImageEntry& rOtherEntry = pIter->second.aSizeEntry[nOtherIdx];
+ // actually read the image ...
+ if (rSizeEntry.aImage.IsEmpty())
+ rSizeEntry.aImage = ReadImageFromURL(rSizeEntry.aURL);
+
+ if (rSizeEntry.aImage.IsEmpty())
+ { // try the other size and scale it
+ aImage = ScaleImage(ReadImageFromURL(rOtherEntry.aURL), bBig);
+ rSizeEntry.aImage = aImage;
+ if (rSizeEntry.aImage.IsEmpty())
+ SAL_WARN("fwk", "failed to load addons image " << aURL);
+ }
+
+ // FIXME: bNoScale is not terribly meaningful or useful
+
+ if (aImage.IsEmpty() && bNoScale)
+ aImage = rSizeEntry.aImage;
+
+ if (aImage.IsEmpty() && !rSizeEntry.aScaled.IsEmpty())
+ aImage = rSizeEntry.aScaled;
+
+ else // scale to the correct size for the theme / toolbox
+ {
+ aImage = rSizeEntry.aImage;
+ if (aImage.IsEmpty()) // use and scale the other if one size is missing
+ aImage = rOtherEntry.aImage;
+
+ aImage = ScaleImage(aImage, bBig);
+ rSizeEntry.aScaled = aImage; // cache for next time
+ }
+ }
+
+ return aImage;
+}
+
+void AddonsOptions_Impl::ReadAddonMenuSet( Sequence< Sequence< PropertyValue > >& rAddonMenuSeq )
+{
+ // Read the AddonMenu set and fill property sequences
+ OUString aAddonMenuNodeName( "AddonUI/AddonMenu" );
+ Sequence< OUString > aAddonMenuNodeSeq = GetNodeNames( aAddonMenuNodeName );
+ OUString aAddonMenuItemNode( aAddonMenuNodeName + m_aPathDelimiter );
+
+ sal_uInt32 nCount = aAddonMenuNodeSeq.getLength();
+ sal_uInt32 nIndex = 0;
+ Sequence< PropertyValue > aMenuItem( PROPERTYCOUNT_MENUITEM );
+ auto pMenuItem = aMenuItem.getArray();
+ // Init the property value sequence
+ pMenuItem[ OFFSET_MENUITEM_URL ].Name = m_aPropNames[ INDEX_URL ];
+ pMenuItem[ OFFSET_MENUITEM_TITLE ].Name = m_aPropNames[ INDEX_TITLE ];
+ pMenuItem[ OFFSET_MENUITEM_TARGET ].Name = m_aPropNames[ INDEX_TARGET ];
+ pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Name = m_aPropNames[ INDEX_IMAGEIDENTIFIER];
+ pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT ];
+ pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Name = m_aPropNames[ INDEX_SUBMENU ]; // Submenu set!
+
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aRootMenuItemNode( aAddonMenuItemNode + aAddonMenuNodeSeq[n] );
+
+ // Read the MenuItem
+ if ( ReadMenuItem( aRootMenuItemNode, aMenuItem ) )
+ {
+ // Successfully read a menu item, append to our list
+ sal_uInt32 nMenuItemCount = rAddonMenuSeq.getLength() + 1;
+ rAddonMenuSeq.realloc( nMenuItemCount );
+ rAddonMenuSeq.getArray()[nIndex++] = aMenuItem;
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadOfficeHelpSet( Sequence< Sequence< PropertyValue > >& rAddonOfficeHelpMenuSeq )
+{
+ // Read the AddonMenu set and fill property sequences
+ OUString aAddonHelpMenuNodeName( "AddonUI/OfficeHelp" );
+ Sequence< OUString > aAddonHelpMenuNodeSeq = GetNodeNames( aAddonHelpMenuNodeName );
+ OUString aAddonHelpMenuItemNode( aAddonHelpMenuNodeName + m_aPathDelimiter );
+
+ sal_uInt32 nCount = aAddonHelpMenuNodeSeq.getLength();
+ sal_uInt32 nIndex = 0;
+ Sequence< PropertyValue > aMenuItem( PROPERTYCOUNT_MENUITEM );
+ auto pMenuItem = aMenuItem.getArray();
+ // Init the property value sequence
+ pMenuItem[ OFFSET_MENUITEM_URL ].Name = m_aPropNames[ INDEX_URL ];
+ pMenuItem[ OFFSET_MENUITEM_TITLE ].Name = m_aPropNames[ INDEX_TITLE ];
+ pMenuItem[ OFFSET_MENUITEM_TARGET ].Name = m_aPropNames[ INDEX_TARGET ];
+ pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Name = m_aPropNames[ INDEX_IMAGEIDENTIFIER];
+ pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT ];
+ pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Name = m_aPropNames[ INDEX_SUBMENU ]; // Submenu set!
+
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aRootMenuItemNode( aAddonHelpMenuItemNode + aAddonHelpMenuNodeSeq[n] );
+
+ // Read the MenuItem
+ if ( ReadMenuItem( aRootMenuItemNode, aMenuItem, true ) )
+ {
+ // Successfully read a menu item, append to our list
+ sal_uInt32 nMenuItemCount = rAddonOfficeHelpMenuSeq.getLength() + 1;
+ rAddonOfficeHelpMenuSeq.realloc( nMenuItemCount );
+ rAddonOfficeHelpMenuSeq.getArray()[nIndex++] = aMenuItem;
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadOfficeMenuBarSet( Sequence< Sequence< PropertyValue > >& rAddonOfficeMenuBarSeq )
+{
+ // Read the OfficeMenuBar set and fill property sequences
+ OUString aAddonMenuBarNodeName( "AddonUI/OfficeMenuBar" );
+ Sequence< OUString > aAddonMenuBarNodeSeq = GetNodeNames( aAddonMenuBarNodeName );
+ OUString aAddonMenuBarNode( aAddonMenuBarNodeName + m_aPathDelimiter );
+
+ sal_uInt32 nCount = aAddonMenuBarNodeSeq.getLength();
+ sal_uInt32 nIndex = 0;
+ Sequence< PropertyValue > aPopupMenu( PROPERTYCOUNT_POPUPMENU );
+ auto pPopupMenu = aPopupMenu.getArray();
+ // Init the property value sequence
+ pPopupMenu[ OFFSET_POPUPMENU_TITLE ].Name = m_aPropNames[ INDEX_TITLE ];
+ pPopupMenu[ OFFSET_POPUPMENU_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT];
+ pPopupMenu[ OFFSET_POPUPMENU_SUBMENU ].Name = m_aPropNames[ INDEX_SUBMENU];
+ pPopupMenu[ OFFSET_POPUPMENU_URL ].Name = m_aPropNames[ INDEX_URL ];
+
+ StringToIndexMap aTitleToIndexMap;
+ auto pAddonOfficeMenuBarSeq = rAddonOfficeMenuBarSeq.getArray();
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aPopupMenuNode( aAddonMenuBarNode + aAddonMenuBarNodeSeq[n] );
+
+ // Read the MenuItem
+ if ( ReadPopupMenu( aPopupMenuNode, aPopupMenu ) )
+ {
+ // Successfully read a popup menu, append to our list
+ OUString aPopupTitle;
+ if ( aPopupMenu[OFFSET_POPUPMENU_TITLE].Value >>= aPopupTitle )
+ {
+ StringToIndexMap::const_iterator pIter = aTitleToIndexMap.find( aPopupTitle );
+ if ( pIter != aTitleToIndexMap.end() )
+ {
+ // title already there => concat both popup menus
+ Sequence< PropertyValue >& rOldPopupMenu = pAddonOfficeMenuBarSeq[pIter->second];
+ AppendPopupMenu( rOldPopupMenu, aPopupMenu );
+ }
+ else
+ {
+ // not found
+ sal_uInt32 nMenuItemCount = rAddonOfficeMenuBarSeq.getLength() + 1;
+ rAddonOfficeMenuBarSeq.realloc( nMenuItemCount );
+ pAddonOfficeMenuBarSeq = rAddonOfficeMenuBarSeq.getArray();
+ pAddonOfficeMenuBarSeq[nIndex] = aPopupMenu;
+ aTitleToIndexMap.emplace( aPopupTitle, nIndex );
+ ++nIndex;
+ }
+ }
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadOfficeToolBarSet( AddonToolBars& rAddonOfficeToolBars, std::vector< OUString >& rAddonOfficeToolBarResNames )
+{
+ // Read the OfficeToolBar set and fill property sequences
+ OUString aAddonToolBarNodeName( "AddonUI/OfficeToolBar" );
+ Sequence< OUString > aAddonToolBarNodeSeq = GetNodeNames( aAddonToolBarNodeName );
+ OUString aAddonToolBarNode( aAddonToolBarNodeName + m_aPathDelimiter );
+
+ sal_uInt32 nCount = aAddonToolBarNodeSeq.getLength();
+
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aToolBarItemNode( aAddonToolBarNode + aAddonToolBarNodeSeq[n] );
+ rAddonOfficeToolBarResNames.push_back( aAddonToolBarNodeSeq[n] );
+ rAddonOfficeToolBars.push_back( m_aEmptyAddonToolBar );
+ ReadToolBarItemSet( aToolBarItemNode, rAddonOfficeToolBars[n] );
+ }
+}
+
+bool AddonsOptions_Impl::ReadToolBarItemSet( const OUString& rToolBarItemSetNodeName, Sequence< Sequence< PropertyValue > >& rAddonOfficeToolBarSeq )
+{
+ sal_uInt32 nToolBarItemCount = rAddonOfficeToolBarSeq.getLength();
+ OUString aAddonToolBarItemSetNode( rToolBarItemSetNodeName + m_aPathDelimiter );
+ Sequence< OUString > aAddonToolBarItemSetNodeSeq = GetNodeNames( rToolBarItemSetNodeName );
+ Sequence< PropertyValue > aToolBarItem( PROPERTYCOUNT_TOOLBARITEM );
+ auto pToolBarItem = aToolBarItem.getArray();
+ // Init the property value sequence
+ pToolBarItem[ OFFSET_TOOLBARITEM_URL ].Name = m_aPropNames[ INDEX_URL ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_TITLE ].Name = m_aPropNames[ INDEX_TITLE ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_IMAGEIDENTIFIER ].Name = m_aPropNames[ INDEX_IMAGEIDENTIFIER];
+ pToolBarItem[ OFFSET_TOOLBARITEM_TARGET ].Name = m_aPropNames[ INDEX_TARGET ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_CONTROLTYPE ].Name = m_aPropNames[ INDEX_CONTROLTYPE ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_WIDTH ].Name = m_aPropNames[ INDEX_WIDTH ];
+
+ sal_uInt32 nCount = aAddonToolBarItemSetNodeSeq.getLength();
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aToolBarItemNode( aAddonToolBarItemSetNode + aAddonToolBarItemSetNodeSeq[n] );
+
+ // Read the ToolBarItem
+ if ( ReadToolBarItem( aToolBarItemNode, aToolBarItem ) )
+ {
+ // Successfully read a toolbar item, append to our list
+ sal_uInt32 nAddonCount = rAddonOfficeToolBarSeq.getLength();
+ rAddonOfficeToolBarSeq.realloc( nAddonCount+1 );
+ rAddonOfficeToolBarSeq.getArray()[nAddonCount] = aToolBarItem;
+ }
+ }
+
+ return ( o3tl::make_unsigned(rAddonOfficeToolBarSeq.getLength()) > nToolBarItemCount );
+}
+
+void AddonsOptions_Impl::ReadOfficeNotebookBarSet(
+ AddonNotebookBars& rAddonOfficeNotebookBars,
+ std::vector<OUString>& rAddonOfficeNotebookBarResNames)
+{
+ // Read the OfficeToolBar set and fill property sequences
+ OUString aAddonNotebookBarNodeName("AddonUI/OfficeNotebookBar");
+ Sequence<OUString> aAddonNotebookBarNodeSeq = GetNodeNames(aAddonNotebookBarNodeName);
+ OUString aAddonNotebookBarNode(aAddonNotebookBarNodeName + m_aPathDelimiter);
+
+ sal_uInt32 nCount = aAddonNotebookBarNodeSeq.getLength();
+
+ for (sal_uInt32 n = 0; n < nCount; n++)
+ {
+ OUString aNotebookBarItemNode(aAddonNotebookBarNode + aAddonNotebookBarNodeSeq[n]);
+ rAddonOfficeNotebookBarResNames.push_back(aAddonNotebookBarNodeSeq[n]);
+ rAddonOfficeNotebookBars.push_back(m_aEmptyAddonNotebookBar);
+ ReadNotebookBarItemSet(aNotebookBarItemNode, rAddonOfficeNotebookBars[n]);
+ }
+}
+
+bool AddonsOptions_Impl::ReadNotebookBarItemSet(
+ const OUString& rNotebookBarItemSetNodeName,
+ Sequence<Sequence<PropertyValue>>& rAddonOfficeNotebookBarSeq)
+{
+ sal_uInt32 nNotebookBarItemCount = rAddonOfficeNotebookBarSeq.getLength();
+ OUString aAddonNotebookBarItemSetNode(rNotebookBarItemSetNodeName + m_aPathDelimiter);
+ Sequence<OUString> aAddonNotebookBarItemSetNodeSeq = GetNodeNames(rNotebookBarItemSetNodeName);
+ Sequence<PropertyValue> aNotebookBarItem(PROPERTYCOUNT_NOTEBOOKBARITEM);
+ auto pNotebookBarItem = aNotebookBarItem.getArray();
+ // Init the property value sequence
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_URL].Name = m_aPropNames[INDEX_URL];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_TITLE].Name = m_aPropNames[INDEX_TITLE];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER].Name
+ = m_aPropNames[INDEX_IMAGEIDENTIFIER];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_TARGET].Name = m_aPropNames[INDEX_TARGET];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_CONTEXT].Name = m_aPropNames[INDEX_CONTEXT];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_CONTROLTYPE].Name = m_aPropNames[INDEX_CONTROLTYPE];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_WIDTH].Name = m_aPropNames[INDEX_WIDTH];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_STYLE].Name = m_aPropNames[INDEX_STYLE];
+
+ sal_uInt32 nCount = aAddonNotebookBarItemSetNodeSeq.getLength();
+ for (sal_uInt32 n = 0; n < nCount; n++)
+ {
+ OUString aNotebookBarItemNode(aAddonNotebookBarItemSetNode
+ + aAddonNotebookBarItemSetNodeSeq[n]);
+ // Read the NotebookBarItem
+ if (ReadNotebookBarItem(aNotebookBarItemNode, aNotebookBarItem))
+ {
+ // Successfully read a toolbar item, append to our list
+ sal_uInt32 nAddonCount = rAddonOfficeNotebookBarSeq.getLength();
+ rAddonOfficeNotebookBarSeq.realloc(nAddonCount + 1);
+ rAddonOfficeNotebookBarSeq.getArray()[nAddonCount] = aNotebookBarItem;
+ }
+ }
+
+ return (o3tl::make_unsigned(rAddonOfficeNotebookBarSeq.getLength())
+ > nNotebookBarItemCount);
+}
+
+void AddonsOptions_Impl::ReadImages( ImageManager& aImageManager )
+{
+ // Read the user-defined Images set and fill image manager
+ OUString aAddonImagesNodeName( "AddonUI/Images" );
+ Sequence< OUString > aAddonImagesNodeSeq = GetNodeNames( aAddonImagesNodeName );
+ OUString aAddonImagesNode( aAddonImagesNodeName + m_aPathDelimiter );
+
+ sal_uInt32 nCount = aAddonImagesNodeSeq.getLength();
+
+ // Init the property value sequence
+ OUString aURL;
+
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aImagesItemNode( aAddonImagesNode + aAddonImagesNodeSeq[n] );
+
+ // Create sequence for data access
+ Sequence< OUString > aAddonImageItemNodePropNames = { aImagesItemNode +
+ m_aPathDelimiter +
+ m_aPropNames[ OFFSET_MENUITEM_URL ] };
+
+ Sequence< Any > aAddonImageItemNodeValues = GetProperties( aAddonImageItemNodePropNames );
+
+ // An user-defined image entry must have a URL. As "ImageIdentifier" has a higher priority
+ // we also check if we already have an images association.
+ if (( aAddonImageItemNodeValues[0] >>= aURL ) &&
+ !aURL.isEmpty() &&
+ !HasAssociatedImages( aURL ))
+ {
+ OUString aImagesUserDefinedItemNode = aImagesItemNode +
+ m_aPathDelimiter +
+ IMAGES_NODENAME +
+ m_aPathDelimiter;
+
+ // Read a user-defined images data
+ std::unique_ptr<ImageEntry> pImageEntry = ReadImageData( aImagesUserDefinedItemNode );
+ if ( pImageEntry )
+ {
+ // Successfully read a user-defined images item, put it into our image manager
+ aImageManager.emplace( aURL, std::move(*pImageEntry) );
+ }
+ }
+ }
+}
+
+OUString AddonsOptions_Impl::GeneratePrefixURL()
+{
+ // Create a unique prefixed Add-On popup menu URL so it can be identified later as a runtime popup menu.
+ OUString aPopupMenuURL;
+ OUStringBuffer aBuf( m_aRootAddonPopupMenuURLPrexfix.getLength() + 3 );
+ aBuf.append( m_aRootAddonPopupMenuURLPrexfix );
+ aBuf.append( ++m_nRootAddonPopupMenuId );
+ aPopupMenuURL = aBuf.makeStringAndClear();
+ return aPopupMenuURL;
+}
+
+void AddonsOptions_Impl::ReadMenuMergeInstructions( MergeMenuInstructionContainer& aContainer )
+{
+ static const OUStringLiteral aMenuMergeRootName( u"AddonUI/OfficeMenuBarMerging/" );
+
+ Sequence< OUString > aAddonMergeNodesSeq = GetNodeNames( aMenuMergeRootName );
+
+ sal_uInt32 nCount = aAddonMergeNodesSeq.getLength();
+
+ // Init the property value sequence
+ Sequence< OUString > aNodePropNames( 5 );
+ auto pNodePropNames = aNodePropNames.getArray();
+
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ OUString aMergeAddonInstructions( aMenuMergeRootName + aAddonMergeNodesSeq[i] );
+
+ Sequence< OUString > aAddonInstMergeNodesSeq = GetNodeNames( aMergeAddonInstructions );
+ sal_uInt32 nCountAddons = aAddonInstMergeNodesSeq.getLength();
+
+ for ( sal_uInt32 j = 0; j < nCountAddons; j++ )
+ {
+ OUString aMergeAddonInstructionBase = aMergeAddonInstructions +
+ m_aPathDelimiter +
+ aAddonInstMergeNodesSeq[j] +
+ m_aPathDelimiter;
+
+ // Create sequence for data access
+ pNodePropNames[0] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGEPOINT ];
+
+ pNodePropNames[1] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECOMMAND ];
+
+ pNodePropNames[2] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECOMMANDPARAMETER ];
+
+ pNodePropNames[3] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGEFALLBACK ];
+
+ pNodePropNames[4] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECONTEXT ];
+
+ Sequence< Any > aNodePropValues = GetProperties( aNodePropNames );
+
+ MergeMenuInstruction aMergeMenuInstruction;
+ aNodePropValues[0] >>= aMergeMenuInstruction.aMergePoint;
+ aNodePropValues[1] >>= aMergeMenuInstruction.aMergeCommand;
+ aNodePropValues[2] >>= aMergeMenuInstruction.aMergeCommandParameter;
+ aNodePropValues[3] >>= aMergeMenuInstruction.aMergeFallback;
+ aNodePropValues[4] >>= aMergeMenuInstruction.aMergeContext;
+
+ ReadMergeMenuData( aMergeAddonInstructionBase, aMergeMenuInstruction.aMergeMenu );
+
+ aContainer.push_back( aMergeMenuInstruction );
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadMergeMenuData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeMenu )
+{
+ OUString aMergeMenuBaseNode( aMergeAddonInstructionBase+m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MENUITEMS ] );
+
+ Sequence< OUString > aSubMenuNodeNames = GetNodeNames( aMergeMenuBaseNode );
+ aMergeMenuBaseNode += m_aPathDelimiter;
+
+ // extend the node names to have full path strings
+ for ( OUString& rName : asNonConstRange(aSubMenuNodeNames) )
+ rName = aMergeMenuBaseNode + rName;
+
+ ReadSubMenuEntries( aSubMenuNodeNames, rMergeMenu );
+}
+
+void AddonsOptions_Impl::ReadToolbarMergeInstructions( ToolbarMergingInstructions& rCachedToolbarMergingInstructions )
+{
+ static const OUStringLiteral aToolbarMergeRootName( u"AddonUI/OfficeToolbarMerging/" );
+
+ Sequence< OUString > aAddonMergeNodesSeq = GetNodeNames( aToolbarMergeRootName );
+ sal_uInt32 nCount = aAddonMergeNodesSeq.getLength();
+
+ // Init the property value sequence
+ Sequence< OUString > aNodePropNames( 6 );
+ auto pNodePropNames = aNodePropNames.getArray();
+
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ OUString aMergeAddonInstructions( aToolbarMergeRootName + aAddonMergeNodesSeq[i] );
+
+ Sequence< OUString > aAddonInstMergeNodesSeq = GetNodeNames( aMergeAddonInstructions );
+ sal_uInt32 nCountAddons = aAddonInstMergeNodesSeq.getLength();
+
+ for ( sal_uInt32 j = 0; j < nCountAddons; j++ )
+ {
+ OUString aMergeAddonInstructionBase = aMergeAddonInstructions +
+ m_aPathDelimiter +
+ aAddonInstMergeNodesSeq[j] +
+ m_aPathDelimiter;
+
+ // Create sequence for data access
+ pNodePropNames[0] = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_TOOLBAR ];
+
+ pNodePropNames[1] = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGEPOINT ];
+
+ pNodePropNames[2] = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECOMMAND ];
+
+ pNodePropNames[3] = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECOMMANDPARAMETER ];
+
+ pNodePropNames[4] = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGEFALLBACK ];
+
+ pNodePropNames[5] = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECONTEXT ];
+
+ Sequence< Any > aNodePropValues = GetProperties( aNodePropNames );
+
+ MergeToolbarInstruction aMergeToolbarInstruction;
+ aNodePropValues[0] >>= aMergeToolbarInstruction.aMergeToolbar;
+ aNodePropValues[1] >>= aMergeToolbarInstruction.aMergePoint;
+ aNodePropValues[2] >>= aMergeToolbarInstruction.aMergeCommand;
+ aNodePropValues[3] >>= aMergeToolbarInstruction.aMergeCommandParameter;
+ aNodePropValues[4] >>= aMergeToolbarInstruction.aMergeFallback;
+ aNodePropValues[5] >>= aMergeToolbarInstruction.aMergeContext;
+
+ ReadMergeToolbarData( aMergeAddonInstructionBase,
+ aMergeToolbarInstruction.aMergeToolbarItems );
+
+ MergeToolbarInstructionContainer& rVector = rCachedToolbarMergingInstructions[ aMergeToolbarInstruction.aMergeToolbar ];
+ rVector.push_back( aMergeToolbarInstruction );
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadMergeToolbarData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeToolbarItems )
+{
+ OUString aMergeToolbarBaseNode = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_TOOLBARITEMS ];
+
+ ReadToolBarItemSet( aMergeToolbarBaseNode, rMergeToolbarItems );
+}
+
+void AddonsOptions_Impl::ReadNotebookBarMergeInstructions(
+ NotebookBarMergingInstructions& rCachedNotebookBarMergingInstructions)
+{
+ static const OUStringLiteral aNotebookBarMergeRootName(u"AddonUI/OfficeNotebookBarMerging/");
+
+ Sequence<OUString> aAddonMergeNodesSeq = GetNodeNames(aNotebookBarMergeRootName);
+ sal_uInt32 nCount = aAddonMergeNodesSeq.getLength();
+
+ // Init the property value sequence
+ Sequence<OUString> aNodePropNames(6);
+ auto pNodePropNames = aNodePropNames.getArray();
+
+ for (sal_uInt32 i = 0; i < nCount; i++)
+ {
+ OUString aMergeAddonInstructions(aNotebookBarMergeRootName + aAddonMergeNodesSeq[i]);
+
+ Sequence<OUString> aAddonInstMergeNodesSeq = GetNodeNames(aMergeAddonInstructions);
+ sal_uInt32 nCountAddons = aAddonInstMergeNodesSeq.getLength();
+
+ for (sal_uInt32 j = 0; j < nCountAddons; j++)
+ {
+ OUString aMergeAddonInstructionBase = aMergeAddonInstructions +
+ m_aPathDelimiter +
+ aAddonInstMergeNodesSeq[j] +
+ m_aPathDelimiter;
+
+ // Create sequence for data access
+ pNodePropNames[0] = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBAR];
+
+ pNodePropNames[1] = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGEPOINT];
+
+ pNodePropNames[2] = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGECOMMAND];
+
+ pNodePropNames[3] = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGECOMMANDPARAMETER];
+
+ pNodePropNames[4] = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGEFALLBACK];
+
+ pNodePropNames[5] = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGECONTEXT];
+
+ Sequence<Any> aNodePropValues = GetProperties(aNodePropNames);
+
+ MergeNotebookBarInstruction aMergeNotebookBarInstruction;
+ aNodePropValues[0] >>= aMergeNotebookBarInstruction.aMergeNotebookBar;
+ aNodePropValues[1] >>= aMergeNotebookBarInstruction.aMergePoint;
+ aNodePropValues[2] >>= aMergeNotebookBarInstruction.aMergeCommand;
+ aNodePropValues[3] >>= aMergeNotebookBarInstruction.aMergeCommandParameter;
+ aNodePropValues[4] >>= aMergeNotebookBarInstruction.aMergeFallback;
+ aNodePropValues[5] >>= aMergeNotebookBarInstruction.aMergeContext;
+
+ ReadMergeNotebookBarData(aMergeAddonInstructionBase,
+ aMergeNotebookBarInstruction.aMergeNotebookBarItems);
+
+ MergeNotebookBarInstructionContainer& rVector
+ = rCachedNotebookBarMergingInstructions[aMergeNotebookBarInstruction
+ .aMergeNotebookBar];
+ rVector.push_back(aMergeNotebookBarInstruction);
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadMergeNotebookBarData(
+ std::u16string_view aMergeAddonInstructionBase,
+ Sequence<Sequence<PropertyValue>>& rMergeNotebookBarItems)
+{
+ OUString aMergeNotebookBarBaseNode = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBARITEMS];
+
+ ReadNotebookBarItemSet(aMergeNotebookBarBaseNode, rMergeNotebookBarItems);
+}
+
+void AddonsOptions_Impl::ReadStatusbarMergeInstructions( MergeStatusbarInstructionContainer& aContainer )
+{
+ static const OUStringLiteral aStatusbarMergeRootName( u"AddonUI/OfficeStatusbarMerging/" );
+
+ Sequence< OUString > aAddonMergeNodesSeq = GetNodeNames( aStatusbarMergeRootName );
+ sal_uInt32 nCount = aAddonMergeNodesSeq.getLength();
+
+ Sequence< OUString > aNodePropNames( 5 );
+ auto pNodePropNames = aNodePropNames.getArray();
+
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ OUString aMergeAddonInstructions( aStatusbarMergeRootName + aAddonMergeNodesSeq[i] );
+
+ Sequence< OUString > aAddonInstMergeNodesSeq = GetNodeNames( aMergeAddonInstructions );
+ sal_uInt32 nCountAddons = aAddonInstMergeNodesSeq.getLength();
+
+ for ( sal_uInt32 j = 0; j < nCountAddons; j++ )
+ {
+ OUString aMergeAddonInstructionBase = aMergeAddonInstructions +
+ m_aPathDelimiter +
+ aAddonInstMergeNodesSeq[j] +
+ m_aPathDelimiter;
+
+ // Create sequence for data access
+ pNodePropNames[0] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGEPOINT ];
+
+ pNodePropNames[1] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGECOMMAND ];
+
+ pNodePropNames[2] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGECOMMANDPARAMETER ];
+
+ pNodePropNames[3] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGEFALLBACK ];
+
+ pNodePropNames[4] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGECONTEXT ];
+
+ Sequence< Any > aNodePropValues = GetProperties( aNodePropNames );
+
+ MergeStatusbarInstruction aMergeStatusbarInstruction;
+ aNodePropValues[0] >>= aMergeStatusbarInstruction.aMergePoint;
+ aNodePropValues[1] >>= aMergeStatusbarInstruction.aMergeCommand;
+ aNodePropValues[2] >>= aMergeStatusbarInstruction.aMergeCommandParameter;
+ // aNodePropValues[3] >>= aMergeStatusbarInstruction.aMergeFallback;
+ aNodePropValues[4] >>= aMergeStatusbarInstruction.aMergeContext;
+
+ ReadMergeStatusbarData( aMergeAddonInstructionBase,
+ aMergeStatusbarInstruction.aMergeStatusbarItems );
+
+ aContainer.push_back( aMergeStatusbarInstruction );
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadMergeStatusbarData(
+ std::u16string_view aMergeAddonInstructionBase,
+ Sequence< Sequence< PropertyValue > >& rMergeStatusbarItems )
+{
+ OUString aMergeStatusbarBaseNode = aMergeAddonInstructionBase +
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_STATUSBARITEMS ];
+
+ OUString aAddonStatusbarItemSetNode( aMergeStatusbarBaseNode + m_aPathDelimiter );
+ Sequence< OUString > aAddonStatusbarItemSetNodeSeq = GetNodeNames( aMergeStatusbarBaseNode );
+
+ Sequence< PropertyValue > aStatusbarItem( PROPERTYCOUNT_STATUSBARITEM );
+ auto pStatusbarItem = aStatusbarItem.getArray();
+ pStatusbarItem[ OFFSET_STATUSBARITEM_URL ].Name = m_aPropNames[ INDEX_URL ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_TITLE ].Name = m_aPropNames[ INDEX_TITLE ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_ALIGN ].Name = m_aPropNames[ INDEX_ALIGN ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_AUTOSIZE ].Name = m_aPropNames[ INDEX_AUTOSIZE ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_OWNERDRAW ].Name = m_aPropNames[ INDEX_OWNERDRAW ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_MANDATORY ].Name = m_aPropNames[ INDEX_MANDATORY ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_WIDTH ].Name = m_aPropNames[ INDEX_WIDTH ];
+
+ sal_uInt32 nCount = aAddonStatusbarItemSetNodeSeq.getLength();
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aStatusbarItemNode( aAddonStatusbarItemSetNode + aAddonStatusbarItemSetNodeSeq[n] );
+
+ if ( ReadStatusBarItem( aStatusbarItemNode, aStatusbarItem ) )
+ {
+ sal_uInt32 nAddonCount = rMergeStatusbarItems.getLength();
+ rMergeStatusbarItems.realloc( nAddonCount+1 );
+ rMergeStatusbarItems.getArray()[nAddonCount] = aStatusbarItem;
+ }
+ }
+}
+
+bool AddonsOptions_Impl::ReadStatusBarItem(
+ std::u16string_view aStatusarItemNodeName,
+ Sequence< PropertyValue >& aStatusbarItem )
+{
+ bool bResult( false );
+ OUString aURL;
+ OUString aAddonStatusbarItemTreeNode( aStatusarItemNodeName + m_aPathDelimiter );
+
+ Sequence< Any > aStatusbarItemNodePropValues = GetProperties( GetPropertyNamesStatusbarItem( aAddonStatusbarItemTreeNode ) );
+
+ // Command URL is required
+ if (( aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_URL ] >>= aURL ) && aURL.getLength() > 0 )
+ {
+ auto pStatusbarItem = aStatusbarItem.getArray();
+ pStatusbarItem[ OFFSET_STATUSBARITEM_URL ].Value <<= aURL;
+ pStatusbarItem[ OFFSET_STATUSBARITEM_TITLE ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_TITLE ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_CONTEXT ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_CONTEXT ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_ALIGN ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_ALIGN ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_AUTOSIZE ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_AUTOSIZE ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_OWNERDRAW ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_OWNERDRAW ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_MANDATORY ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_MANDATORY ];
+
+ // Configuration uses hyper for long. Therefore transform into sal_Int32
+ sal_Int64 nValue( 0 );
+ aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_WIDTH ] >>= nValue;
+ pStatusbarItem[ OFFSET_STATUSBARITEM_WIDTH ].Value <<= sal_Int32( nValue );
+
+ bResult = true;
+ }
+
+ return bResult;
+}
+
+bool AddonsOptions_Impl::ReadMenuItem( std::u16string_view aMenuNodeName, Sequence< PropertyValue >& aMenuItem, bool bIgnoreSubMenu )
+{
+ bool bResult = false;
+ OUString aStrValue;
+ OUString aAddonMenuItemTreeNode( aMenuNodeName + m_aPathDelimiter );
+
+ Sequence< Any > aMenuItemNodePropValues = GetProperties( GetPropertyNamesMenuItem( aAddonMenuItemTreeNode ) );
+ if (( aMenuItemNodePropValues[ OFFSET_MENUITEM_TITLE ] >>= aStrValue ) && !aStrValue.isEmpty() )
+ {
+ auto pMenuItem = aMenuItem.getArray();
+ pMenuItem[ OFFSET_MENUITEM_TITLE ].Value <<= aStrValue;
+
+ OUString aRootSubMenuName( aAddonMenuItemTreeNode + m_aPropNames[ INDEX_SUBMENU ] );
+ Sequence< OUString > aRootSubMenuNodeNames = GetNodeNames( aRootSubMenuName );
+ if ( aRootSubMenuNodeNames.hasElements() && !bIgnoreSubMenu )
+ {
+ // Set a unique prefixed Add-On popup menu URL so it can be identified later
+ OUString aPopupMenuURL = GeneratePrefixURL();
+ OUString aPopupMenuImageId;
+
+ aMenuItemNodePropValues[ OFFSET_MENUITEM_IMAGEIDENTIFIER ] >>= aPopupMenuImageId;
+ ReadAndAssociateImages( aPopupMenuURL, aPopupMenuImageId );
+
+ // A popup menu must have a title and can have a URL and ImageIdentifier
+ // Set the other property values to empty
+ pMenuItem[ OFFSET_MENUITEM_URL ].Value <<= aPopupMenuURL;
+ pMenuItem[ OFFSET_MENUITEM_TARGET ].Value <<= OUString();
+ pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Value <<= aPopupMenuImageId;
+ pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Value = aMenuItemNodePropValues[ OFFSET_MENUITEM_CONTEXT ];
+
+ // Continue to read the sub menu nodes
+ Sequence< Sequence< PropertyValue > > aSubMenuSeq;
+ OUString aSubMenuRootNodeName( aRootSubMenuName + m_aPathDelimiter );
+ for ( OUString& rName : asNonConstRange(aRootSubMenuNodeNames) )
+ rName = aSubMenuRootNodeName + rName;
+ ReadSubMenuEntries( aRootSubMenuNodeNames, aSubMenuSeq );
+ pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Value <<= aSubMenuSeq;
+ bResult = true;
+ }
+ else if (( aMenuItemNodePropValues[ OFFSET_MENUITEM_URL ] >>= aStrValue ) && !aStrValue.isEmpty() )
+ {
+ // A simple menu item => read the other properties;
+ OUString aMenuImageId;
+
+ aMenuItemNodePropValues[ OFFSET_MENUITEM_IMAGEIDENTIFIER ] >>= aMenuImageId;
+ ReadAndAssociateImages( aStrValue, aMenuImageId );
+
+ pMenuItem[ OFFSET_MENUITEM_URL ].Value <<= aStrValue;
+ pMenuItem[ OFFSET_MENUITEM_TARGET ].Value = aMenuItemNodePropValues[ OFFSET_MENUITEM_TARGET ];
+ pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Value <<= aMenuImageId;
+ pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Value = aMenuItemNodePropValues[ OFFSET_MENUITEM_CONTEXT ];
+ pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Value <<= Sequence< Sequence< PropertyValue > >(); // Submenu set!
+
+ bResult = true;
+ }
+ }
+ else if (( aMenuItemNodePropValues[ OFFSET_MENUITEM_URL ] >>= aStrValue ) &&
+ aStrValue == SEPARATOR_URL )
+ {
+ auto pMenuItem = aMenuItem.getArray();
+
+ // Separator
+ pMenuItem[ OFFSET_MENUITEM_URL ].Value <<= aStrValue;
+ pMenuItem[ OFFSET_MENUITEM_TARGET ].Value <<= OUString();
+ pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Value <<= OUString();
+ pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Value <<= OUString();
+ pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Value <<= Sequence< Sequence< PropertyValue > >(); // Submenu set!
+ bResult = true;
+ }
+
+ return bResult;
+}
+
+bool AddonsOptions_Impl::ReadPopupMenu( std::u16string_view aPopupMenuNodeName, Sequence< PropertyValue >& aPopupMenu )
+{
+ bool bResult = false;
+ OUString aStrValue;
+ OUString aAddonPopupMenuTreeNode( aPopupMenuNodeName + m_aPathDelimiter );
+
+ Sequence< Any > aPopupMenuNodePropValues = GetProperties( GetPropertyNamesPopupMenu( aAddonPopupMenuTreeNode ) );
+ if (( aPopupMenuNodePropValues[ OFFSET_POPUPMENU_TITLE ] >>= aStrValue ) &&
+ !aStrValue.isEmpty() )
+ {
+ auto pPopupMenu = aPopupMenu.getArray();
+ pPopupMenu[ OFFSET_POPUPMENU_TITLE ].Value <<= aStrValue;
+
+ OUString aRootSubMenuName( aAddonPopupMenuTreeNode + m_aPropNames[ INDEX_SUBMENU ] );
+ Sequence< OUString > aRootSubMenuNodeNames = GetNodeNames( aRootSubMenuName );
+ if ( aRootSubMenuNodeNames.hasElements() )
+ {
+ // A top-level popup menu needs a title
+ // Set a unique prefixed Add-On popup menu URL so it can be identified later
+ OUString aPopupMenuURL = GeneratePrefixURL();
+
+ pPopupMenu[ OFFSET_POPUPMENU_URL ].Value <<= aPopupMenuURL;
+ pPopupMenu[ OFFSET_POPUPMENU_CONTEXT ].Value = aPopupMenuNodePropValues[ OFFSET_POPUPMENU_CONTEXT ];
+
+ // Continue to read the sub menu nodes
+ Sequence< Sequence< PropertyValue > > aSubMenuSeq;
+ OUString aSubMenuRootNodeName( aRootSubMenuName + m_aPathDelimiter );
+ for ( OUString& rName : asNonConstRange(aRootSubMenuNodeNames) )
+ rName = aSubMenuRootNodeName + rName;
+ ReadSubMenuEntries( aRootSubMenuNodeNames, aSubMenuSeq );
+ pPopupMenu[ OFFSET_POPUPMENU_SUBMENU ].Value <<= aSubMenuSeq;
+ bResult = true;
+ }
+ }
+
+ return bResult;
+}
+
+void AddonsOptions_Impl::AppendPopupMenu( Sequence< PropertyValue >& rTargetPopupMenu, const Sequence< PropertyValue >& rSourcePopupMenu )
+{
+ Sequence< Sequence< PropertyValue > > aTargetSubMenuSeq;
+ Sequence< Sequence< PropertyValue > > aSourceSubMenuSeq;
+
+ if (( rTargetPopupMenu[ OFFSET_POPUPMENU_SUBMENU ].Value >>= aTargetSubMenuSeq ) &&
+ ( rSourcePopupMenu[ OFFSET_POPUPMENU_SUBMENU ].Value >>= aSourceSubMenuSeq ))
+ {
+ sal_uInt32 nIndex = aTargetSubMenuSeq.getLength();
+ aTargetSubMenuSeq.realloc( nIndex + aSourceSubMenuSeq.getLength() );
+ auto pTargetSubMenuSeq = aTargetSubMenuSeq.getArray();
+ for ( Sequence<PropertyValue> const & rSeq : std::as_const(aSourceSubMenuSeq) )
+ pTargetSubMenuSeq[nIndex++] = rSeq;
+ rTargetPopupMenu.getArray()[ OFFSET_POPUPMENU_SUBMENU ].Value <<= aTargetSubMenuSeq;
+ }
+}
+
+bool AddonsOptions_Impl::ReadToolBarItem( std::u16string_view aToolBarItemNodeName, Sequence< PropertyValue >& aToolBarItem )
+{
+ bool bResult = false;
+ OUString aURL;
+ OUString aAddonToolBarItemTreeNode( aToolBarItemNodeName + m_aPathDelimiter );
+
+ Sequence< Any > aToolBarItemNodePropValues = GetProperties( GetPropertyNamesToolBarItem( aAddonToolBarItemTreeNode ) );
+
+ // A toolbar item must have a command URL
+ if (( aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_URL ] >>= aURL ) && !aURL.isEmpty() )
+ {
+ OUString aTitle;
+ if ( aURL == SEPARATOR_URL )
+ {
+ auto pToolBarItem = aToolBarItem.getArray();
+
+ // A separator toolbar item only needs a URL
+ pToolBarItem[ OFFSET_TOOLBARITEM_URL ].Value <<= aURL;
+ pToolBarItem[ OFFSET_TOOLBARITEM_TITLE ].Value <<= OUString();
+ pToolBarItem[ OFFSET_TOOLBARITEM_TARGET ].Value <<= OUString();
+ pToolBarItem[ OFFSET_TOOLBARITEM_IMAGEIDENTIFIER ].Value <<= OUString();
+ pToolBarItem[ OFFSET_TOOLBARITEM_CONTEXT ].Value <<= OUString();
+ pToolBarItem[ OFFSET_TOOLBARITEM_CONTROLTYPE ].Value <<= OUString();
+ pToolBarItem[ OFFSET_TOOLBARITEM_WIDTH ].Value <<= sal_Int32( 0 );
+
+ bResult = true;
+ }
+ else if (( aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_TITLE ] >>= aTitle ) && !aTitle.isEmpty() )
+ {
+ auto pToolBarItem = aToolBarItem.getArray();
+
+ // A normal toolbar item must also have title => read the other properties;
+ OUString aImageId;
+
+ // Try to map a user-defined image URL to our internal private image URL
+ aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_IMAGEIDENTIFIER ] >>= aImageId;
+ ReadAndAssociateImages( aURL, aImageId );
+
+ pToolBarItem[ OFFSET_TOOLBARITEM_URL ].Value <<= aURL;
+ pToolBarItem[ OFFSET_TOOLBARITEM_TITLE ].Value <<= aTitle;
+ pToolBarItem[ OFFSET_TOOLBARITEM_TARGET ].Value = aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_TARGET ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_IMAGEIDENTIFIER ].Value <<= aImageId;
+ pToolBarItem[ OFFSET_TOOLBARITEM_CONTEXT ].Value = aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_CONTEXT ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_CONTROLTYPE ].Value = aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_CONTROLTYPE ];
+
+ // Configuration uses hyper for long. Therefore transform into sal_Int32
+ sal_Int64 nValue( 0 );
+ aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_WIDTH ] >>= nValue;
+ pToolBarItem[ OFFSET_TOOLBARITEM_WIDTH ].Value <<= sal_Int32( nValue );
+
+ bResult = true;
+ }
+ }
+
+ return bResult;
+}
+
+bool AddonsOptions_Impl::ReadNotebookBarItem( std::u16string_view aNotebookBarItemNodeName, Sequence< PropertyValue >& aNotebookBarItem )
+{
+ bool bResult = false;
+ OUString aURL;
+ OUString aAddonNotebookBarItemTreeNode( aNotebookBarItemNodeName + m_aPathDelimiter );
+
+ Sequence< Any > aNotebookBarItemNodePropValues = GetProperties( GetPropertyNamesNotebookBarItem( aAddonNotebookBarItemTreeNode ) );
+
+ // A toolbar item must have a command URL
+ if (( aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_URL ] >>= aURL ) && !aURL.isEmpty() )
+ {
+ OUString aTitle;
+ if ( aURL == SEPARATOR_URL )
+ {
+ auto pNotebookBarItem = aNotebookBarItem.getArray();
+
+ // A separator toolbar item only needs a URL
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_URL ].Value <<= aURL;
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_TITLE ].Value <<= OUString();
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_TARGET ].Value <<= OUString();
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER ].Value <<= OUString();
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_CONTEXT ].Value <<= OUString();
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_CONTROLTYPE ].Value <<= OUString();
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_WIDTH ].Value <<= sal_Int32( 0 );
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_STYLE ].Value <<= OUString();
+
+ bResult = true;
+ }
+ else if (( aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_TITLE ] >>= aTitle ) && !aTitle.isEmpty() )
+ {
+ auto pNotebookBarItem = aNotebookBarItem.getArray();
+
+ // A normal toolbar item must also have title => read the other properties;
+ OUString aImageId;
+
+ // Try to map a user-defined image URL to our internal private image URL
+ aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER ] >>= aImageId;
+ ReadAndAssociateImages( aURL, aImageId );
+
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_URL ].Value <<= aURL;
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_TITLE ].Value <<= aTitle;
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_TARGET ].Value = aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_TARGET ];
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER ].Value <<= aImageId;
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_CONTEXT ].Value = aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_CONTEXT ];
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_CONTROLTYPE ].Value = aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_CONTROLTYPE ];
+
+ // Configuration uses hyper for long. Therefore transform into sal_Int32
+ sal_Int64 nValue( 0 );
+ aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_WIDTH ] >>= nValue;
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_WIDTH ].Value <<= sal_Int32( nValue );
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_STYLE ].Value = aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_STYLE ];
+
+ bResult = true;
+ }
+ }
+
+ return bResult;
+}
+
+void AddonsOptions_Impl::ReadSubMenuEntries( const Sequence< OUString >& aSubMenuNodeNames, Sequence< Sequence< PropertyValue > >& rSubMenuSeq )
+{
+ Sequence< PropertyValue > aMenuItem( PROPERTYCOUNT_MENUITEM );
+ auto pMenuItem = aMenuItem.getArray();
+
+ // Init the property value sequence
+ pMenuItem[ OFFSET_MENUITEM_URL ].Name = PROPERTYNAME_URL;
+ pMenuItem[ OFFSET_MENUITEM_TITLE ].Name = PROPERTYNAME_TITLE;
+ pMenuItem[ OFFSET_MENUITEM_TARGET ].Name = PROPERTYNAME_TARGET;
+ pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Name = PROPERTYNAME_IMAGEIDENTIFIER;
+ pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Name = PROPERTYNAME_CONTEXT;
+ pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Name = PROPERTYNAME_SUBMENU; // Submenu set!
+
+ sal_uInt32 nIndex = 0;
+ sal_uInt32 nCount = aSubMenuNodeNames.getLength();
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ if ( ReadMenuItem( aSubMenuNodeNames[n], aMenuItem ))
+ {
+ sal_uInt32 nSubMenuCount = rSubMenuSeq.getLength() + 1;
+ rSubMenuSeq.realloc( nSubMenuCount );
+ rSubMenuSeq.getArray()[nIndex++] = aMenuItem;
+ }
+ }
+}
+
+bool AddonsOptions_Impl::HasAssociatedImages( const OUString& aURL )
+{
+ // FIXME: potentially this is not so useful in a world of delayed image loading
+ ImageManager::const_iterator pIter = m_aImageManager.find( aURL );
+ return ( pIter != m_aImageManager.end() );
+}
+
+void AddonsOptions_Impl::SubstituteVariables( OUString& aURL )
+{
+ aURL = comphelper::getExpandedUri(
+ comphelper::getProcessComponentContext(), aURL);
+}
+
+BitmapEx AddonsOptions_Impl::ReadImageFromURL(const OUString& aImageURL)
+{
+ BitmapEx aImage;
+
+ std::unique_ptr<SvStream> pStream = UcbStreamHelper::CreateStream( aImageURL, StreamMode::STD_READ );
+ if ( pStream && ( pStream->GetErrorCode() == ERRCODE_NONE ))
+ {
+ // Use graphic class to also support more graphic formats (bmp,png,...)
+ Graphic aGraphic;
+
+ GraphicFilter& rGF = GraphicFilter::GetGraphicFilter();
+ rGF.ImportGraphic( aGraphic, u"", *pStream );
+
+ BitmapEx aBitmapEx = aGraphic.GetBitmapEx();
+
+ Size aBmpSize = aBitmapEx.GetSizePixel();
+ if ( !aBmpSize.IsEmpty() )
+ {
+ // Support non-transparent bitmaps to be downward compatible with OOo 1.1.x addons
+ if( !aBitmapEx.IsAlpha() )
+ aBitmapEx = BitmapEx( aBitmapEx.GetBitmap(), COL_LIGHTMAGENTA );
+
+ aImage = aBitmapEx;
+ }
+ }
+
+ return aImage;
+}
+
+void AddonsOptions_Impl::ReadAndAssociateImages( const OUString& aURL, const OUString& aImageId )
+{
+ if ( aImageId.isEmpty() )
+ return;
+
+ ImageEntry aImageEntry;
+ OUString aImageURL( aImageId );
+
+ SubstituteVariables( aImageURL );
+
+ // Loop to create the two possible image names and try to read the bitmap files
+ static const char* aExtArray[] = { "_16", "_26" };
+ for ( size_t i = 0; i < SAL_N_ELEMENTS(aExtArray); i++ )
+ {
+ OUStringBuffer aFileURL( aImageURL );
+ aFileURL.appendAscii( aExtArray[i] );
+ aFileURL.append( ".bmp" );
+
+ aImageEntry.addImage( !i ? IMGSIZE_SMALL : IMGSIZE_BIG, aFileURL.makeStringAndClear() );
+ }
+
+ m_aImageManager.emplace( aURL, aImageEntry );
+}
+
+std::unique_ptr<AddonsOptions_Impl::ImageEntry> AddonsOptions_Impl::ReadImageData( std::u16string_view aImagesNodeName )
+{
+ Sequence< OUString > aImageDataNodeNames = GetPropertyNamesImages( aImagesNodeName );
+ Sequence< Any > aPropertyData;
+ Sequence< sal_Int8 > aImageDataSeq;
+ OUString aImageURL;
+
+ std::unique_ptr<ImageEntry> pEntry;
+
+ // It is possible to use both forms (embedded image data and URLs to external bitmap files) at the
+ // same time. Embedded image data has a higher priority.
+ aPropertyData = GetProperties( aImageDataNodeNames );
+ for ( int i = 0; i < PROPERTYCOUNT_IMAGES; i++ )
+ {
+ if ( i < PROPERTYCOUNT_EMBEDDED_IMAGES )
+ {
+ // Extract image data from the embedded hex binary sequence
+ BitmapEx aImage;
+ if (( aPropertyData[i] >>= aImageDataSeq ) &&
+ aImageDataSeq.hasElements() &&
+ ( CreateImageFromSequence( aImage, aImageDataSeq ) ) )
+ {
+ if ( !pEntry )
+ pEntry.reset(new ImageEntry);
+ pEntry->addImage(i == OFFSET_IMAGES_SMALL ? IMGSIZE_SMALL : IMGSIZE_BIG, aImage);
+ }
+ }
+ else if ( i == OFFSET_IMAGES_SMALL_URL || i == OFFSET_IMAGES_BIG_URL )
+ {
+ if(!pEntry)
+ pEntry.reset(new ImageEntry());
+
+ // Retrieve image data from an external bitmap file. Make sure that embedded image data
+ // has a higher priority.
+ if (aPropertyData[i] >>= aImageURL)
+ {
+ SubstituteVariables(aImageURL);
+ pEntry->addImage(i == OFFSET_IMAGES_SMALL_URL ? IMGSIZE_SMALL : IMGSIZE_BIG, aImageURL);
+ }
+ }
+ }
+
+ return pEntry;
+}
+
+bool AddonsOptions_Impl::CreateImageFromSequence( BitmapEx& rImage, Sequence< sal_Int8 >& rBitmapDataSeq ) const
+{
+ bool bResult = false;
+
+ if ( rBitmapDataSeq.hasElements() )
+ {
+ SvMemoryStream aMemStream( rBitmapDataSeq.getArray(), rBitmapDataSeq.getLength(), StreamMode::STD_READ );
+
+ ReadDIBBitmapEx(rImage, aMemStream);
+
+ if( !rImage.IsAlpha() )
+ {
+ // Support non-transparent bitmaps to be downward compatible with OOo 1.1.x addons
+ rImage = BitmapEx( rImage.GetBitmap(), COL_LIGHTMAGENTA );
+ }
+
+ bResult = true;
+ }
+
+ return bResult;
+}
+
+Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesMenuItem( std::u16string_view aPropertyRootNode ) const
+{
+ Sequence< OUString > lResult( PROPERTYCOUNT_MENUITEM );
+ auto plResult = lResult.getArray();
+
+ // Create property names dependent from the root node name
+ plResult[OFFSET_MENUITEM_URL] = aPropertyRootNode + m_aPropNames[ INDEX_URL ];
+ plResult[OFFSET_MENUITEM_TITLE] = aPropertyRootNode + m_aPropNames[ INDEX_TITLE ];
+ plResult[OFFSET_MENUITEM_IMAGEIDENTIFIER] = aPropertyRootNode + m_aPropNames[ INDEX_IMAGEIDENTIFIER ];
+ plResult[OFFSET_MENUITEM_TARGET] = aPropertyRootNode + m_aPropNames[ INDEX_TARGET ];
+ plResult[OFFSET_MENUITEM_CONTEXT] = aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ];
+ plResult[OFFSET_MENUITEM_SUBMENU] = aPropertyRootNode + m_aPropNames[ INDEX_SUBMENU ];
+
+ return lResult;
+}
+
+Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesPopupMenu( std::u16string_view aPropertyRootNode ) const
+{
+ // The URL is automatically set and not read from the configuration.
+ Sequence< OUString > lResult( PROPERTYCOUNT_POPUPMENU-1 );
+ auto plResult = lResult.getArray();
+
+ // Create property names dependent from the root node name
+ plResult[OFFSET_POPUPMENU_TITLE] = aPropertyRootNode + m_aPropNames[ INDEX_TITLE ];
+ plResult[OFFSET_POPUPMENU_CONTEXT] = aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ];
+ plResult[OFFSET_POPUPMENU_SUBMENU] = aPropertyRootNode + m_aPropNames[ INDEX_SUBMENU ];
+
+ return lResult;
+}
+
+Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesToolBarItem( std::u16string_view aPropertyRootNode ) const
+{
+ Sequence< OUString > lResult( PROPERTYCOUNT_TOOLBARITEM );
+ auto plResult = lResult.getArray();
+
+ // Create property names dependent from the root node name
+ plResult[0] = aPropertyRootNode + m_aPropNames[ INDEX_URL ];
+ plResult[1] = aPropertyRootNode + m_aPropNames[ INDEX_TITLE ];
+ plResult[2] = aPropertyRootNode + m_aPropNames[ INDEX_IMAGEIDENTIFIER];
+ plResult[3] = aPropertyRootNode + m_aPropNames[ INDEX_TARGET ];
+ plResult[4] = aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ];
+ plResult[5] = aPropertyRootNode + m_aPropNames[ INDEX_CONTROLTYPE ];
+ plResult[6] = aPropertyRootNode + m_aPropNames[ INDEX_WIDTH ];
+
+ return lResult;
+}
+
+Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesNotebookBarItem( std::u16string_view aPropertyRootNode ) const
+{
+ Sequence< OUString > lResult( PROPERTYCOUNT_NOTEBOOKBARITEM );
+ auto plResult = lResult.getArray();
+
+ // Create property names dependent from the root node name
+ plResult[0] = aPropertyRootNode + m_aPropNames[ INDEX_URL ];
+ plResult[1] = aPropertyRootNode + m_aPropNames[ INDEX_TITLE ];
+ plResult[2] = aPropertyRootNode + m_aPropNames[ INDEX_IMAGEIDENTIFIER];
+ plResult[3] = aPropertyRootNode + m_aPropNames[ INDEX_TARGET ];
+ plResult[4] = aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ];
+ plResult[5] = aPropertyRootNode + m_aPropNames[ INDEX_CONTROLTYPE ];
+ plResult[6] = aPropertyRootNode + m_aPropNames[ INDEX_WIDTH ];
+ plResult[7] = aPropertyRootNode + m_aPropNames[ INDEX_STYLE ];
+
+ return lResult;
+}
+
+Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesStatusbarItem(
+ std::u16string_view aPropertyRootNode ) const
+{
+ Sequence< OUString > lResult( PROPERTYCOUNT_STATUSBARITEM );
+ auto plResult = lResult.getArray();
+
+ plResult[0] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_URL ] );
+ plResult[1] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_TITLE ] );
+ plResult[2] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ] );
+ plResult[3] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_ALIGN ] );
+ plResult[4] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_AUTOSIZE ] );
+ plResult[5] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_OWNERDRAW ] );
+ plResult[6] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_MANDATORY ] );
+ plResult[7] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_WIDTH ] );
+
+ return lResult;
+}
+
+Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesImages( std::u16string_view aPropertyRootNode ) const
+{
+ Sequence< OUString > lResult( PROPERTYCOUNT_IMAGES );
+ auto plResult = lResult.getArray();
+
+ // Create property names dependent from the root node name
+ plResult[0] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_SMALL ];
+ plResult[1] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_BIG ];
+ plResult[2] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_SMALLHC ];
+ plResult[3] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_BIGHC ];
+ plResult[4] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_SMALL_URL ];
+ plResult[5] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_BIG_URL ];
+ plResult[6] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_SMALLHC_URL];
+ plResult[7] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_BIGHC_URL ];
+
+ return lResult;
+}
+
+namespace{
+ //global
+ std::weak_ptr<AddonsOptions_Impl> g_pAddonsOptions;
+}
+
+AddonsOptions::AddonsOptions()
+{
+ // Global access, must be guarded (multithreading!).
+ MutexGuard aGuard( GetOwnStaticMutex() );
+
+ m_pImpl = g_pAddonsOptions.lock();
+ if( !m_pImpl )
+ {
+ m_pImpl = std::make_shared<AddonsOptions_Impl>();
+ g_pAddonsOptions = m_pImpl;
+ }
+}
+
+AddonsOptions::~AddonsOptions()
+{
+ // Global access, must be guarded (multithreading!)
+ MutexGuard aGuard( GetOwnStaticMutex() );
+
+ m_pImpl.reset();
+}
+
+// public method
+
+bool AddonsOptions::HasAddonsMenu() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->HasAddonsMenu();
+}
+
+// public method
+
+sal_Int32 AddonsOptions::GetAddonsToolBarCount() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsToolBarCount();
+}
+
+// public method
+
+sal_Int32 AddonsOptions::GetAddonsNotebookBarCount() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsNotebookBarCount();
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsMenu() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsMenu();
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsMenuBarPart() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsMenuBarPart();
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsToolBarPart( sal_uInt32 nIndex ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsToolBarPart( nIndex );
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsNotebookBarPart( sal_uInt32 nIndex ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsNotebookBarPart( nIndex );
+}
+
+// public method
+
+OUString AddonsOptions::GetAddonsToolbarResourceName( sal_uInt32 nIndex ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsToolbarResourceName( nIndex );
+}
+
+// public method
+
+OUString AddonsOptions::GetAddonsNotebookBarResourceName( sal_uInt32 nIndex ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsNotebookBarResourceName( nIndex );
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsHelpMenu() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsHelpMenu();
+}
+
+// public method
+
+const MergeMenuInstructionContainer& AddonsOptions::GetMergeMenuInstructions() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetMergeMenuInstructions();
+}
+
+// public method
+
+bool AddonsOptions::GetMergeToolbarInstructions(
+ const OUString& rToolbarName,
+ MergeToolbarInstructionContainer& rToolbarInstructions ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetMergeToolbarInstructions(
+ rToolbarName, rToolbarInstructions );
+}
+
+// public method
+
+bool AddonsOptions::GetMergeNotebookBarInstructions(
+ const OUString& rNotebookBarName,
+ MergeNotebookBarInstructionContainer& rNotebookBarInstructions ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetMergeNotebookBarInstructions(
+ rNotebookBarName, rNotebookBarInstructions );
+}
+
+//public method
+
+const MergeStatusbarInstructionContainer& AddonsOptions::GetMergeStatusbarInstructions() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetMergeStatusbarInstructions();
+}
+
+// public method
+
+BitmapEx AddonsOptions::GetImageFromURL( const OUString& aURL, bool bBig, bool bNoScale ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetImageFromURL( aURL, bBig, bNoScale );
+}
+
+// public method
+
+BitmapEx AddonsOptions::GetImageFromURL( const OUString& aURL, bool bBig ) const
+{
+ return GetImageFromURL( aURL, bBig, false );
+}
+
+Mutex& AddonsOptions::GetOwnStaticMutex()
+{
+ // Create static mutex variable.
+ static Mutex ourMutex;
+
+ return ourMutex;
+}
+
+IMPL_LINK_NOARG(AddonsOptions_Impl, NotifyEvent, void*, void)
+{
+ MutexGuard aGuard(AddonsOptions::GetOwnStaticMutex());
+ ReadConfigurationData();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/framelistanalyzer.cxx b/framework/source/fwe/classes/framelistanalyzer.cxx
new file mode 100644
index 000000000..55edf3147
--- /dev/null
+++ b/framework/source/fwe/classes/framelistanalyzer.cxx
@@ -0,0 +1,260 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/framelistanalyzer.hxx>
+
+#include <targets.h>
+#include <properties.h>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/UnknownModuleException.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+namespace framework{
+
+FrameListAnalyzer::FrameListAnalyzer( const css::uno::Reference< css::frame::XFramesSupplier >& xSupplier ,
+ const css::uno::Reference< css::frame::XFrame >& xReferenceFrame ,
+ FrameAnalyzerFlags eDetectMode )
+ : m_xSupplier (xSupplier )
+ , m_xReferenceFrame(xReferenceFrame)
+ , m_eDetectMode (eDetectMode )
+{
+ impl_analyze();
+}
+
+FrameListAnalyzer::~FrameListAnalyzer()
+{
+}
+
+/** returns an analyzed list of all currently opened (top!) frames inside the desktop tree.
+
+ We try to get a snapshot of all opened frames, which are part of the desktop frame container.
+ Of course we can't access frames, which stands outside of this tree.
+ But it's necessary to collect top frames here only. Otherwise we interpret closing of last
+ frame wrong. Further we analyze this list and split into different parts.
+ E.g. for "CloseDoc" we must know, which frames of the given list refer to the same model.
+ These frames must be closed then. But all other frames must be untouched.
+ In case the request was "CloseWin" these split lists can be used too, to decide if the last window
+ or document was closed. Then we have to initialize the backing window...
+ Last but not least we must know something about our special help frame. It must be handled
+ separately. And last but not least - the backing component frame must be detected too.
+*/
+
+void FrameListAnalyzer::impl_analyze()
+{
+ // reset all members to get a consistent state
+ m_bReferenceIsHidden = false;
+ m_bReferenceIsHelp = false;
+ m_bReferenceIsBacking = false;
+ m_xHelp.clear();
+ m_xBackingComponent.clear();
+
+ // try to get the task container by using the given supplier
+ css::uno::Reference< css::container::XIndexAccess > xFrameContainer = m_xSupplier->getFrames();
+
+ // All return list get an initial size to include all possible frames.
+ // They will be packed at the end of this method ... using the actual step positions then.
+ sal_Int32 nVisibleStep = 0;
+ sal_Int32 nHiddenStep = 0;
+ sal_Int32 nModelStep = 0;
+ sal_Int32 nCount = xFrameContainer->getCount();
+
+ m_lOtherVisibleFrames.resize(nCount);
+ m_lOtherHiddenFrames.resize(nCount);
+ m_lModelFrames.resize(nCount);
+
+ // ask for the model of the given reference frame.
+ // It must be compared with the model of every frame of the container
+ // to sort it into the list of frames with the same model.
+ // Suppress this step, if right detect mode isn't set.
+ css::uno::Reference< css::frame::XModel > xReferenceModel;
+ if (m_eDetectMode & FrameAnalyzerFlags::Model)
+ {
+ css::uno::Reference< css::frame::XController > xReferenceController;
+ if (m_xReferenceFrame.is())
+ xReferenceController = m_xReferenceFrame->getController();
+ if (xReferenceController.is())
+ xReferenceModel = xReferenceController->getModel();
+ }
+
+ // check, if the reference frame is in hidden mode.
+ // But look, if this analyze step is really needed.
+ css::uno::Reference< css::beans::XPropertySet > xSet(m_xReferenceFrame, css::uno::UNO_QUERY);
+ if ( (m_eDetectMode & FrameAnalyzerFlags::Hidden) && xSet.is() )
+ {
+ xSet->getPropertyValue(FRAME_PROPNAME_ASCII_ISHIDDEN) >>= m_bReferenceIsHidden;
+ }
+
+ // check, if the reference frame includes the backing component.
+ // But look, if this analyze step is really needed.
+ if ((m_eDetectMode & FrameAnalyzerFlags::BackingComponent) && m_xReferenceFrame.is() )
+ {
+ try
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleMgr = css::frame::ModuleManager::create(xContext);
+ OUString sModule = xModuleMgr->identify(m_xReferenceFrame);
+ m_bReferenceIsBacking = sModule == "com.sun.star.frame.StartModule";
+ }
+ catch(const css::frame::UnknownModuleException&)
+ {
+ }
+ catch(const css::uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ }
+
+ // check, if the reference frame includes the help module.
+ // But look, if this analyze step is really needed.
+ if (
+ (m_eDetectMode & FrameAnalyzerFlags::Help) &&
+ (m_xReferenceFrame.is() ) &&
+ (m_xReferenceFrame->getName() == SPECIALTARGET_HELPTASK)
+ )
+ {
+ m_bReferenceIsHelp = true;
+ }
+
+ try
+ {
+ // Step over all frames of the desktop frame container and analyze it.
+ for (sal_Int32 i=0; i<nCount; ++i)
+ {
+ // Ignore invalid items ... and of course the reference frame.
+ // It will be a member of the given frame list too - but it was already
+ // analyzed before!
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ if (
+ !(xFrameContainer->getByIndex(i) >>= xFrame) ||
+ !(xFrame.is() ) ||
+ (xFrame==m_xReferenceFrame )
+ )
+ continue;
+
+ if (
+ (m_eDetectMode & FrameAnalyzerFlags::Zombie) &&
+ (
+ (!xFrame->getContainerWindow().is()) ||
+ (!xFrame->getComponentWindow().is())
+ )
+ )
+ {
+ SAL_INFO("fwk", "FrameListAnalyzer::impl_analyze(): ZOMBIE!");
+ }
+
+ // a) Is it the special help task?
+ // Return it separated from any return list.
+ if (
+ (m_eDetectMode & FrameAnalyzerFlags::Help) &&
+ (xFrame->getName()==SPECIALTARGET_HELPTASK)
+ )
+ {
+ m_xHelp = xFrame;
+ continue;
+ }
+
+ // b) Or is includes this task the special backing component?
+ // Return it separated from any return list.
+ // But check if the reference task itself is the backing frame.
+ // Our user must know it to decide right.
+ if (m_eDetectMode & FrameAnalyzerFlags::BackingComponent)
+ {
+ try
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleMgr = css::frame::ModuleManager::create(xContext);
+ OUString sModule = xModuleMgr->identify(xFrame);
+ if (sModule == "com.sun.star.frame.StartModule")
+ {
+ m_xBackingComponent = xFrame;
+ continue;
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+ }
+
+ // c) Or is it the a task, which uses the specified model?
+ // Add it to the list of "model frames".
+ if (m_eDetectMode & FrameAnalyzerFlags::Model)
+ {
+ css::uno::Reference< css::frame::XController > xController = xFrame->getController();
+ css::uno::Reference< css::frame::XModel > xModel;
+ if (xController.is())
+ xModel = xController->getModel();
+ if (xModel==xReferenceModel)
+ {
+ m_lModelFrames[nModelStep] = xFrame;
+ ++nModelStep;
+ continue;
+ }
+ }
+
+ // d) Or is it the a task, which use another or no model at all?
+ // Add it to the list of "other frames". But look for its
+ // visible state ... if it's allowed to do so.
+
+ bool bHidden = false;
+ if (m_eDetectMode & FrameAnalyzerFlags::Hidden)
+ {
+ xSet.set(xFrame, css::uno::UNO_QUERY);
+ if (xSet.is())
+ {
+ xSet->getPropertyValue(FRAME_PROPNAME_ASCII_ISHIDDEN) >>= bHidden;
+ }
+ }
+
+ if (bHidden)
+ {
+ m_lOtherHiddenFrames[nHiddenStep] = xFrame;
+ ++nHiddenStep;
+ }
+ else
+ {
+ m_lOtherVisibleFrames[nVisibleStep] = xFrame;
+ ++nVisibleStep;
+ }
+ }
+ }
+ catch (const css::lang::IndexOutOfBoundsException&)
+ {
+ // stop copying if index seems to be wrong.
+ // This interface can't really guarantee its count for multithreaded
+ // environments. So it can occur!
+ }
+
+ // Pack both lists by using the actual step positions.
+ // All empty or ignorable items should exist at the end of these lists
+ // behind the position pointers. So they will be removed by a reallocation.
+ m_lOtherVisibleFrames.resize(nVisibleStep);
+ m_lOtherHiddenFrames.resize(nHiddenStep);
+ m_lModelFrames.resize(nModelStep);
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/fwkresid.cxx b/framework/source/fwe/classes/fwkresid.cxx
new file mode 100644
index 000000000..e9a1d639d
--- /dev/null
+++ b/framework/source/fwe/classes/fwkresid.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <classes/fwkresid.hxx>
+
+OUString FwkResId(TranslateId aId) { return Translate::get(aId, Translate::Create("fwk")); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/rootactiontriggercontainer.cxx b/framework/source/fwe/classes/rootactiontriggercontainer.cxx
new file mode 100644
index 000000000..02fa9d2df
--- /dev/null
+++ b/framework/source/fwe/classes/rootactiontriggercontainer.cxx
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <classes/rootactiontriggercontainer.hxx>
+#include <classes/actiontriggercontainer.hxx>
+#include <classes/actiontriggerpropertyset.hxx>
+#include <classes/actiontriggerseparatorpropertyset.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <framework/actiontriggerhelper.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+using namespace com::sun::star::beans;
+
+namespace framework
+{
+
+const css::uno::Sequence<sal_Int8>& RootActionTriggerContainer::getUnoTunnelId() noexcept
+{
+ static const sal_uInt8 pGUID[16] = { 0x17, 0x0F, 0xA2, 0xC9, 0xCA, 0x50, 0x4A, 0xD3, 0xA6, 0x3B, 0x39, 0x99, 0xC5, 0x96, 0x43, 0x27 };
+ static css::uno::Sequence< sal_Int8 > seqID(reinterpret_cast<const sal_Int8*>(pGUID), 16);
+ return seqID;
+}
+
+RootActionTriggerContainer::RootActionTriggerContainer(css::uno::Reference<css::awt::XPopupMenu> xMenu,
+ const OUString* pMenuIdentifier)
+ : m_bContainerCreated(false)
+ , m_xMenu(std::move(xMenu))
+ , m_pMenuIdentifier(pMenuIdentifier)
+{
+}
+
+RootActionTriggerContainer::~RootActionTriggerContainer()
+{
+}
+
+// XInterface
+Any SAL_CALL RootActionTriggerContainer::queryInterface( const Type& aType )
+{
+ Any a = ::cppu::queryInterface(
+ aType ,
+ static_cast< XMultiServiceFactory* >(this),
+ static_cast< XServiceInfo* >(this),
+ static_cast< XUnoTunnel* >(this),
+ static_cast< XTypeProvider* >(this),
+ static_cast< XNamed* >(this));
+
+ if( a.hasValue() )
+ {
+ return a;
+ }
+
+ return PropertySetContainer::queryInterface( aType );
+}
+
+void SAL_CALL RootActionTriggerContainer::acquire() noexcept
+{
+ PropertySetContainer::acquire();
+}
+
+void SAL_CALL RootActionTriggerContainer::release() noexcept
+{
+ PropertySetContainer::release();
+}
+
+// XMultiServiceFactory
+Reference< XInterface > SAL_CALL RootActionTriggerContainer::createInstance( const OUString& aServiceSpecifier )
+{
+ if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGER )
+ return static_cast<OWeakObject *>( new ActionTriggerPropertySet());
+ else if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGERCONTAINER )
+ return static_cast<OWeakObject *>( new ActionTriggerContainer());
+ else if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGERSEPARATOR )
+ return static_cast<OWeakObject *>( new ActionTriggerSeparatorPropertySet());
+ else
+ throw css::uno::RuntimeException("Unknown service specifier!", static_cast<OWeakObject *>(this) );
+}
+
+Reference< XInterface > SAL_CALL RootActionTriggerContainer::createInstanceWithArguments( const OUString& ServiceSpecifier, const Sequence< Any >& /*Arguments*/ )
+{
+ return createInstance( ServiceSpecifier );
+}
+
+Sequence< OUString > SAL_CALL RootActionTriggerContainer::getAvailableServiceNames()
+{
+ Sequence< OUString > aSeq{ SERVICENAME_ACTIONTRIGGER,
+ SERVICENAME_ACTIONTRIGGERCONTAINER,
+ SERVICENAME_ACTIONTRIGGERSEPARATOR };
+ return aSeq;
+}
+
+// XIndexContainer
+void SAL_CALL RootActionTriggerContainer::insertByIndex( sal_Int32 Index, const Any& Element )
+{
+ SolarMutexGuard g;
+
+ if ( !m_bContainerCreated )
+ FillContainer();
+
+ PropertySetContainer::insertByIndex( Index, Element );
+}
+
+void SAL_CALL RootActionTriggerContainer::removeByIndex( sal_Int32 Index )
+{
+ SolarMutexGuard g;
+
+ if ( !m_bContainerCreated )
+ FillContainer();
+
+ PropertySetContainer::removeByIndex( Index );
+}
+
+// XIndexReplace
+void SAL_CALL RootActionTriggerContainer::replaceByIndex( sal_Int32 Index, const Any& Element )
+{
+ SolarMutexGuard g;
+
+ if ( !m_bContainerCreated )
+ FillContainer();
+
+ PropertySetContainer::replaceByIndex( Index, Element );
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL RootActionTriggerContainer::getCount()
+{
+ SolarMutexGuard g;
+
+ if ( !m_bContainerCreated )
+ {
+ if ( m_xMenu )
+ return m_xMenu->getItemCount();
+ else
+ return 0;
+ }
+ else
+ {
+ return PropertySetContainer::getCount();
+ }
+}
+
+Any SAL_CALL RootActionTriggerContainer::getByIndex( sal_Int32 Index )
+{
+ SolarMutexGuard g;
+
+ if ( !m_bContainerCreated )
+ FillContainer();
+
+ return PropertySetContainer::getByIndex( Index );
+}
+
+// XElementAccess
+Type SAL_CALL RootActionTriggerContainer::getElementType()
+{
+ return cppu::UnoType<XPropertySet>::get();
+}
+
+sal_Bool SAL_CALL RootActionTriggerContainer::hasElements()
+{
+ if (m_xMenu)
+ return m_xMenu->getItemCount() > 0;
+ return false;
+}
+
+// XServiceInfo
+OUString SAL_CALL RootActionTriggerContainer::getImplementationName()
+{
+ return IMPLEMENTATIONNAME_ROOTACTIONTRIGGERCONTAINER;
+}
+
+sal_Bool SAL_CALL RootActionTriggerContainer::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL RootActionTriggerContainer::getSupportedServiceNames()
+{
+ return { SERVICENAME_ACTIONTRIGGERCONTAINER };
+}
+
+// XUnoTunnel
+sal_Int64 SAL_CALL RootActionTriggerContainer::getSomething( const Sequence< sal_Int8 >& aIdentifier )
+{
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+
+// XTypeProvider
+Sequence< Type > SAL_CALL RootActionTriggerContainer::getTypes()
+{
+ // Create a static typecollection ...
+ static ::cppu::OTypeCollection ourTypeCollection(
+ cppu::UnoType<XMultiServiceFactory>::get(),
+ cppu::UnoType<XIndexContainer>::get(),
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XTypeProvider>::get(),
+ cppu::UnoType<XUnoTunnel>::get(),
+ cppu::UnoType<XNamed>::get());
+
+ return ourTypeCollection.getTypes();
+}
+
+Sequence< sal_Int8 > SAL_CALL RootActionTriggerContainer::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// private implementation helper
+void RootActionTriggerContainer::FillContainer()
+{
+ m_bContainerCreated = true;
+ ActionTriggerHelper::FillActionTriggerContainerFromMenu(
+ this, m_xMenu);
+}
+OUString RootActionTriggerContainer::getName()
+{
+ OUString sRet;
+ if( m_pMenuIdentifier )
+ sRet = *m_pMenuIdentifier;
+ return sRet;
+}
+
+void RootActionTriggerContainer::setName( const OUString& )
+{
+ throw RuntimeException();
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/sfxhelperfunctions.cxx b/framework/source/fwe/classes/sfxhelperfunctions.cxx
new file mode 100644
index 000000000..170f3e715
--- /dev/null
+++ b/framework/source/fwe/classes/sfxhelperfunctions.cxx
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/sfxhelperfunctions.hxx>
+#include <framework/ContextChangeEventMultiplexerTunnel.hxx>
+#include <helper/mischelper.hxx>
+#include <svtools/toolboxcontroller.hxx>
+#include <svtools/statusbarcontroller.hxx>
+
+static pfunc_setToolBoxControllerCreator pToolBoxControllerCreator = nullptr;
+static pfunc_setWeldToolBoxControllerCreator pWeldToolBoxControllerCreator = nullptr;
+static pfunc_setStatusBarControllerCreator pStatusBarControllerCreator = nullptr;
+static pfunc_getRefreshToolbars pRefreshToolbars = nullptr;
+static pfunc_createDockingWindow pCreateDockingWindow = nullptr;
+static pfunc_isDockingWindowVisible pIsDockingWindowVisible = nullptr;
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+
+namespace framework
+{
+
+pfunc_setToolBoxControllerCreator SetToolBoxControllerCreator( pfunc_setToolBoxControllerCreator pSetToolBoxControllerCreator )
+{
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pfunc_setToolBoxControllerCreator pOldSetToolBoxControllerCreator = pToolBoxControllerCreator;
+ pToolBoxControllerCreator = pSetToolBoxControllerCreator;
+ return pOldSetToolBoxControllerCreator;
+}
+
+rtl::Reference<svt::ToolboxController> CreateToolBoxController( const Reference< XFrame >& rFrame, ToolBox* pToolbox, ToolBoxItemId nID, const OUString& aCommandURL )
+{
+ pfunc_setToolBoxControllerCreator pFactory = nullptr;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pFactory = pToolBoxControllerCreator;
+ }
+
+ if ( pFactory )
+ return (*pFactory)( rFrame, pToolbox, nID, aCommandURL );
+ else
+ return nullptr;
+}
+
+pfunc_setWeldToolBoxControllerCreator SetWeldToolBoxControllerCreator( pfunc_setWeldToolBoxControllerCreator pSetWeldToolBoxControllerCreator )
+{
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pfunc_setWeldToolBoxControllerCreator pOldSetToolBoxControllerCreator = pWeldToolBoxControllerCreator;
+ pWeldToolBoxControllerCreator = pSetWeldToolBoxControllerCreator;
+ return pOldSetToolBoxControllerCreator;
+}
+
+css::uno::Reference<css::frame::XToolbarController> CreateWeldToolBoxController( const Reference< XFrame >& rFrame, weld::Toolbar* pToolbar, weld::Builder* pBuilder, const OUString& aCommandURL )
+{
+ pfunc_setWeldToolBoxControllerCreator pFactory = nullptr;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pFactory = pWeldToolBoxControllerCreator;
+ }
+
+ if ( pFactory )
+ return (*pFactory)( rFrame, pToolbar, pBuilder, aCommandURL );
+ else
+ return nullptr;
+}
+
+pfunc_setStatusBarControllerCreator SetStatusBarControllerCreator( pfunc_setStatusBarControllerCreator pSetStatusBarControllerCreator )
+{
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pfunc_setStatusBarControllerCreator pOldSetStatusBarControllerCreator = pSetStatusBarControllerCreator;
+ pStatusBarControllerCreator = pSetStatusBarControllerCreator;
+ return pOldSetStatusBarControllerCreator;
+}
+
+rtl::Reference<svt::StatusbarController> CreateStatusBarController( const Reference< XFrame >& rFrame, StatusBar* pStatusBar, unsigned short nID, const OUString& aCommandURL )
+{
+ pfunc_setStatusBarControllerCreator pFactory = nullptr;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pFactory = pStatusBarControllerCreator;
+ }
+
+ if ( pFactory )
+ return (*pFactory)( rFrame, pStatusBar, nID, aCommandURL );
+ else
+ return nullptr;
+}
+
+pfunc_getRefreshToolbars SetRefreshToolbars( pfunc_getRefreshToolbars pNewRefreshToolbarsFunc )
+{
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pfunc_getRefreshToolbars pOldFunc = pRefreshToolbars;
+ pRefreshToolbars = pNewRefreshToolbarsFunc;
+
+ return pOldFunc;
+}
+
+void RefreshToolbars( css::uno::Reference< css::frame::XFrame > const & rFrame )
+{
+ pfunc_getRefreshToolbars pCallback = nullptr;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pCallback = pRefreshToolbars;
+ }
+
+ if ( pCallback )
+ (*pCallback)( rFrame );
+}
+
+pfunc_createDockingWindow SetDockingWindowCreator( pfunc_createDockingWindow pNewCreateDockingWindow )
+{
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pfunc_createDockingWindow pOldFunc = pCreateDockingWindow;
+ pCreateDockingWindow = pNewCreateDockingWindow;
+
+ return pOldFunc;
+}
+
+void CreateDockingWindow( const css::uno::Reference< css::frame::XFrame >& rFrame, std::u16string_view rResourceURL )
+{
+ pfunc_createDockingWindow pFactory = nullptr;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pFactory = pCreateDockingWindow;
+ }
+
+ if ( pFactory )
+ (*pFactory)( rFrame, rResourceURL );
+}
+
+pfunc_isDockingWindowVisible SetIsDockingWindowVisible( pfunc_isDockingWindowVisible pNewIsDockingWindowVisible)
+{
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pfunc_isDockingWindowVisible pOldFunc = pIsDockingWindowVisible;
+ pIsDockingWindowVisible = pNewIsDockingWindowVisible;
+
+ return pOldFunc;
+}
+
+bool IsDockingWindowVisible( const css::uno::Reference< css::frame::XFrame >& rFrame, std::u16string_view rResourceURL )
+{
+ pfunc_isDockingWindowVisible pCall = nullptr;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pCall = pIsDockingWindowVisible;
+ }
+
+ if ( pCall )
+ return (*pCall)( rFrame, rResourceURL );
+ else
+ return false;
+}
+
+using namespace ::com::sun::star;
+uno::Reference<ui::XContextChangeEventListener> GetFirstListenerWith(
+ css::uno::Reference<css::uno::XComponentContext> const & xComponentContext,
+ uno::Reference<uno::XInterface> const& xEventFocus,
+ std::function<bool (uno::Reference<ui::XContextChangeEventListener> const&)> const& rPredicate)
+{
+ return GetFirstListenerWith_Impl(xComponentContext, xEventFocus, rPredicate);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/dispatch/interaction.cxx b/framework/source/fwe/dispatch/interaction.cxx
new file mode 100644
index 000000000..d0cf88e6f
--- /dev/null
+++ b/framework/source/fwe/dispatch/interaction.cxx
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/interaction.hxx>
+#include <framework/interaction.hxx>
+#include <com/sun/star/document/XInteractionFilterSelect.hpp>
+#include <com/sun/star/document/NoSuchFilterRequest.hpp>
+#include <utility>
+
+using namespace ::com::sun::star;
+
+namespace framework{
+
+namespace {
+
+/*-************************************************************************************************************
+ @short declaration of special continuation for filter selection
+ @descr Sometimes filter detection during loading document failed. Then we need a possibility
+ to ask user for his decision. These continuation transport selected filter by user to
+ code user of interaction.
+
+ @attention This implementation could be used one times only. We don't support a resettable continuation yet!
+ Why? Normally interaction should show a filter selection dialog and ask user for his decision.
+ He can select any filter - then instances of these class will be called by handler... or user
+ close dialog without any selection. Then another continuation should be selected by handler to
+ abort continuations... Retrying isn't very useful here... I think.
+
+ @implements XInteractionFilterSelect
+
+ @base ImplInheritanceHelper
+ ContinuationBase
+
+ @devstatus ready to use
+ @threadsafe no (used on once position only!)
+*//*-*************************************************************************************************************/
+class ContinuationFilterSelect : public comphelper::OInteraction< css::document::XInteractionFilterSelect >
+{
+ // c++ interface
+ public:
+ ContinuationFilterSelect();
+
+ // uno interface
+ public:
+ virtual void SAL_CALL setFilter( const OUString& sFilter ) override;
+ virtual OUString SAL_CALL getFilter( ) override;
+
+ // member
+ private:
+ OUString m_sFilter;
+
+}; // class ContinuationFilterSelect
+
+}
+
+// initialize continuation with right start values
+
+ContinuationFilterSelect::ContinuationFilterSelect()
+{
+}
+
+// handler should use it after selection to set user specified filter for transport
+
+void SAL_CALL ContinuationFilterSelect::setFilter( const OUString& sFilter )
+{
+ m_sFilter = sFilter;
+}
+
+// read access to transported filter
+
+OUString SAL_CALL ContinuationFilterSelect::getFilter()
+{
+ return m_sFilter;
+}
+
+class RequestFilterSelect_Impl : public ::cppu::WeakImplHelper< css::task::XInteractionRequest >
+{
+public:
+ explicit RequestFilterSelect_Impl(const OUString& rURL);
+ bool isAbort () const;
+ OUString getFilter() const;
+
+public:
+ virtual css::uno::Any SAL_CALL getRequest() override;
+ virtual css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > SAL_CALL getContinuations() override;
+
+private:
+ css::uno::Any m_aRequest;
+ rtl::Reference<comphelper::OInteractionAbort> m_xAbort;
+ rtl::Reference<ContinuationFilterSelect> m_xFilter;
+};
+
+// initialize instance with all necessary information
+// We use it without any further checks on our member then ...!
+
+RequestFilterSelect_Impl::RequestFilterSelect_Impl( const OUString& sURL )
+{
+ css::uno::Reference< css::uno::XInterface > temp2;
+ css::document::NoSuchFilterRequest aFilterRequest( OUString(),
+ temp2 ,
+ sURL );
+ m_aRequest <<= aFilterRequest;
+
+ m_xAbort = new comphelper::OInteractionAbort;
+ m_xFilter = new ContinuationFilterSelect;
+}
+
+// return abort state of interaction
+// If it is true, return value of method "getFilter()" will be unspecified then!
+
+bool RequestFilterSelect_Impl::isAbort() const
+{
+ return m_xAbort->wasSelected();
+}
+
+// return user selected filter
+// Return value valid for non aborted interaction only. Please check "isAbort()" before you call these only!
+
+OUString RequestFilterSelect_Impl::getFilter() const
+{
+ return m_xFilter->getFilter();
+}
+
+// handler call it to get type of request
+// Is hard coded to "please select filter" here. see ctor for further information.
+
+css::uno::Any SAL_CALL RequestFilterSelect_Impl::getRequest()
+{
+ return m_aRequest;
+}
+
+// handler call it to get possible continuations
+// We support "abort/select_filter" only here.
+// After interaction we support read access on these continuations on our c++ interface to
+// return user decision.
+
+css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > SAL_CALL RequestFilterSelect_Impl::getContinuations()
+{
+ return { m_xAbort, m_xFilter };
+}
+
+RequestFilterSelect::RequestFilterSelect( const OUString& sURL )
+ : mxImpl(new RequestFilterSelect_Impl( sURL ))
+{
+}
+
+RequestFilterSelect::~RequestFilterSelect()
+{
+}
+
+// return abort state of interaction
+// If it is true, return value of method "getFilter()" will be unspecified then!
+
+bool RequestFilterSelect::isAbort() const
+{
+ return mxImpl->isAbort();
+}
+
+// return user selected filter
+// Return value valid for non aborted interaction only. Please check "isAbort()" before you call these only!
+
+OUString RequestFilterSelect::getFilter() const
+{
+ return mxImpl->getFilter();
+}
+
+uno::Reference < task::XInteractionRequest > RequestFilterSelect::GetRequest() const
+{
+ return mxImpl;
+}
+
+namespace {
+
+class InteractionRequest_Impl : public ::cppu::WeakImplHelper< css::task::XInteractionRequest >
+{
+ uno::Any m_aRequest;
+ uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > m_lContinuations;
+
+public:
+ InteractionRequest_Impl( css::uno::Any aRequest,
+ const css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > >& lContinuations )
+ : m_aRequest(std::move(aRequest)), m_lContinuations(lContinuations)
+ {
+ }
+
+ virtual uno::Any SAL_CALL getRequest() override;
+ virtual uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL getContinuations() override;
+};
+
+}
+
+uno::Any SAL_CALL InteractionRequest_Impl::getRequest()
+{
+ return m_aRequest;
+}
+
+uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL InteractionRequest_Impl::getContinuations()
+{
+ return m_lContinuations;
+}
+
+uno::Reference < task::XInteractionRequest > InteractionRequest::CreateRequest(
+ const uno::Any& aRequest, const uno::Sequence< uno::Reference< task::XInteractionContinuation > >& lContinuations )
+{
+ return new InteractionRequest_Impl( aRequest, lContinuations );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/helper/actiontriggerhelper.cxx b/framework/source/fwe/helper/actiontriggerhelper.cxx
new file mode 100644
index 000000000..88edd70be
--- /dev/null
+++ b/framework/source/fwe/helper/actiontriggerhelper.cxx
@@ -0,0 +1,376 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/actiontriggerhelper.hxx>
+#include <classes/actiontriggerseparatorpropertyset.hxx>
+#include <classes/rootactiontriggercontainer.hxx>
+#include <framework/addonsoptions.hxx>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/awt/XPopupMenu.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <tools/stream.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <o3tl/string_view.hxx>
+
+const sal_uInt16 START_ITEMID = 1000;
+
+using namespace com::sun::star::awt;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::container;
+
+namespace framework
+{
+
+// implementation helper ( menu => ActionTrigger )
+
+static bool IsSeparator( const Reference< XPropertySet >& xPropertySet )
+{
+ Reference< XServiceInfo > xServiceInfo( xPropertySet, UNO_QUERY );
+ try
+ {
+ return xServiceInfo->supportsService( SERVICENAME_ACTIONTRIGGERSEPARATOR );
+ }
+ catch (const Exception&)
+ {
+ }
+
+ return false;
+}
+
+static void GetMenuItemAttributes( const Reference< XPropertySet >& xActionTriggerPropertySet,
+ OUString& aMenuLabel,
+ OUString& aCommandURL,
+ OUString& aHelpURL,
+ Reference< XBitmap >& xBitmap,
+ Reference< XIndexContainer >& xSubContainer )
+{
+ Any a;
+
+ try
+ {
+ // mandatory properties
+ a = xActionTriggerPropertySet->getPropertyValue("Text");
+ a >>= aMenuLabel;
+ a = xActionTriggerPropertySet->getPropertyValue("CommandURL");
+ a >>= aCommandURL;
+ a = xActionTriggerPropertySet->getPropertyValue("Image");
+ a >>= xBitmap;
+ a = xActionTriggerPropertySet->getPropertyValue("SubContainer");
+ a >>= xSubContainer;
+ }
+ catch (const Exception&)
+ {
+ }
+
+ // optional properties
+ try
+ {
+ a = xActionTriggerPropertySet->getPropertyValue("HelpURL");
+ a >>= aHelpURL;
+ }
+ catch (const Exception&)
+ {
+ }
+}
+
+static void InsertSubMenuItems(const Reference<XPopupMenu>& rSubMenu, sal_uInt16& nItemId,
+ const Reference<XIndexContainer>& xActionTriggerContainer)
+{
+ if ( !xActionTriggerContainer.is() )
+ return;
+
+ AddonsOptions aAddonOptions;
+ OUString aSlotURL( "slot:" );
+
+ for ( sal_Int32 i = 0; i < xActionTriggerContainer->getCount(); i++ )
+ {
+ try
+ {
+ Reference< XPropertySet > xPropSet;
+ if (( xActionTriggerContainer->getByIndex( i ) >>= xPropSet ) && ( xPropSet.is() ))
+ {
+ if ( IsSeparator( xPropSet ))
+ {
+ // Separator
+ SolarMutexGuard aGuard;
+ rSubMenu->insertSeparator(i);
+ }
+ else
+ {
+ // Menu item
+ OUString aLabel;
+ OUString aCommandURL;
+ OUString aHelpURL;
+ Reference< XBitmap > xBitmap;
+ Reference< XIndexContainer > xSubContainer;
+
+ sal_uInt16 nNewItemId = nItemId++;
+ GetMenuItemAttributes( xPropSet, aLabel, aCommandURL, aHelpURL, xBitmap, xSubContainer );
+
+ SolarMutexGuard aGuard;
+ {
+ // insert new menu item
+ sal_Int32 nIndex = aCommandURL.indexOf( aSlotURL );
+ if ( nIndex >= 0 )
+ {
+ // Special code for our menu implementation: some menu items don't have a
+ // command url but uses the item id as a unique identifier. These entries
+ // got a special url during conversion from menu=>actiontriggercontainer.
+ // Now we have to extract this special url and set the correct item id!!!
+ nNewItemId = static_cast<sal_uInt16>(o3tl::toInt32(aCommandURL.subView( nIndex+aSlotURL.getLength() )));
+ rSubMenu->insertItem(nNewItemId, aLabel, 0, i);
+ }
+ else
+ {
+ rSubMenu->insertItem(nNewItemId, aLabel, 0, i);
+ rSubMenu->setCommand(nNewItemId, aCommandURL);
+ }
+
+ // handle bitmap
+ if ( xBitmap.is() )
+ {
+ bool bImageSet = false;
+
+ Reference<css::graphic::XGraphic> xGraphic(xBitmap, UNO_QUERY);
+ if (xGraphic.is())
+ {
+ // we can take the optimized route if XGraphic is supported
+ rSubMenu->setItemImage(nNewItemId, xGraphic, false);
+ bImageSet = true;
+ }
+
+ if ( !bImageSet )
+ {
+ // This is an unknown implementation of a XBitmap interface. We have to
+ // use a more time consuming way to build an Image!
+ BitmapEx aBitmap;
+
+ Sequence< sal_Int8 > aDIBSeq;
+ {
+ aDIBSeq = xBitmap->getDIB();
+ SvMemoryStream aMem( const_cast<sal_Int8 *>(aDIBSeq.getConstArray()), aDIBSeq.getLength(), StreamMode::READ );
+ ReadDIBBitmapEx(aBitmap, aMem);
+ }
+
+ aDIBSeq = xBitmap->getMaskDIB();
+ if ( aDIBSeq.hasElements() )
+ {
+ Bitmap aMaskBitmap;
+ SvMemoryStream aMem( const_cast<sal_Int8 *>(aDIBSeq.getConstArray()), aDIBSeq.getLength(), StreamMode::READ );
+ ReadDIB(aMaskBitmap, aMem, true);
+ aBitmap = BitmapEx(aBitmap.GetBitmap(), aMaskBitmap);
+ }
+
+ if (!aBitmap.IsEmpty())
+ rSubMenu->setItemImage(nNewItemId, Graphic(aBitmap).GetXGraphic(), false);
+ }
+ }
+ else
+ {
+ // Support add-on images for context menu interceptors
+ BitmapEx aBitmap(aAddonOptions.GetImageFromURL(aCommandURL, false, true));
+ if (!aBitmap.IsEmpty())
+ rSubMenu->setItemImage(nNewItemId, Graphic(aBitmap).GetXGraphic(), false);
+ }
+
+ if ( xSubContainer.is() )
+ {
+ rtl::Reference xNewSubMenu(new VCLXPopupMenu);
+
+ // Sub menu (recursive call CreateSubMenu )
+ InsertSubMenuItems(xNewSubMenu, nItemId, xSubContainer);
+ rSubMenu->setPopupMenu(nNewItemId, xNewSubMenu);
+ }
+ }
+ }
+ }
+ }
+ catch (const IndexOutOfBoundsException&)
+ {
+ return;
+ }
+ catch (const WrappedTargetException&)
+ {
+ return;
+ }
+ catch (const RuntimeException&)
+ {
+ return;
+ }
+ }
+}
+
+// implementation helper ( ActionTrigger => menu )
+
+/// @throws RuntimeException
+static Reference< XPropertySet > CreateActionTrigger(sal_uInt16 nItemId,
+ const Reference<XPopupMenu>& rMenu,
+ const Reference<XIndexContainer>& rActionTriggerContainer)
+{
+ Reference< XPropertySet > xPropSet;
+
+ Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
+ if ( xMultiServiceFactory.is() )
+ {
+ xPropSet.set( xMultiServiceFactory->createInstance( "com.sun.star.ui.ActionTrigger" ),
+ UNO_QUERY );
+
+ Any a;
+
+ try
+ {
+ // Retrieve the menu attributes and set them in our PropertySet
+ OUString aLabel = rMenu->getItemText(nItemId);
+ a <<= aLabel;
+ xPropSet->setPropertyValue("Text", a );
+
+ OUString aCommandURL = rMenu->getCommand(nItemId);
+
+ if ( aCommandURL.isEmpty() )
+ {
+ aCommandURL = "slot:" + OUString::number( nItemId );
+ }
+
+ a <<= aCommandURL;
+ xPropSet->setPropertyValue("CommandURL", a );
+
+ Reference<XBitmap> xBitmap(rMenu->getItemImage(nItemId), UNO_QUERY);
+ if (xBitmap.is())
+ {
+ a <<= xBitmap;
+ xPropSet->setPropertyValue("Image", a );
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ return xPropSet;
+}
+
+/// @throws RuntimeException
+static Reference< XPropertySet > CreateActionTriggerSeparator( const Reference< XIndexContainer >& rActionTriggerContainer )
+{
+ Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
+ if ( xMultiServiceFactory.is() )
+ {
+ return Reference< XPropertySet >( xMultiServiceFactory->createInstance(
+ "com.sun.star.ui.ActionTriggerSeparator" ),
+ UNO_QUERY );
+ }
+
+ return Reference< XPropertySet >();
+}
+
+/// @throws RuntimeException
+static Reference< XIndexContainer > CreateActionTriggerContainer( const Reference< XIndexContainer >& rActionTriggerContainer )
+{
+ Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
+ if ( xMultiServiceFactory.is() )
+ {
+ return Reference< XIndexContainer >( xMultiServiceFactory->createInstance(
+ "com.sun.star.ui.ActionTriggerContainer" ),
+ UNO_QUERY );
+ }
+
+ return Reference< XIndexContainer >();
+}
+
+static void FillActionTriggerContainerWithMenu(const Reference<XPopupMenu>& rMenu,
+ const Reference<XIndexContainer>& rActionTriggerContainer)
+{
+ SolarMutexGuard aGuard;
+
+ for (sal_uInt16 nPos = 0, nCount = rMenu->getItemCount(); nPos < nCount; ++nPos)
+ {
+ sal_uInt16 nItemId = rMenu->getItemId(nPos);
+ css::awt::MenuItemType nType = rMenu->getItemType(nPos);
+
+ try
+ {
+ Any a;
+ Reference< XPropertySet > xPropSet;
+
+ if (nType == css::awt::MenuItemType_SEPARATOR)
+ {
+ xPropSet = CreateActionTriggerSeparator( rActionTriggerContainer );
+
+ a <<= xPropSet;
+ rActionTriggerContainer->insertByIndex( nPos, a );
+ }
+ else
+ {
+ xPropSet = CreateActionTrigger(nItemId, rMenu, rActionTriggerContainer);
+
+ a <<= xPropSet;
+ rActionTriggerContainer->insertByIndex( nPos, a );
+
+ css::uno::Reference<XPopupMenu> xPopupMenu = rMenu->getPopupMenu(nItemId);
+ if (xPopupMenu.is())
+ {
+ // recursive call to build next sub menu
+ Reference< XIndexContainer > xSubContainer = CreateActionTriggerContainer( rActionTriggerContainer );
+
+ a <<= xSubContainer;
+ xPropSet->setPropertyValue("SubContainer", a );
+ FillActionTriggerContainerWithMenu(xPopupMenu, xSubContainer);
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+}
+
+void ActionTriggerHelper::CreateMenuFromActionTriggerContainer(
+ const Reference<XPopupMenu>& rNewMenu,
+ const Reference<XIndexContainer>& rActionTriggerContainer)
+{
+ sal_uInt16 nItemId = START_ITEMID;
+
+ if ( rActionTriggerContainer.is() )
+ InsertSubMenuItems(rNewMenu, nItemId, rActionTriggerContainer);
+}
+
+void ActionTriggerHelper::FillActionTriggerContainerFromMenu(
+ Reference< XIndexContainer > const & xActionTriggerContainer,
+ const css::uno::Reference<XPopupMenu>& rMenu)
+{
+ FillActionTriggerContainerWithMenu(rMenu, xActionTriggerContainer);
+}
+
+Reference< XIndexContainer > ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
+ const css::uno::Reference<XPopupMenu>& rMenu,
+ const OUString* pMenuIdentifier )
+{
+ return new RootActionTriggerContainer(rMenu, pMenuIdentifier);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/helper/configimporter.cxx b/framework/source/fwe/helper/configimporter.cxx
new file mode 100644
index 000000000..bc92d180b
--- /dev/null
+++ b/framework/source/fwe/helper/configimporter.cxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/configimporter.hxx>
+#include <toolboxconfiguration.hxx>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/ui/XUIConfigurationManager2.hpp>
+
+using namespace ::com::sun::star;
+
+namespace framework
+{
+
+bool UIConfigurationImporterOOo1x::ImportCustomToolbars(
+ const uno::Reference< ui::XUIConfigurationManager2 >& rContainerFactory,
+ std::vector< uno::Reference< container::XIndexContainer > >& rSeqContainer,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< embed::XStorage >& rToolbarStorage )
+{
+ bool bResult ( false );
+ if ( rToolbarStorage.is() && rContainerFactory.is() )
+ {
+ try
+ {
+ for ( sal_uInt16 i = 1; i <= 4; i++ )
+ {
+ OUString aTbxStreamName = "userdeftoolbox" + OUString::number(i) + ".xml";
+ uno::Reference< io::XStream > xStream = rToolbarStorage->openStreamElement( aTbxStreamName, embed::ElementModes::READ );
+ if ( xStream.is() )
+ {
+ uno::Reference< io::XInputStream > xInputStream = xStream->getInputStream();
+ if ( xInputStream.is() )
+ {
+ uno::Reference< container::XIndexContainer > xContainer = rContainerFactory->createSettings();
+ if ( ToolBoxConfiguration::LoadToolBox( rxContext, xInputStream, xContainer ))
+ {
+ rSeqContainer.push_back( xContainer );
+ bResult = true;
+ }
+ }
+ }
+ }
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+ }
+
+ return bResult;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/helper/documentundoguard.cxx b/framework/source/fwe/helper/documentundoguard.cxx
new file mode 100644
index 000000000..5f7d16041
--- /dev/null
+++ b/framework/source/fwe/helper/documentundoguard.cxx
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/documentundoguard.hxx>
+
+#include <com/sun/star/document/XUndoManagerSupplier.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+#include <tools/diagnose_ex.h>
+
+namespace framework
+{
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::document::XUndoManagerSupplier;
+ using ::com::sun::star::document::XUndoManager;
+ using ::com::sun::star::document::XUndoManagerListener;
+ using ::com::sun::star::document::UndoManagerEvent;
+ using ::com::sun::star::lang::EventObject;
+
+ //= UndoManagerContextListener
+
+ typedef ::cppu::WeakImplHelper < XUndoManagerListener
+ > UndoManagerContextListener_Base;
+
+ namespace {
+
+ class UndoManagerContextListener : public UndoManagerContextListener_Base
+ {
+ public:
+ explicit UndoManagerContextListener( const Reference< XUndoManager >& i_undoManager )
+ :m_xUndoManager( i_undoManager )
+ ,m_nRelativeContextDepth( 0 )
+ ,m_documentDisposed( false )
+ {
+ osl_atomic_increment( &m_refCount );
+ {
+ m_xUndoManager->addUndoManagerListener( this );
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+ void finish()
+ {
+ OSL_ENSURE( m_nRelativeContextDepth >= 0, "UndoManagerContextListener: more contexts left than entered?" );
+
+ if ( m_documentDisposed )
+ return;
+
+ // work with a copy of m_nRelativeContextDepth, to be independent from possible bugs in the
+ // listener notifications (where it would be decremented with every leaveUndoContext)
+ sal_Int32 nDepth = m_nRelativeContextDepth;
+ while ( nDepth-- > 0 )
+ {
+ m_xUndoManager->leaveUndoContext();
+ }
+ m_xUndoManager->removeUndoManagerListener( this );
+ }
+
+ // XUndoManagerListener
+ virtual void SAL_CALL undoActionAdded( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL actionUndone( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL actionRedone( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL allActionsCleared( const EventObject& i_event ) override;
+ virtual void SAL_CALL redoActionsCleared( const EventObject& i_event ) override;
+ virtual void SAL_CALL resetAll( const EventObject& i_event ) override;
+ virtual void SAL_CALL enteredContext( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL enteredHiddenContext( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL leftContext( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL leftHiddenContext( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL cancelledContext( const UndoManagerEvent& i_event ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const EventObject& i_event ) override;
+
+ private:
+ Reference< XUndoManager > const m_xUndoManager;
+ oslInterlockedCount m_nRelativeContextDepth;
+ bool m_documentDisposed;
+ };
+
+ }
+
+ void SAL_CALL UndoManagerContextListener::undoActionAdded( const UndoManagerEvent& )
+ {
+ // not interested in
+ }
+
+ void SAL_CALL UndoManagerContextListener::actionUndone( const UndoManagerEvent& )
+ {
+ // not interested in
+ }
+
+ void SAL_CALL UndoManagerContextListener::actionRedone( const UndoManagerEvent& )
+ {
+ // not interested in
+ }
+
+ void SAL_CALL UndoManagerContextListener::allActionsCleared( const EventObject& )
+ {
+ // not interested in
+ }
+
+ void SAL_CALL UndoManagerContextListener::redoActionsCleared( const EventObject& )
+ {
+ // not interested in
+ }
+
+ void SAL_CALL UndoManagerContextListener::resetAll( const EventObject& )
+ {
+ m_nRelativeContextDepth = 0;
+ }
+
+ void SAL_CALL UndoManagerContextListener::enteredContext( const UndoManagerEvent& )
+ {
+ osl_atomic_increment( &m_nRelativeContextDepth );
+ }
+
+ void SAL_CALL UndoManagerContextListener::enteredHiddenContext( const UndoManagerEvent& )
+ {
+ osl_atomic_increment( &m_nRelativeContextDepth );
+ }
+
+ void SAL_CALL UndoManagerContextListener::leftContext( const UndoManagerEvent& )
+ {
+ osl_atomic_decrement( &m_nRelativeContextDepth );
+ }
+
+ void SAL_CALL UndoManagerContextListener::leftHiddenContext( const UndoManagerEvent& )
+ {
+ osl_atomic_decrement( &m_nRelativeContextDepth );
+ }
+
+ void SAL_CALL UndoManagerContextListener::cancelledContext( const UndoManagerEvent& )
+ {
+ osl_atomic_decrement( &m_nRelativeContextDepth );
+ }
+
+ void SAL_CALL UndoManagerContextListener::disposing( const EventObject& )
+ {
+ m_documentDisposed = true;
+ }
+
+ //= DocumentUndoGuard_Data
+
+ struct DocumentUndoGuard_Data
+ {
+ Reference< XUndoManager > xUndoManager;
+ ::rtl::Reference< UndoManagerContextListener > pContextListener;
+ };
+
+ namespace
+ {
+
+ void lcl_init( DocumentUndoGuard_Data& i_data, const Reference< XInterface >& i_undoSupplierComponent )
+ {
+ try
+ {
+ Reference< XUndoManagerSupplier > xUndoSupplier( i_undoSupplierComponent, UNO_QUERY );
+ if ( xUndoSupplier.is() )
+ i_data.xUndoManager.set( xUndoSupplier->getUndoManager(), css::uno::UNO_SET_THROW );
+
+ if ( i_data.xUndoManager.is() )
+ i_data.pContextListener.set( new UndoManagerContextListener( i_data.xUndoManager ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ }
+
+ void lcl_restore( DocumentUndoGuard_Data& i_data )
+ {
+ try
+ {
+ if ( i_data.pContextListener.is() )
+ i_data.pContextListener->finish();
+ i_data.pContextListener.clear();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ }
+ }
+
+ //= DocumentUndoGuard
+
+ DocumentUndoGuard::DocumentUndoGuard( const Reference< XInterface >& i_undoSupplierComponent )
+ :m_xData( new DocumentUndoGuard_Data )
+ {
+ lcl_init( *m_xData, i_undoSupplierComponent );
+ }
+
+ DocumentUndoGuard::~DocumentUndoGuard()
+ {
+ lcl_restore( *m_xData );
+ }
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/helper/propertysetcontainer.cxx b/framework/source/fwe/helper/propertysetcontainer.cxx
new file mode 100644
index 000000000..ad7d63953
--- /dev/null
+++ b/framework/source/fwe/helper/propertysetcontainer.cxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/propertysetcontainer.hxx>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <cppuhelper/queryinterface.hxx>
+#include <vcl/svapp.hxx>
+
+constexpr OUStringLiteral WRONG_TYPE_EXCEPTION = u"Only XPropertSet allowed!";
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::container;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+
+namespace framework
+{
+
+PropertySetContainer::PropertySetContainer()
+{
+}
+
+PropertySetContainer::~PropertySetContainer()
+{
+}
+
+// XInterface
+void SAL_CALL PropertySetContainer::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL PropertySetContainer::release() noexcept
+{
+ OWeakObject::release();
+}
+
+Any SAL_CALL PropertySetContainer::queryInterface( const Type& rType )
+{
+ Any a = ::cppu::queryInterface(
+ rType ,
+ static_cast< XIndexContainer* >(this),
+ static_cast< XIndexReplace* >(this),
+ static_cast< XIndexAccess* >(this),
+ static_cast< XElementAccess* >(this) );
+
+ if( a.hasValue() )
+ {
+ return a;
+ }
+
+ return OWeakObject::queryInterface( rType );
+}
+
+// XIndexContainer
+void SAL_CALL PropertySetContainer::insertByIndex( sal_Int32 Index, const css::uno::Any& Element )
+{
+ SolarMutexGuard g;
+
+ sal_Int32 nSize = m_aPropertySetVector.size();
+
+ if ( nSize < Index )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ Reference< XPropertySet > aPropertySetElement;
+
+ if ( !(Element >>= aPropertySetElement) )
+ {
+ throw IllegalArgumentException(
+ WRONG_TYPE_EXCEPTION,
+ static_cast<OWeakObject *>(this), 2 );
+ }
+
+ if ( nSize == Index )
+ m_aPropertySetVector.push_back( aPropertySetElement );
+ else
+ {
+ PropertySetVector::iterator aIter = m_aPropertySetVector.begin();
+ aIter += Index;
+ m_aPropertySetVector.insert( aIter, aPropertySetElement );
+ }
+}
+
+void SAL_CALL PropertySetContainer::removeByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ if ( static_cast<sal_Int32>(m_aPropertySetVector.size()) <= nIndex )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ m_aPropertySetVector.erase(m_aPropertySetVector.begin() + nIndex);
+}
+
+// XIndexReplace
+void SAL_CALL PropertySetContainer::replaceByIndex( sal_Int32 Index, const css::uno::Any& Element )
+{
+ if ( static_cast<sal_Int32>(m_aPropertySetVector.size()) <= Index )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ Reference< XPropertySet > aPropertySetElement;
+
+ if ( !(Element >>= aPropertySetElement) )
+ {
+ throw IllegalArgumentException(
+ WRONG_TYPE_EXCEPTION,
+ static_cast<OWeakObject *>(this), 2 );
+ }
+
+ m_aPropertySetVector[ Index ] = aPropertySetElement;
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL PropertySetContainer::getCount()
+{
+ SolarMutexGuard g;
+
+ return m_aPropertySetVector.size();
+}
+
+Any SAL_CALL PropertySetContainer::getByIndex( sal_Int32 Index )
+{
+ SolarMutexGuard g;
+
+ if ( static_cast<sal_Int32>(m_aPropertySetVector.size()) <= Index )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ return Any(m_aPropertySetVector[ Index ]);
+}
+
+// XElementAccess
+sal_Bool SAL_CALL PropertySetContainer::hasElements()
+{
+ SolarMutexGuard g;
+
+ return !( m_aPropertySetVector.empty() );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/helper/titlehelper.cxx b/framework/source/fwe/helper/titlehelper.cxx
new file mode 100644
index 000000000..aa4e8f0b2
--- /dev/null
+++ b/framework/source/fwe/helper/titlehelper.cxx
@@ -0,0 +1,685 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/titlehelper.hxx>
+#include <classes/fwkresid.hxx>
+#include <strings.hrc>
+#include <properties.h>
+
+#include <com/sun/star/frame/UntitledNumbersConst.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XUntitledNumbers.hpp>
+#include <com/sun/star/frame/XModel3.hpp>
+#include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
+
+#include <unotools/configmgr.hxx>
+#include <unotools/bootstrap.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/mutex.hxx>
+#include <tools/urlobj.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+
+using namespace css;
+using namespace css::uno;
+using namespace css::frame;
+
+namespace framework{
+
+TitleHelper::TitleHelper(css::uno::Reference< css::uno::XComponentContext > xContext,
+ const css::uno::Reference< css::uno::XInterface >& xOwner,
+ const css::uno::Reference< css::frame::XUntitledNumbers >& xNumbers)
+ : ::cppu::BaseMutex ()
+ , m_xContext (std::move(xContext))
+ , m_bExternalTitle (false)
+ , m_nLeasedNumber (css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ , m_aListener (m_aMutex)
+{
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ m_xOwner = xOwner;
+ m_xUntitledNumbers = xNumbers;
+ }
+ // <- SYNCHRONIZED
+
+ css::uno::Reference< css::frame::XModel > xModel(xOwner, css::uno::UNO_QUERY);
+ if (xModel.is ())
+ {
+ impl_startListeningForModel (xModel);
+ return;
+ }
+
+ css::uno::Reference< css::frame::XController > xController(xOwner, css::uno::UNO_QUERY);
+ if (xController.is ())
+ {
+ impl_startListeningForController (xController);
+ return;
+ }
+
+ css::uno::Reference< css::frame::XFrame > xFrame(xOwner, css::uno::UNO_QUERY);
+ if (xFrame.is ())
+ {
+ impl_startListeningForFrame (xFrame);
+ return;
+ }
+}
+
+TitleHelper::~TitleHelper()
+{
+}
+
+OUString SAL_CALL TitleHelper::getTitle()
+{
+ // SYNCHRONIZED ->
+ osl::MutexGuard aLock(m_aMutex);
+
+ // An external title will win always and disable all internal logic about
+ // creating/using a title value.
+ // Even an empty string will be accepted as valid title !
+ if (m_bExternalTitle)
+ return m_sTitle;
+
+ // Title seems to be up-to-date. Return it directly.
+ if (!m_sTitle.isEmpty())
+ return m_sTitle;
+
+ // Title seems to be unused till now ... do bootstraping
+ impl_updateTitle (true);
+
+ return m_sTitle;
+ // <- SYNCHRONIZED
+}
+
+void SAL_CALL TitleHelper::setTitle(const OUString& sTitle)
+{
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ m_bExternalTitle = true;
+ m_sTitle = sTitle;
+ }
+ // <- SYNCHRONIZED
+
+ impl_sendTitleChangedEvent ();
+}
+
+void SAL_CALL TitleHelper::addTitleChangeListener(const css::uno::Reference< css::frame::XTitleChangeListener >& xListener)
+{
+ // container is threadsafe by himself
+ m_aListener.addInterface( cppu::UnoType<css::frame::XTitleChangeListener>::get(), xListener );
+}
+
+void SAL_CALL TitleHelper::removeTitleChangeListener(const css::uno::Reference< css::frame::XTitleChangeListener >& xListener)
+{
+ // container is threadsafe by himself
+ m_aListener.removeInterface( cppu::UnoType<css::frame::XTitleChangeListener>::get(), xListener );
+}
+
+void SAL_CALL TitleHelper::titleChanged(const css::frame::TitleChangedEvent& aEvent)
+{
+ css::uno::Reference< css::frame::XTitle > xSubTitle;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xSubTitle.set(m_xSubTitle.get (), css::uno::UNO_QUERY);
+ }
+ // <- SYNCHRONIZED
+
+ if (aEvent.Source != xSubTitle)
+ return;
+
+ impl_updateTitle ();
+}
+
+void SAL_CALL TitleHelper::documentEventOccured(const css::document::DocumentEvent& aEvent)
+{
+ if ( ! aEvent.EventName.equalsIgnoreAsciiCase("OnSaveAsDone")
+ && ! aEvent.EventName.equalsIgnoreAsciiCase("OnModeChanged")
+ && ! aEvent.EventName.equalsIgnoreAsciiCase("OnTitleChanged"))
+ return;
+
+ css::uno::Reference< css::frame::XModel > xOwner;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xOwner.set(m_xOwner.get (), css::uno::UNO_QUERY);
+ }
+ // <- SYNCHRONIZED
+
+ if (aEvent.Source != xOwner
+ || ((aEvent.EventName.equalsIgnoreAsciiCase("OnModeChanged")
+ || aEvent.EventName.equalsIgnoreAsciiCase("OnTitleChanged"))
+ && !xOwner.is()))
+ {
+ return;
+ }
+
+ impl_updateTitle ();
+}
+
+void SAL_CALL TitleHelper::frameAction(const css::frame::FrameActionEvent& aEvent)
+{
+ css::uno::Reference< css::frame::XFrame > xOwner;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xOwner.set(m_xOwner.get (), css::uno::UNO_QUERY);
+ }
+ // <- SYNCHRONIZED
+
+ if (aEvent.Source != xOwner)
+ return;
+
+ // we are interested on events only, which must trigger a title bar update
+ // because component was changed.
+ if (
+ (aEvent.Action == css::frame::FrameAction_COMPONENT_ATTACHED ) ||
+ (aEvent.Action == css::frame::FrameAction_COMPONENT_REATTACHED) ||
+ (aEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING )
+ )
+ {
+ impl_updateListeningForFrame (xOwner);
+ impl_updateTitle ();
+ }
+}
+
+void SAL_CALL TitleHelper::disposing(const css::lang::EventObject& aEvent)
+{
+ css::uno::Reference< css::uno::XInterface > xOwner;
+ css::uno::Reference< css::frame::XUntitledNumbers > xNumbers;
+ ::sal_Int32 nLeasedNumber;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xOwner = m_xOwner;
+ xNumbers.set(m_xUntitledNumbers.get(), css::uno::UNO_QUERY);
+ nLeasedNumber = m_nLeasedNumber;
+ }
+ // <- SYNCHRONIZED
+
+ if ( ! xOwner.is ())
+ return;
+
+ css::uno::Reference< css::frame::XFrame > xFrame(xOwner, css::uno::UNO_QUERY);
+ if (xFrame.is())
+ xFrame->removeFrameActionListener(this);
+
+ if (xOwner != aEvent.Source)
+ return;
+
+ if (
+ (xNumbers.is () ) &&
+ (nLeasedNumber != css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ )
+ xNumbers->releaseNumber (nLeasedNumber);
+
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ m_xOwner.clear();
+ m_sTitle = OUString ();
+ m_nLeasedNumber = css::frame::UntitledNumbersConst::INVALID_NUMBER;
+ }
+ // <- SYNCHRONIZED
+}
+
+void TitleHelper::impl_sendTitleChangedEvent ()
+{
+ css::uno::Reference<css::uno::XInterface> xOwner;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xOwner = m_xOwner;
+ }
+ // <- SYNCHRONIZED
+
+ css::frame::TitleChangedEvent aEvent(xOwner, m_sTitle);
+
+ if( ! aEvent.Source.is() )
+ return;
+
+ comphelper::OInterfaceContainerHelper2* pContainer = m_aListener.getContainer( cppu::UnoType<css::frame::XTitleChangeListener>::get());
+ if ( ! pContainer)
+ return;
+
+ comphelper::OInterfaceIteratorHelper2 pIt( *pContainer );
+ while ( pIt.hasMoreElements() )
+ {
+ try
+ {
+ static_cast<css::frame::XTitleChangeListener*>(pIt.next())->titleChanged( aEvent );
+ }
+ catch(const css::uno::Exception&)
+ {
+ pIt.remove();
+ }
+ }
+}
+
+void TitleHelper::impl_updateTitle (bool init)
+{
+ css::uno::Reference< css::frame::XModel3 > xModel;
+ css::uno::Reference< css::frame::XController > xController;
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xModel.set (m_xOwner.get(), css::uno::UNO_QUERY);
+ xController.set(m_xOwner.get(), css::uno::UNO_QUERY);
+ xFrame.set (m_xOwner.get(), css::uno::UNO_QUERY);
+ }
+ // <- SYNCHRONIZED
+
+ if (xModel.is ())
+ {
+ impl_updateTitleForModel (xModel, init);
+ }
+ else if (xController.is ())
+ {
+ impl_updateTitleForController (xController, init);
+ }
+ else if (xFrame.is ())
+ {
+ impl_updateTitleForFrame (xFrame, init);
+ }
+}
+
+void TitleHelper::impl_updateTitleForModel (const css::uno::Reference< css::frame::XModel3 >& xModel, bool init)
+{
+ css::uno::Reference< css::uno::XInterface > xOwner;
+ css::uno::Reference< css::frame::XUntitledNumbers > xNumbers;
+ ::sal_Int32 nLeasedNumber;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ // external title won't be updated internally!
+ // It has to be set from outside new.
+ if (m_bExternalTitle)
+ return;
+
+ xOwner = m_xOwner;
+ xNumbers.set (m_xUntitledNumbers.get(), css::uno::UNO_QUERY);
+ nLeasedNumber = m_nLeasedNumber;
+ }
+ // <- SYNCHRONIZED
+
+ if (
+ ( ! xOwner.is ()) ||
+ ( ! xNumbers.is ()) ||
+ ( ! xModel.is ())
+ )
+ return;
+
+ OUString sTitle;
+ OUString sURL;
+
+ css::uno::Reference< css::frame::XStorable > xURLProvider(xModel , css::uno::UNO_QUERY);
+ if (xURLProvider.is())
+ sURL = xURLProvider->getLocation ();
+
+ utl::MediaDescriptor aDescriptor(xModel->getArgs2( { utl::MediaDescriptor::PROP_SUGGESTEDSAVEASNAME } ));
+ const OUString sSuggestedSaveAsName = aDescriptor.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_SUGGESTEDSAVEASNAME, OUString());
+
+ if (!sURL.isEmpty())
+ {
+ sTitle = impl_convertURL2Title(sURL);
+ if (nLeasedNumber != css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ xNumbers->releaseNumber (nLeasedNumber);
+ nLeasedNumber = css::frame::UntitledNumbersConst::INVALID_NUMBER;
+ }
+ else if (!sSuggestedSaveAsName.isEmpty())
+ {
+ // tdf#121537 Use suggested save as name for title if file has not yet been saved
+ sTitle = sSuggestedSaveAsName;
+ }
+ else
+ {
+ if (nLeasedNumber == css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ nLeasedNumber = xNumbers->leaseNumber (xOwner);
+
+ if (nLeasedNumber != css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ sTitle = xNumbers->getUntitledPrefix() + OUString::number(nLeasedNumber);
+ else
+ sTitle = xNumbers->getUntitledPrefix() + "?";
+ }
+
+ bool bChanged;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ // WORKAROUND: the notification is currently sent always,
+ // can be changed after shared mode is supported per UNO API
+ bChanged = !init; // && m_sTitle != sTitle
+
+ m_sTitle = sTitle;
+ m_nLeasedNumber = nLeasedNumber;
+ }
+ // <- SYNCHRONIZED
+
+ if (bChanged)
+ impl_sendTitleChangedEvent ();
+}
+
+void TitleHelper::impl_updateTitleForController (const css::uno::Reference< css::frame::XController >& xController, bool init)
+{
+ css::uno::Reference< css::uno::XInterface > xOwner;
+ css::uno::Reference< css::frame::XUntitledNumbers > xNumbers;
+ ::sal_Int32 nLeasedNumber;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ // external title won't be updated internally!
+ // It has to be set from outside new.
+ if (m_bExternalTitle)
+ return;
+
+ xOwner = m_xOwner;
+ xNumbers.set (m_xUntitledNumbers.get(), css::uno::UNO_QUERY);
+ nLeasedNumber = m_nLeasedNumber;
+ }
+ // <- SYNCHRONIZED
+
+ if (
+ ( ! xOwner.is ()) ||
+ ( ! xNumbers.is ()) ||
+ ( ! xController.is ())
+ )
+ return;
+
+ OUStringBuffer sTitle(256);
+
+ if (nLeasedNumber == css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ nLeasedNumber = xNumbers->leaseNumber (xOwner);
+
+ css::uno::Reference< css::frame::XTitle > xModelTitle(xController->getModel (), css::uno::UNO_QUERY);
+ css::uno::Reference< css::frame::XModel > xModel = xController->getModel ();
+ if (!xModelTitle.is ())
+ xModelTitle.set(xController, css::uno::UNO_QUERY);
+ if (xModelTitle.is ())
+ {
+ sTitle.append (xModelTitle->getTitle ());
+ if ( nLeasedNumber > 1 )
+ {
+ sTitle.append(" : ");
+ sTitle.append(nLeasedNumber);
+ }
+ if (xModel.is ())
+ {
+ INetURLObject aURL (xModel->getURL ());
+ if (aURL.GetProtocol () != INetProtocol::File
+ && aURL.GetProtocol () != INetProtocol::NotValid)
+ {
+ OUString sRemoteText (FwkResId (STR_REMOTE_TITLE));
+ sTitle.append (sRemoteText);
+ }
+ }
+ }
+ else
+ {
+ sTitle.append (xNumbers->getUntitledPrefix ());
+ if ( nLeasedNumber > 1 )
+ {
+ sTitle.append(nLeasedNumber );
+ }
+ }
+
+ bool bChanged;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ OUString sNewTitle = sTitle.makeStringAndClear ();
+ bChanged = !init && m_sTitle != sNewTitle;
+ m_sTitle = sNewTitle;
+ m_nLeasedNumber = nLeasedNumber;
+ }
+ // <- SYNCHRONIZED
+
+ if (bChanged)
+ impl_sendTitleChangedEvent ();
+}
+
+void TitleHelper::impl_updateTitleForFrame (const css::uno::Reference< css::frame::XFrame >& xFrame, bool init)
+{
+ if ( ! xFrame.is ())
+ return;
+
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ // external title won't be updated internally!
+ // It has to be set from outside new.
+ if (m_bExternalTitle)
+ return;
+ }
+ // <- SYNCHRONIZED
+
+ css::uno::Reference< css::uno::XInterface > xComponent = xFrame->getController ();
+ if ( ! xComponent.is ())
+ xComponent = xFrame->getComponentWindow ();
+
+ OUStringBuffer sTitle (256);
+
+ impl_appendComponentTitle (sTitle, xComponent);
+#ifndef MACOSX
+ // fdo#70376: We want the window title to contain just the
+ // document name (from the above "component title").
+ impl_appendProductName (sTitle);
+ impl_appendModuleName (sTitle);
+ impl_appendDebugVersion (sTitle);
+#endif
+ impl_appendSafeMode (sTitle);
+
+ bool bChanged;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ OUString sNewTitle = sTitle.makeStringAndClear ();
+ bChanged = !init && m_sTitle != sNewTitle;
+ m_sTitle = sNewTitle;
+ }
+ // <- SYNCHRONIZED
+
+ if (bChanged)
+ impl_sendTitleChangedEvent ();
+}
+
+void TitleHelper::impl_appendComponentTitle ( OUStringBuffer& sTitle ,
+ const css::uno::Reference< css::uno::XInterface >& xComponent)
+{
+ css::uno::Reference< css::frame::XTitle > xTitle(xComponent, css::uno::UNO_QUERY);
+
+ // Note: Title has to be used (even if it's empty) if the right interface is supported.
+ if (xTitle.is ())
+ sTitle.append (xTitle->getTitle ());
+}
+
+void TitleHelper::impl_appendProductName (OUStringBuffer& sTitle)
+{
+ OUString name(utl::ConfigManager::getProductName());
+ if (!name.isEmpty())
+ {
+ if (!sTitle.isEmpty())
+ sTitle.append(" - ");
+ sTitle.append(name);
+ }
+}
+
+void TitleHelper::impl_appendModuleName (OUStringBuffer& sTitle)
+{
+ css::uno::Reference< css::uno::XInterface > xOwner;
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xOwner = m_xOwner.get();
+ xContext = m_xContext;
+ }
+ // <- SYNCHRONIZED
+
+ try
+ {
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager =
+ css::frame::ModuleManager::create(xContext);
+
+ const OUString sID = xModuleManager->identify(xOwner);
+ ::comphelper::SequenceAsHashMap lProps = xModuleManager->getByName (sID);
+ const OUString sUIName = lProps.getUnpackedValueOrDefault (OFFICEFACTORY_PROPNAME_ASCII_UINAME, OUString());
+
+ // An UIname property is an optional value !
+ // So please add it to the title in case it does really exists only.
+ if (!sUIName.isEmpty())
+ {
+ sTitle.append (" " );
+ sTitle.append (sUIName);
+ }
+ }
+ catch(const css::uno::Exception&)
+ {}
+}
+
+#ifdef DBG_UTIL
+void TitleHelper::impl_appendDebugVersion (OUStringBuffer& sTitle)
+{
+ OUString version(utl::ConfigManager::getProductVersion());
+ sTitle.append(' ');
+ sTitle.append(version);
+ OUString sVersion = ::utl::Bootstrap::getBuildIdData("development");
+ sTitle.append(" [");
+ sTitle.append(sVersion);
+ sTitle.append("]");
+}
+#else
+void TitleHelper::impl_appendDebugVersion (OUStringBuffer&)
+{
+}
+#endif
+
+void TitleHelper::impl_appendSafeMode (OUStringBuffer& sTitle)
+{
+ if (Application::IsSafeModeEnabled())
+ sTitle.append(FwkResId (STR_SAFEMODE_TITLE));
+}
+
+void TitleHelper::impl_startListeningForModel (const css::uno::Reference< css::frame::XModel >& xModel)
+{
+ css::uno::Reference< css::document::XDocumentEventBroadcaster > xBroadcaster(xModel, css::uno::UNO_QUERY);
+ if ( ! xBroadcaster.is ())
+ return;
+
+ xBroadcaster->addDocumentEventListener (static_cast< css::document::XDocumentEventListener* >(this));
+}
+
+void TitleHelper::impl_startListeningForController (const css::uno::Reference< css::frame::XController >& xController)
+{
+ xController->addEventListener (static_cast< css::lang::XEventListener* > (static_cast< css::frame::XFrameActionListener* > (this) ) );
+ css::uno::Reference< css::frame::XTitle > xSubTitle(xController->getModel (), css::uno::UNO_QUERY);
+ impl_setSubTitle (xSubTitle);
+}
+
+void TitleHelper::impl_startListeningForFrame (const css::uno::Reference< css::frame::XFrame >& xFrame)
+{
+ xFrame->addFrameActionListener(this );
+ impl_updateListeningForFrame (xFrame);
+}
+
+void TitleHelper::impl_updateListeningForFrame (const css::uno::Reference< css::frame::XFrame >& xFrame)
+{
+ css::uno::Reference< css::frame::XTitle > xSubTitle(xFrame->getController (), css::uno::UNO_QUERY);
+ impl_setSubTitle (xSubTitle);
+}
+
+void TitleHelper::impl_setSubTitle (const css::uno::Reference< css::frame::XTitle >& xSubTitle)
+{
+ css::uno::Reference< css::frame::XTitle > xOldSubTitle;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ // ignore duplicate calls. Makes outside using of this helper more easy :-)
+ xOldSubTitle.set(m_xSubTitle.get(), css::uno::UNO_QUERY);
+ if (xOldSubTitle == xSubTitle)
+ return;
+
+ m_xSubTitle = xSubTitle;
+ }
+ // <- SYNCHRONIZED
+
+ css::uno::Reference< css::frame::XTitleChangeBroadcaster > xOldBroadcaster(xOldSubTitle , css::uno::UNO_QUERY );
+ css::uno::Reference< css::frame::XTitleChangeBroadcaster > xNewBroadcaster(xSubTitle , css::uno::UNO_QUERY );
+ css::uno::Reference< css::frame::XTitleChangeListener > xThis(this);
+
+ if (xOldBroadcaster.is())
+ xOldBroadcaster->removeTitleChangeListener (xThis);
+
+ if (xNewBroadcaster.is())
+ xNewBroadcaster->addTitleChangeListener (xThis);
+}
+
+OUString TitleHelper::impl_convertURL2Title(std::u16string_view sURL)
+{
+ INetURLObject aURL (sURL);
+ OUString sTitle;
+
+ if (aURL.GetProtocol() == INetProtocol::File)
+ {
+ if (aURL.HasMark())
+ aURL = INetURLObject(aURL.GetURLNoMark());
+
+ sTitle = aURL.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset);
+ }
+ else
+ {
+ if (aURL.hasExtension())
+ sTitle = aURL.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset);
+
+ if ( sTitle.isEmpty() )
+ sTitle = aURL.GetHostPort(INetURLObject::DecodeMechanism::WithCharset);
+
+ if ( sTitle.isEmpty() )
+ sTitle = aURL.GetURLNoPass(INetURLObject::DecodeMechanism::WithCharset);
+ }
+
+ return sTitle;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/helper/undomanagerhelper.cxx b/framework/source/fwe/helper/undomanagerhelper.cxx
new file mode 100644
index 000000000..779fe02e6
--- /dev/null
+++ b/framework/source/fwe/helper/undomanagerhelper.cxx
@@ -0,0 +1,1056 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/undomanagerhelper.hxx>
+#include <framework/imutex.hxx>
+
+#include <com/sun/star/document/EmptyUndoStackException.hpp>
+#include <com/sun/star/document/UndoContextNotClosedException.hpp>
+#include <com/sun/star/document/UndoFailedException.hpp>
+#include <com/sun/star/document/XUndoManager.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/util/InvalidStateException.hpp>
+#include <com/sun/star/util/NotLockedException.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <comphelper/flagguard.hxx>
+#include <comphelper/asyncnotification.hxx>
+#include <svl/undo.hxx>
+#include <tools/diagnose_ex.h>
+#include <osl/conditn.hxx>
+
+#include <functional>
+#include <mutex>
+#include <stack>
+#include <queue>
+#include <utility>
+
+namespace framework
+{
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::document::XUndoManagerListener;
+ using ::com::sun::star::document::UndoManagerEvent;
+ using ::com::sun::star::document::EmptyUndoStackException;
+ using ::com::sun::star::document::UndoContextNotClosedException;
+ using ::com::sun::star::document::UndoFailedException;
+ using ::com::sun::star::util::NotLockedException;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::document::XUndoAction;
+ using ::com::sun::star::lang::XComponent;
+ using ::com::sun::star::document::XUndoManager;
+ using ::com::sun::star::util::InvalidStateException;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::util::XModifyListener;
+
+ //= UndoActionWrapper
+
+ namespace {
+
+ class UndoActionWrapper : public SfxUndoAction
+ {
+ public:
+ explicit UndoActionWrapper(
+ Reference< XUndoAction > const& i_undoAction
+ );
+ virtual ~UndoActionWrapper() override;
+
+ virtual OUString GetComment() const override;
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual bool CanRepeat(SfxRepeatTarget&) const override;
+
+ private:
+ const Reference< XUndoAction > m_xUndoAction;
+ };
+
+ }
+
+ UndoActionWrapper::UndoActionWrapper( Reference< XUndoAction > const& i_undoAction )
+ : m_xUndoAction( i_undoAction )
+ {
+ ENSURE_OR_THROW( m_xUndoAction.is(), "illegal undo action" );
+ }
+
+ UndoActionWrapper::~UndoActionWrapper()
+ {
+ try
+ {
+ Reference< XComponent > xComponent( m_xUndoAction, UNO_QUERY );
+ if ( xComponent.is() )
+ xComponent->dispose();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ }
+
+ OUString UndoActionWrapper::GetComment() const
+ {
+ OUString sComment;
+ try
+ {
+ sComment = m_xUndoAction->getTitle();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ return sComment;
+ }
+
+ void UndoActionWrapper::Undo()
+ {
+ m_xUndoAction->undo();
+ }
+
+ void UndoActionWrapper::Redo()
+ {
+ m_xUndoAction->redo();
+ }
+
+ bool UndoActionWrapper::CanRepeat(SfxRepeatTarget&) const
+ {
+ return false;
+ }
+
+ //= UndoManagerRequest
+
+ namespace {
+
+ class UndoManagerRequest : public ::comphelper::AnyEvent
+ {
+ public:
+ explicit UndoManagerRequest( ::std::function<void ()> i_request )
+ :m_request(std::move( i_request ))
+ {
+ m_finishCondition.reset();
+ }
+
+ void execute()
+ {
+ try
+ {
+ m_request();
+ }
+ catch( const Exception& )
+ {
+ m_caughtException = ::cppu::getCaughtException();
+ }
+ m_finishCondition.set();
+ }
+
+ void wait()
+ {
+ m_finishCondition.wait();
+ if ( m_caughtException.hasValue() )
+ ::cppu::throwException( m_caughtException );
+ }
+
+ void cancel( const Reference< XInterface >& i_context )
+ {
+ m_caughtException <<= RuntimeException(
+ "Concurrency error: an earlier operation on the stack failed.",
+ i_context
+ );
+ m_finishCondition.set();
+ }
+
+ protected:
+ virtual ~UndoManagerRequest() override
+ {
+ }
+
+ private:
+ ::std::function<void ()> m_request;
+ Any m_caughtException;
+ ::osl::Condition m_finishCondition;
+ };
+
+ }
+
+ //= UndoManagerHelper_Impl
+
+ class UndoManagerHelper_Impl : public SfxUndoListener
+ {
+ private:
+ ::osl::Mutex m_aMutex;
+ std::mutex m_aQueueMutex;
+ bool m_bAPIActionRunning;
+ bool m_bProcessingEvents;
+ sal_Int32 m_nLockCount;
+ ::comphelper::OInterfaceContainerHelper3<XUndoManagerListener> m_aUndoListeners;
+ ::comphelper::OInterfaceContainerHelper3<XModifyListener> m_aModifyListeners;
+ IUndoManagerImplementation& m_rUndoManagerImplementation;
+ ::std::stack< bool > m_aContextVisibilities;
+#if OSL_DEBUG_LEVEL > 0
+ bool m_bContextAPIFlagsEverPushed = {false};
+ ::std::stack< bool > m_aContextAPIFlags;
+#endif
+ ::std::queue< ::rtl::Reference< UndoManagerRequest > >
+ m_aEventQueue;
+
+ public:
+ ::osl::Mutex& getMutex() { return m_aMutex; }
+
+ public:
+ explicit UndoManagerHelper_Impl( IUndoManagerImplementation& i_undoManagerImpl )
+ :m_bAPIActionRunning( false )
+ ,m_bProcessingEvents( false )
+ ,m_nLockCount( 0 )
+ ,m_aUndoListeners( m_aMutex )
+ ,m_aModifyListeners( m_aMutex )
+ ,m_rUndoManagerImplementation( i_undoManagerImpl )
+ {
+ getUndoManager().AddUndoListener( *this );
+ }
+
+ virtual ~UndoManagerHelper_Impl()
+ {
+ }
+
+ SfxUndoManager& getUndoManager() const
+ {
+ return m_rUndoManagerImplementation.getImplUndoManager();
+ }
+
+ Reference< XUndoManager > getXUndoManager() const
+ {
+ return m_rUndoManagerImplementation.getThis();
+ }
+
+ // SfxUndoListener
+ virtual void actionUndone( const OUString& i_actionComment ) override;
+ virtual void actionRedone( const OUString& i_actionComment ) override;
+ virtual void undoActionAdded( const OUString& i_actionComment ) override;
+ virtual void cleared() override;
+ virtual void clearedRedo() override;
+ virtual void resetAll() override;
+ virtual void listActionEntered( const OUString& i_comment ) override;
+ virtual void listActionLeft( const OUString& i_comment ) override;
+ virtual void listActionCancelled() override;
+
+ // public operations
+ void disposing();
+
+ void enterUndoContext( const OUString& i_title, const bool i_hidden, IMutexGuard& i_instanceLock );
+ void leaveUndoContext( IMutexGuard& i_instanceLock );
+ void addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock );
+ void undo( IMutexGuard& i_instanceLock );
+ void redo( IMutexGuard& i_instanceLock );
+ void clear( IMutexGuard& i_instanceLock );
+ void clearRedo( IMutexGuard& i_instanceLock );
+ void reset( IMutexGuard& i_instanceLock );
+
+ void lock();
+ void unlock();
+
+ void addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
+ {
+ m_aUndoListeners.addInterface( i_listener );
+ }
+
+ void removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
+ {
+ m_aUndoListeners.removeInterface( i_listener );
+ }
+
+ void addModifyListener( const Reference< XModifyListener >& i_listener )
+ {
+ m_aModifyListeners.addInterface( i_listener );
+ }
+
+ void removeModifyListener( const Reference< XModifyListener >& i_listener )
+ {
+ m_aModifyListeners.removeInterface( i_listener );
+ }
+
+ UndoManagerEvent
+ buildEvent( OUString const& i_title ) const;
+
+ void impl_notifyModified();
+ void notify( OUString const& i_title,
+ void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& )
+ );
+ void notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ) );
+
+ private:
+ /// adds a function to be called to the request processor's queue
+ void impl_processRequest(::std::function<void ()> const& i_request, IMutexGuard& i_instanceLock);
+
+ /// impl-versions of the XUndoManager API.
+ void impl_enterUndoContext( const OUString& i_title, const bool i_hidden );
+ void impl_leaveUndoContext();
+ void impl_addUndoAction( const Reference< XUndoAction >& i_action );
+ void impl_doUndoRedo( IMutexGuard& i_externalLock, const bool i_undo );
+ void impl_clear();
+ void impl_clearRedo();
+ void impl_reset();
+ };
+
+ void UndoManagerHelper_Impl::disposing()
+ {
+ EventObject aEvent;
+ aEvent.Source = getXUndoManager();
+ m_aUndoListeners.disposeAndClear( aEvent );
+ m_aModifyListeners.disposeAndClear( aEvent );
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ getUndoManager().RemoveUndoListener( *this );
+ }
+
+ UndoManagerEvent UndoManagerHelper_Impl::buildEvent( OUString const& i_title ) const
+ {
+ UndoManagerEvent aEvent;
+ aEvent.Source = getXUndoManager();
+ aEvent.UndoActionTitle = i_title;
+ aEvent.UndoContextDepth = getUndoManager().GetListActionDepth();
+ return aEvent;
+ }
+
+ void UndoManagerHelper_Impl::impl_notifyModified()
+ {
+ const EventObject aEvent( getXUndoManager() );
+ m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvent );
+ }
+
+ void UndoManagerHelper_Impl::notify( OUString const& i_title,
+ void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ) )
+ {
+ const UndoManagerEvent aEvent( buildEvent( i_title ) );
+
+ // TODO: this notification method here is used by UndoManagerHelper_Impl, to multiplex the notifications we
+ // receive from the SfxUndoManager. Those notifications are sent with a locked SolarMutex, which means
+ // we're doing the multiplexing here with a locked SM, too. Which is Bad (TM).
+ // Fixing this properly would require outsourcing all the notifications into an own thread - which might lead
+ // to problems of its own, since clients might expect synchronous notifications.
+
+ m_aUndoListeners.notifyEach( i_notificationMethod, aEvent );
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ) )
+ {
+ const EventObject aEvent( getXUndoManager() );
+
+ // TODO: the same comment as in the other notify, regarding SM locking applies here ...
+
+ m_aUndoListeners.notifyEach( i_notificationMethod, aEvent );
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::enterUndoContext( const OUString& i_title, const bool i_hidden, IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this, &i_title, i_hidden] () { return this->impl_enterUndoContext(i_title, i_hidden); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::leaveUndoContext( IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this] () { return this->impl_leaveUndoContext(); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock )
+ {
+ if ( !i_action.is() )
+ throw IllegalArgumentException(
+ "illegal undo action object",
+ getXUndoManager(),
+ 1
+ );
+
+ impl_processRequest(
+ [this, &i_action] () { return this->impl_addUndoAction(i_action); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::clear( IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this] () { return this->impl_clear(); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::clearRedo( IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this] () { return this->impl_clearRedo(); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::reset( IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this] () { return this->impl_reset(); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::lock()
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( getMutex() );
+
+ if ( ++m_nLockCount == 1 )
+ {
+ SfxUndoManager& rUndoManager = getUndoManager();
+ rUndoManager.EnableUndo( false );
+ }
+ // <--- SYNCHRONIZED
+ }
+
+ void UndoManagerHelper_Impl::unlock()
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( getMutex() );
+
+ if ( m_nLockCount == 0 )
+ throw NotLockedException( "Undo manager is not locked", getXUndoManager() );
+
+ if ( --m_nLockCount == 0 )
+ {
+ SfxUndoManager& rUndoManager = getUndoManager();
+ rUndoManager.EnableUndo( true );
+ }
+ // <--- SYNCHRONIZED
+ }
+
+ void UndoManagerHelper_Impl::impl_processRequest(::std::function<void ()> const& i_request, IMutexGuard& i_instanceLock)
+ {
+ // create the request, and add it to our queue
+ ::rtl::Reference< UndoManagerRequest > pRequest( new UndoManagerRequest( i_request ) );
+ {
+ std::unique_lock aQueueGuard( m_aQueueMutex );
+ m_aEventQueue.push( pRequest );
+ }
+
+ i_instanceLock.clear();
+
+ if ( m_bProcessingEvents )
+ {
+ // another thread is processing the event queue currently => it will also process the event which we just added
+ pRequest->wait();
+ return;
+ }
+
+ m_bProcessingEvents = true;
+ do
+ {
+ pRequest.clear();
+ {
+ std::unique_lock aQueueGuard( m_aQueueMutex );
+ if ( m_aEventQueue.empty() )
+ {
+ // reset the flag before releasing the queue mutex, otherwise it's possible that another thread
+ // could add an event after we release the mutex, but before we reset the flag. If then this other
+ // thread checks the flag before be reset it, this thread's event would starve.
+ m_bProcessingEvents = false;
+ return;
+ }
+ pRequest = m_aEventQueue.front();
+ m_aEventQueue.pop();
+ }
+ try
+ {
+ pRequest->execute();
+ pRequest->wait();
+ }
+ catch( ... )
+ {
+ {
+ // no chance to process further requests, if the current one failed
+ // => discard them
+ std::unique_lock aQueueGuard( m_aQueueMutex );
+ while ( !m_aEventQueue.empty() )
+ {
+ pRequest = m_aEventQueue.front();
+ m_aEventQueue.pop();
+ pRequest->cancel( getXUndoManager() );
+ }
+ m_bProcessingEvents = false;
+ }
+ // re-throw the error
+ throw;
+ }
+ }
+ while ( true );
+ }
+
+ void UndoManagerHelper_Impl::impl_enterUndoContext( const OUString& i_title, const bool i_hidden )
+ {
+ // SYNCHRONIZED --->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ if ( !rUndoManager.IsUndoEnabled() )
+ // ignore this request if the manager is locked
+ return;
+
+ if ( i_hidden && ( rUndoManager.GetUndoActionCount() == 0 ) )
+ throw EmptyUndoStackException(
+ "can't enter a hidden context without a previous Undo action",
+ m_rUndoManagerImplementation.getThis()
+ );
+
+ {
+ ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
+ rUndoManager.EnterListAction( i_title, OUString(), 0, ViewShellId(-1) );
+ }
+
+ m_aContextVisibilities.push( i_hidden );
+
+ const UndoManagerEvent aEvent( buildEvent( i_title ) );
+ aGuard.clear();
+ // <--- SYNCHRONIZED
+
+ m_aUndoListeners.notifyEach( i_hidden ? &XUndoManagerListener::enteredHiddenContext : &XUndoManagerListener::enteredContext, aEvent );
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::impl_leaveUndoContext()
+ {
+ // SYNCHRONIZED --->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ if ( !rUndoManager.IsUndoEnabled() )
+ // ignore this request if the manager is locked
+ return;
+
+ if ( !rUndoManager.IsInListAction() )
+ throw InvalidStateException(
+ "no active undo context",
+ getXUndoManager()
+ );
+
+ size_t nContextElements = 0;
+
+ const bool isHiddenContext = m_aContextVisibilities.top();
+ m_aContextVisibilities.pop();
+
+ const bool bHadRedoActions = ( rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ) > 0 );
+ {
+ ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
+ if ( isHiddenContext )
+ nContextElements = rUndoManager.LeaveAndMergeListAction();
+ else
+ nContextElements = rUndoManager.LeaveListAction();
+ }
+ const bool bHasRedoActions = ( rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ) > 0 );
+
+ // prepare notification
+ void ( SAL_CALL XUndoManagerListener::*notificationMethod )( const UndoManagerEvent& ) = nullptr;
+
+ UndoManagerEvent aContextEvent( buildEvent( OUString() ) );
+ const EventObject aClearedEvent( getXUndoManager() );
+ if ( nContextElements == 0 )
+ {
+ notificationMethod = &XUndoManagerListener::cancelledContext;
+ }
+ else if ( isHiddenContext )
+ {
+ notificationMethod = &XUndoManagerListener::leftHiddenContext;
+ }
+ else
+ {
+ aContextEvent.UndoActionTitle = rUndoManager.GetUndoActionComment();
+ notificationMethod = &XUndoManagerListener::leftContext;
+ }
+
+ aGuard.clear();
+ // <--- SYNCHRONIZED
+
+ if ( bHadRedoActions && !bHasRedoActions )
+ m_aUndoListeners.notifyEach( &XUndoManagerListener::redoActionsCleared, aClearedEvent );
+ m_aUndoListeners.notifyEach( notificationMethod, aContextEvent );
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::impl_doUndoRedo( IMutexGuard& i_externalLock, const bool i_undo )
+ {
+ ::osl::Guard< ::framework::IMutex > aExternalGuard( i_externalLock.getGuardedMutex() );
+ // note that this assumes that the mutex has been released in the thread which added the
+ // Undo/Redo request, so we can successfully acquire it
+
+ // SYNCHRONIZED --->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ if ( rUndoManager.IsInListAction() )
+ throw UndoContextNotClosedException( OUString(), getXUndoManager() );
+
+ const size_t nElements = i_undo
+ ? rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel )
+ : rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel );
+ if ( nElements == 0 )
+ throw EmptyUndoStackException("stack is empty", getXUndoManager() );
+
+ aGuard.clear();
+ // <--- SYNCHRONIZED
+
+ try
+ {
+ if ( i_undo )
+ rUndoManager.Undo();
+ else
+ rUndoManager.Redo();
+ }
+ catch( const RuntimeException& ) { /* allowed to leave here */ throw; }
+ catch( const UndoFailedException& ) { /* allowed to leave here */ throw; }
+ catch( const Exception& )
+ {
+ // not allowed to leave
+ const Any aError( ::cppu::getCaughtException() );
+ throw UndoFailedException( OUString(), getXUndoManager(), aError );
+ }
+
+ // note that in opposite to all of the other methods, we do *not* have our mutex locked when calling
+ // into the SfxUndoManager implementation. This ensures that an actual XUndoAction::undo/redo is also
+ // called without our mutex being locked.
+ // As a consequence, we do not set m_bAPIActionRunning here. Instead, our actionUndone/actionRedone methods
+ // *always* multiplex the event to our XUndoManagerListeners, not only when m_bAPIActionRunning is FALSE (This
+ // again is different from all other SfxUndoListener methods).
+ // So, we do not need to do this notification here ourself.
+ }
+
+ void UndoManagerHelper_Impl::impl_addUndoAction( const Reference< XUndoAction >& i_action )
+ {
+ // SYNCHRONIZED --->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ if ( !rUndoManager.IsUndoEnabled() )
+ // ignore the request if the manager is locked
+ return;
+
+ const UndoManagerEvent aEventAdd( buildEvent( i_action->getTitle() ) );
+ const EventObject aEventClear( getXUndoManager() );
+
+ const bool bHadRedoActions = ( rUndoManager.GetRedoActionCount() > 0 );
+ {
+ ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
+ rUndoManager.AddUndoAction( std::make_unique<UndoActionWrapper>( i_action ) );
+ }
+ const bool bHasRedoActions = ( rUndoManager.GetRedoActionCount() > 0 );
+
+ aGuard.clear();
+ // <--- SYNCHRONIZED
+
+ m_aUndoListeners.notifyEach( &XUndoManagerListener::undoActionAdded, aEventAdd );
+ if ( bHadRedoActions && !bHasRedoActions )
+ m_aUndoListeners.notifyEach( &XUndoManagerListener::redoActionsCleared, aEventClear );
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::impl_clear()
+ {
+ // SYNCHRONIZED --->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ if ( rUndoManager.IsInListAction() )
+ throw UndoContextNotClosedException( OUString(), getXUndoManager() );
+
+ {
+ ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
+ rUndoManager.Clear();
+ }
+
+ const EventObject aEvent( getXUndoManager() );
+ aGuard.clear();
+ // <--- SYNCHRONIZED
+
+ m_aUndoListeners.notifyEach( &XUndoManagerListener::allActionsCleared, aEvent );
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::impl_clearRedo()
+ {
+ // SYNCHRONIZED --->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ if ( rUndoManager.IsInListAction() )
+ throw UndoContextNotClosedException( OUString(), getXUndoManager() );
+
+ {
+ ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
+ rUndoManager.ClearRedo();
+ }
+
+ const EventObject aEvent( getXUndoManager() );
+ aGuard.clear();
+ // <--- SYNCHRONIZED
+
+ m_aUndoListeners.notifyEach( &XUndoManagerListener::redoActionsCleared, aEvent );
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::impl_reset()
+ {
+ // SYNCHRONIZED --->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ {
+ ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
+ rUndoManager.Reset();
+ }
+
+ const EventObject aEvent( getXUndoManager() );
+ aGuard.clear();
+ // <--- SYNCHRONIZED
+
+ m_aUndoListeners.notifyEach( &XUndoManagerListener::resetAll, aEvent );
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::actionUndone( const OUString& i_actionComment )
+ {
+ UndoManagerEvent aEvent;
+ aEvent.Source = getXUndoManager();
+ aEvent.UndoActionTitle = i_actionComment;
+ aEvent.UndoContextDepth = 0; // Undo can happen on level 0 only
+ m_aUndoListeners.notifyEach( &XUndoManagerListener::actionUndone, aEvent );
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::actionRedone( const OUString& i_actionComment )
+ {
+ UndoManagerEvent aEvent;
+ aEvent.Source = getXUndoManager();
+ aEvent.UndoActionTitle = i_actionComment;
+ aEvent.UndoContextDepth = 0; // Redo can happen on level 0 only
+ m_aUndoListeners.notifyEach( &XUndoManagerListener::actionRedone, aEvent );
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::undoActionAdded( const OUString& i_actionComment )
+ {
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( i_actionComment, &XUndoManagerListener::undoActionAdded );
+ }
+
+ void UndoManagerHelper_Impl::cleared()
+ {
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( &XUndoManagerListener::allActionsCleared );
+ }
+
+ void UndoManagerHelper_Impl::clearedRedo()
+ {
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( &XUndoManagerListener::redoActionsCleared );
+ }
+
+ void UndoManagerHelper_Impl::resetAll()
+ {
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( &XUndoManagerListener::resetAll );
+ }
+
+ void UndoManagerHelper_Impl::listActionEntered( const OUString& i_comment )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ m_aContextAPIFlags.push( m_bAPIActionRunning );
+ m_bContextAPIFlagsEverPushed = true;
+#endif
+
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( i_comment, &XUndoManagerListener::enteredContext );
+ }
+
+ void UndoManagerHelper_Impl::listActionLeft( const OUString& i_comment )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ // It may happen that the very first event listener is added during a
+ // list action after listActionEntered() was already called, e.g. Calc
+ // formula calculation event listener during the input of the very
+ // first formula. Instead of checking m_aContextAPIFlags for empty,
+ // still assert (on calling top()) other stack mismatches but ignore
+ // this one case. See tdf#142980
+ if (m_bContextAPIFlagsEverPushed)
+ {
+ const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top();
+ m_aContextAPIFlags.pop();
+ OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionLeft: API and non-API contexts interwoven!" );
+ }
+#endif
+
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( i_comment, &XUndoManagerListener::leftContext );
+ }
+
+ void UndoManagerHelper_Impl::listActionCancelled()
+ {
+#if OSL_DEBUG_LEVEL > 0
+ const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top();
+ m_aContextAPIFlags.pop();
+ OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionCancelled: API and non-API contexts interwoven!" );
+#endif
+
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( OUString(), &XUndoManagerListener::cancelledContext );
+ }
+
+ //= UndoManagerHelper
+
+ UndoManagerHelper::UndoManagerHelper( IUndoManagerImplementation& i_undoManagerImpl )
+ :m_xImpl( new UndoManagerHelper_Impl( i_undoManagerImpl ) )
+ {
+ }
+
+ UndoManagerHelper::~UndoManagerHelper()
+ {
+ }
+
+ void UndoManagerHelper::disposing()
+ {
+ m_xImpl->disposing();
+ }
+
+ void UndoManagerHelper::enterUndoContext( const OUString& i_title, IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->enterUndoContext( i_title, false, i_instanceLock );
+ }
+
+ void UndoManagerHelper::enterHiddenUndoContext( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->enterUndoContext( OUString(), true, i_instanceLock );
+ }
+
+ void UndoManagerHelper::leaveUndoContext( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->leaveUndoContext( i_instanceLock );
+ }
+
+ void UndoManagerHelper_Impl::undo( IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this, &i_instanceLock] () { return this->impl_doUndoRedo(i_instanceLock, true); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::redo( IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this, &i_instanceLock] () { return this->impl_doUndoRedo(i_instanceLock, false); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper::addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->addUndoAction( i_action, i_instanceLock );
+ }
+
+ void UndoManagerHelper::undo( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->undo( i_instanceLock );
+ }
+
+ void UndoManagerHelper::redo( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->redo( i_instanceLock );
+ }
+
+ bool UndoManagerHelper::isUndoPossible() const
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( m_xImpl->getMutex() );
+ SfxUndoManager& rUndoManager = m_xImpl->getUndoManager();
+ if ( rUndoManager.IsInListAction() )
+ return false;
+ return rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel ) > 0;
+ // <--- SYNCHRONIZED
+ }
+
+ bool UndoManagerHelper::isRedoPossible() const
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( m_xImpl->getMutex() );
+ const SfxUndoManager& rUndoManager = m_xImpl->getUndoManager();
+ if ( rUndoManager.IsInListAction() )
+ return false;
+ return rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ) > 0;
+ // <--- SYNCHRONIZED
+ }
+
+ namespace
+ {
+
+ OUString lcl_getCurrentActionTitle( UndoManagerHelper_Impl& i_impl, const bool i_undo )
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( i_impl.getMutex() );
+
+ const SfxUndoManager& rUndoManager = i_impl.getUndoManager();
+ const size_t nActionCount = i_undo
+ ? rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel )
+ : rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel );
+ if ( nActionCount == 0 )
+ throw EmptyUndoStackException(
+ i_undo ? OUString( "no action on the undo stack" )
+ : OUString( "no action on the redo stack" ),
+ i_impl.getXUndoManager()
+ );
+ return i_undo
+ ? rUndoManager.GetUndoActionComment( 0, SfxUndoManager::TopLevel )
+ : rUndoManager.GetRedoActionComment( 0, SfxUndoManager::TopLevel );
+ // <--- SYNCHRONIZED
+ }
+
+ Sequence< OUString > lcl_getAllActionTitles( UndoManagerHelper_Impl& i_impl, const bool i_undo )
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( i_impl.getMutex() );
+
+ const SfxUndoManager& rUndoManager = i_impl.getUndoManager();
+ const size_t nCount = i_undo
+ ? rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel )
+ : rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel );
+
+ Sequence< OUString > aTitles( nCount );
+ auto aTitlesRange = asNonConstRange(aTitles);
+ for ( size_t i=0; i<nCount; ++i )
+ {
+ aTitlesRange[i] = i_undo
+ ? rUndoManager.GetUndoActionComment( i, SfxUndoManager::TopLevel )
+ : rUndoManager.GetRedoActionComment( i, SfxUndoManager::TopLevel );
+ }
+ return aTitles;
+ // <--- SYNCHRONIZED
+ }
+ }
+
+ OUString UndoManagerHelper::getCurrentUndoActionTitle() const
+ {
+ return lcl_getCurrentActionTitle( *m_xImpl, true );
+ }
+
+ OUString UndoManagerHelper::getCurrentRedoActionTitle() const
+ {
+ return lcl_getCurrentActionTitle( *m_xImpl, false );
+ }
+
+ Sequence< OUString > UndoManagerHelper::getAllUndoActionTitles() const
+ {
+ return lcl_getAllActionTitles( *m_xImpl, true );
+ }
+
+ Sequence< OUString > UndoManagerHelper::getAllRedoActionTitles() const
+ {
+ return lcl_getAllActionTitles( *m_xImpl, false );
+ }
+
+ void UndoManagerHelper::clear( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->clear( i_instanceLock );
+ }
+
+ void UndoManagerHelper::clearRedo( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->clearRedo( i_instanceLock );
+ }
+
+ void UndoManagerHelper::reset( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->reset( i_instanceLock );
+ }
+
+ void UndoManagerHelper::lock()
+ {
+ m_xImpl->lock();
+ }
+
+ void UndoManagerHelper::unlock()
+ {
+ m_xImpl->unlock();
+ }
+
+ bool UndoManagerHelper::isLocked()
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( m_xImpl->getMutex() );
+
+ SfxUndoManager& rUndoManager = m_xImpl->getUndoManager();
+ return !rUndoManager.IsUndoEnabled();
+ // <--- SYNCHRONIZED
+ }
+
+ void UndoManagerHelper::addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
+ {
+ if ( i_listener.is() )
+ m_xImpl->addUndoManagerListener( i_listener );
+ }
+
+ void UndoManagerHelper::removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
+ {
+ if ( i_listener.is() )
+ m_xImpl->removeUndoManagerListener( i_listener );
+ }
+
+ void UndoManagerHelper::addModifyListener( const Reference< XModifyListener >& i_listener )
+ {
+ if ( i_listener.is() )
+ m_xImpl->addModifyListener( i_listener );
+ }
+
+ void UndoManagerHelper::removeModifyListener( const Reference< XModifyListener >& i_listener )
+ {
+ if ( i_listener.is() )
+ m_xImpl->removeModifyListener( i_listener );
+ }
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/menuconfiguration.cxx b/framework/source/fwe/xml/menuconfiguration.cxx
new file mode 100644
index 000000000..d2131c8a8
--- /dev/null
+++ b/framework/source/fwe/xml/menuconfiguration.cxx
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <menuconfiguration.hxx>
+
+#include <addonmenu.hxx>
+#include <utility>
+#include <xml/menudocumenthandler.hxx>
+#include <xml/saxnamespacefilter.hxx>
+
+#include <uielement/rootitemcontainer.hxx>
+
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::io;
+
+namespace framework
+{
+
+MenuConfiguration::MenuConfiguration( css::uno::Reference< css::uno::XComponentContext > xContext )
+: m_xContext(std::move( xContext ))
+{
+}
+
+MenuConfiguration::~MenuConfiguration()
+{
+}
+
+Reference< XIndexAccess > MenuConfiguration::CreateMenuBarConfigurationFromXML(
+ Reference< XInputStream > const & rInputStream )
+{
+ Reference< XParser > xParser = Parser::create( m_xContext );
+
+ // connect stream to input stream to the parser
+ InputSource aInputSource;
+
+ aInputSource.aInputStream = rInputStream;
+
+ // create menu bar
+ Reference< XIndexContainer > xItemContainer( new RootItemContainer() );
+
+ // create namespace filter and set menudocument handler inside to support xml namespaces
+
+ Reference< XDocumentHandler > xDocHandler( new OReadMenuDocumentHandler( xItemContainer ));
+
+ Reference< XDocumentHandler > xFilter( new SaxNamespaceFilter( xDocHandler ));
+
+ // connect parser and filter
+ xParser->setDocumentHandler( xFilter );
+
+ try
+ {
+ xParser->parseStream( aInputSource );
+ return xItemContainer;
+ }
+ catch ( const RuntimeException& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx );
+ }
+ catch( const SAXException& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ SAXException aWrappedSAXException;
+
+ if ( !( e.WrappedException >>= aWrappedSAXException ))
+ throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx );
+ else
+ throw WrappedTargetException( aWrappedSAXException.Message, Reference< XInterface >(), e.WrappedException );
+ }
+ catch( const css::io::IOException& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx );
+ }
+}
+
+void MenuConfiguration::StoreMenuBarConfigurationToXML(
+ Reference< XIndexAccess > const & rMenuBarConfiguration,
+ Reference< XOutputStream > const & rOutputStream, bool bIsMenuBar )
+{
+ Reference< XWriter > xWriter = Writer::create(m_xContext);
+ xWriter->setOutputStream( rOutputStream );
+
+ try
+ {
+ OWriteMenuDocumentHandler aWriteMenuDocumentHandler( rMenuBarConfiguration, xWriter, bIsMenuBar );
+ aWriteMenuDocumentHandler.WriteMenuDocument();
+ }
+ catch ( const RuntimeException& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx );
+ }
+ catch ( const SAXException& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx );
+ }
+ catch ( const css::io::IOException& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx );
+ }
+}
+
+void* MenuAttributes::CreateAttribute(const OUString& rFrame, const OUString& rImageIdStr)
+{
+ MenuAttributes* pAttributes = new MenuAttributes(rFrame, rImageIdStr);
+ pAttributes->acquire();
+ return pAttributes;
+}
+
+void* MenuAttributes::CreateAttribute(const css::uno::WeakReference<css::frame::XDispatchProvider>& rDispatchProvider)
+{
+ MenuAttributes* pAttributes = new MenuAttributes(rDispatchProvider);
+ pAttributes->acquire();
+ return pAttributes;
+}
+
+void MenuAttributes::ReleaseAttribute(void* nAttributePtr)
+{
+ if (!nAttributePtr)
+ return;
+ MenuAttributes* pAttributes = static_cast<MenuAttributes*>(nAttributePtr);
+ pAttributes->release();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/menudocumenthandler.cxx b/framework/source/fwe/xml/menudocumenthandler.cxx
new file mode 100644
index 000000000..cfb4e9f97
--- /dev/null
+++ b/framework/source/fwe/xml/menudocumenthandler.cxx
@@ -0,0 +1,897 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/macros.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <xml/menudocumenthandler.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <com/sun/star/ui/ItemStyle.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/attributelist.hxx>
+
+#ifdef ATTRIBUTE_HELPID
+#undef ATTRIBUTE_HELPID
+#endif
+
+constexpr OUStringLiteral XMLNS_MENU = u"http://openoffice.org/2001/menu";
+
+constexpr OUStringLiteral ELEMENT_MENUBAR = u"http://openoffice.org/2001/menu^menubar";
+constexpr OUStringLiteral ELEMENT_MENU = u"http://openoffice.org/2001/menu^menu";
+constexpr OUStringLiteral ELEMENT_MENUPOPUP = u"http://openoffice.org/2001/menu^menupopup";
+constexpr OUStringLiteral ELEMENT_MENUITEM = u"http://openoffice.org/2001/menu^menuitem";
+constexpr OUStringLiteral ELEMENT_MENUSEPARATOR = u"http://openoffice.org/2001/menu^menuseparator";
+
+constexpr OUStringLiteral ELEMENT_NS_MENUBAR = u"menu:menubar";
+constexpr OUStringLiteral ELEMENT_NS_MENU = u"menu:menu";
+constexpr OUStringLiteral ELEMENT_NS_MENUPOPUP = u"menu:menupopup";
+constexpr OUStringLiteral ELEMENT_NS_MENUITEM = u"menu:menuitem";
+constexpr OUStringLiteral ELEMENT_NS_MENUSEPARATOR = u"menu:menuseparator";
+
+constexpr OUStringLiteral ATTRIBUTE_ID = u"http://openoffice.org/2001/menu^id";
+constexpr OUStringLiteral ATTRIBUTE_LABEL = u"http://openoffice.org/2001/menu^label";
+constexpr OUStringLiteral ATTRIBUTE_HELPID = u"http://openoffice.org/2001/menu^helpid";
+constexpr OUStringLiteral ATTRIBUTE_STYLE = u"http://openoffice.org/2001/menu^style";
+
+constexpr OUStringLiteral ATTRIBUTE_NS_ID = u"menu:id";
+constexpr OUStringLiteral ATTRIBUTE_NS_LABEL = u"menu:label";
+constexpr OUStringLiteral ATTRIBUTE_NS_HELPID = u"menu:helpid";
+constexpr OUStringLiteral ATTRIBUTE_NS_STYLE = u"menu:style";
+
+constexpr OUStringLiteral ATTRIBUTE_XMLNS_MENU = u"xmlns:menu";
+
+constexpr OUStringLiteral ATTRIBUTE_TYPE_CDATA = u"CDATA";
+
+constexpr OUStringLiteral MENUBAR_DOCTYPE = u"<!DOCTYPE menu:menubar PUBLIC \"-//OpenOffice.org//DTD OfficeDocument 1.0//EN\" \"menubar.dtd\">";
+
+#define ATTRIBUTE_ITEMSTYLE_TEXT "text"
+#define ATTRIBUTE_ITEMSTYLE_IMAGE "image"
+#define ATTRIBUTE_ITEMSTYLE_RADIO "radio"
+
+// Property names of a menu/menu item ItemDescriptor
+constexpr OUStringLiteral ITEM_DESCRIPTOR_COMMANDURL = u"CommandURL";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_HELPURL = u"HelpURL";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_CONTAINER = u"ItemDescriptorContainer";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_LABEL = u"Label";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_TYPE = u"Type";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_STYLE = u"Style";
+
+// using namespaces
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::ui;
+
+namespace framework
+{
+
+namespace {
+
+struct MenuStyleItem
+{
+ sal_Int16 nBit;
+ const char* attrName;
+};
+
+}
+
+const MenuStyleItem MenuItemStyles[ ] = {
+ { css::ui::ItemStyle::ICON, ATTRIBUTE_ITEMSTYLE_IMAGE },
+ { css::ui::ItemStyle::TEXT, ATTRIBUTE_ITEMSTYLE_TEXT },
+ { css::ui::ItemStyle::RADIO_CHECK, ATTRIBUTE_ITEMSTYLE_RADIO }
+};
+
+sal_Int32 const nMenuStyleItemEntries = SAL_N_ELEMENTS(MenuItemStyles);
+
+static void ExtractMenuParameters( const Sequence< PropertyValue >& rProp,
+ OUString& rCommandURL,
+ OUString& rLabel,
+ OUString& rHelpURL,
+ Reference< XIndexAccess >& rSubMenu,
+ sal_Int16& rType,
+ sal_Int16& rStyle )
+{
+ for ( const PropertyValue& p : rProp )
+ {
+ if ( p.Name == ITEM_DESCRIPTOR_COMMANDURL )
+ {
+ p.Value >>= rCommandURL;
+ rCommandURL = rCommandURL.intern();
+ }
+ else if ( p.Name == ITEM_DESCRIPTOR_HELPURL )
+ {
+ p.Value >>= rHelpURL;
+ }
+ else if ( p.Name == ITEM_DESCRIPTOR_CONTAINER )
+ {
+ p.Value >>= rSubMenu;
+ }
+ else if ( p.Name == ITEM_DESCRIPTOR_LABEL )
+ {
+ p.Value >>= rLabel;
+ }
+ else if ( p.Name == ITEM_DESCRIPTOR_TYPE )
+ {
+ p.Value >>= rType;
+ }
+ else if ( p.Name == ITEM_DESCRIPTOR_STYLE )
+ {
+ p.Value >>= rStyle;
+ }
+ }
+}
+
+// Base class implementation
+
+ReadMenuDocumentHandlerBase::ReadMenuDocumentHandlerBase() :
+ m_aType( ITEM_DESCRIPTOR_TYPE ),
+ m_aLabel( ITEM_DESCRIPTOR_LABEL ),
+ m_aContainer( ITEM_DESCRIPTOR_CONTAINER ),
+ m_aHelpURL( ITEM_DESCRIPTOR_HELPURL ),
+ m_aCommandURL( ITEM_DESCRIPTOR_COMMANDURL ),
+ m_aStyle( ITEM_DESCRIPTOR_STYLE )
+{
+}
+
+ReadMenuDocumentHandlerBase::~ReadMenuDocumentHandlerBase()
+{
+}
+
+void SAL_CALL ReadMenuDocumentHandlerBase::ignorableWhitespace(
+ const OUString& )
+{
+}
+
+void SAL_CALL ReadMenuDocumentHandlerBase::processingInstruction(
+ const OUString& /*aTarget*/, const OUString& /*aData*/ )
+{
+}
+
+void SAL_CALL ReadMenuDocumentHandlerBase::setDocumentLocator(
+ const Reference< XLocator > &xLocator)
+{
+ m_xLocator = xLocator;
+}
+
+OUString ReadMenuDocumentHandlerBase::getErrorLineString()
+{
+ if ( m_xLocator.is() )
+ return "Line: " + OUString::number( m_xLocator->getLineNumber() ) + " - ";
+ else
+ return OUString();
+}
+
+void ReadMenuDocumentHandlerBase::initPropertyCommon(
+ Sequence< PropertyValue > &rProps, const OUString &rCommandURL,
+ const OUString &rHelpId, const OUString &rLabel, sal_Int16 nItemStyleBits )
+{
+ auto pProps = rProps.getArray();
+
+ pProps[0].Name = m_aCommandURL;
+ pProps[1].Name = m_aHelpURL;
+ pProps[2].Name = m_aContainer;
+ pProps[3].Name = m_aLabel;
+ pProps[4].Name = m_aStyle;
+ pProps[5].Name = m_aType;
+
+ // Common values
+ pProps[0].Value <<= rCommandURL.intern();
+ pProps[1].Value <<= rHelpId;
+ pProps[2].Value <<= Reference< XIndexContainer >();
+ pProps[3].Value <<= rLabel;
+ pProps[4].Value <<= nItemStyleBits;
+ pProps[5].Value <<= css::ui::ItemType::DEFAULT;
+}
+
+OReadMenuDocumentHandler::OReadMenuDocumentHandler(
+ const Reference< XIndexContainer >& rMenuBarContainer )
+: m_nElementDepth( 0 ),
+ m_eReaderMode( ReaderMode::None ),
+ m_xMenuBarContainer( rMenuBarContainer ),
+ m_xContainerFactory( rMenuBarContainer, UNO_QUERY )
+{
+}
+
+OReadMenuDocumentHandler::~OReadMenuDocumentHandler()
+{
+}
+
+void SAL_CALL OReadMenuDocumentHandler::startDocument()
+{
+}
+
+void SAL_CALL OReadMenuDocumentHandler::endDocument()
+{
+ if ( m_nElementDepth > 0 )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "A closing element is missing!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+}
+
+void SAL_CALL OReadMenuDocumentHandler::startElement(
+ const OUString& aName, const Reference< XAttributeList > &xAttrList )
+{
+ if ( m_eReaderMode != ReaderMode::None )
+ {
+ ++m_nElementDepth;
+ m_xReader->startElement( aName, xAttrList );
+ }
+ else
+ {
+ if ( aName == ELEMENT_MENUBAR )
+ {
+ m_eReaderMode = ReaderMode::MenuBar;
+ m_xReader.set( new OReadMenuBarHandler( m_xMenuBarContainer, m_xContainerFactory ));
+ }
+ else if ( aName == ELEMENT_MENUPOPUP )
+ {
+ m_eReaderMode = ReaderMode::MenuPopup;
+ m_xReader.set( new OReadMenuPopupHandler( m_xMenuBarContainer, m_xContainerFactory ));
+ }
+ ++m_nElementDepth;
+ m_xReader->startDocument();
+ }
+}
+
+void SAL_CALL OReadMenuDocumentHandler::characters(const OUString&)
+{
+}
+
+void SAL_CALL OReadMenuDocumentHandler::endElement( const OUString& aName )
+{
+ if ( m_eReaderMode == ReaderMode::None )
+ return;
+
+ --m_nElementDepth;
+ m_xReader->endElement( aName );
+ if ( 0 != m_nElementDepth )
+ return;
+
+ m_xReader->endDocument();
+ m_xReader.clear();
+ if ( m_eReaderMode == ReaderMode::MenuBar && aName != ELEMENT_MENUBAR )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menubar expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ else if ( m_eReaderMode == ReaderMode::MenuPopup && aName != ELEMENT_MENUPOPUP )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menupopup expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ m_eReaderMode = ReaderMode::None;
+}
+
+OReadMenuBarHandler::OReadMenuBarHandler(
+ const Reference< XIndexContainer >& rMenuBarContainer,
+ const Reference< XSingleComponentFactory >& rFactory )
+: m_nElementDepth( 0 ),
+ m_bMenuMode( false ),
+ m_xMenuBarContainer( rMenuBarContainer ),
+ m_xContainerFactory( rFactory )
+{
+}
+
+OReadMenuBarHandler::~OReadMenuBarHandler()
+{
+}
+
+void SAL_CALL OReadMenuBarHandler::startDocument()
+{
+}
+
+void SAL_CALL OReadMenuBarHandler::endDocument()
+{
+}
+
+void SAL_CALL OReadMenuBarHandler::startElement(
+ const OUString& rName, const Reference< XAttributeList > &xAttrList )
+{
+ if ( m_bMenuMode )
+ {
+ ++m_nElementDepth;
+ m_xReader->startElement( rName, xAttrList );
+ }
+ else if ( rName == ELEMENT_MENU )
+ {
+ ++m_nElementDepth;
+
+ OUString aHelpId;
+ OUString aCommandId;
+ OUString aLabel;
+ sal_Int16 nItemBits(0);
+
+ m_bMenuMode = true;
+
+ // Container must be factory to create sub container
+ Reference< XComponentContext > xComponentContext(
+ comphelper::getProcessComponentContext() );
+
+ Reference< XIndexContainer > xSubItemContainer;
+ if ( m_xContainerFactory.is() )
+ xSubItemContainer.set( m_xContainerFactory->createInstanceWithContext( xComponentContext ), UNO_QUERY );
+
+ if ( xSubItemContainer.is() )
+ {
+ // read attributes for menu
+ for ( sal_Int16 i=0; i< xAttrList->getLength(); i++ )
+ {
+ OUString aName = xAttrList->getNameByIndex( i );
+ const OUString aValue = xAttrList->getValueByIndex( i );
+ if ( aName == ATTRIBUTE_ID )
+ aCommandId = aValue;
+ else if ( aName == ATTRIBUTE_LABEL )
+ aLabel = aValue;
+ else if ( aName == ATTRIBUTE_HELPID )
+ aHelpId = aValue;
+ else if ( aName == ATTRIBUTE_STYLE )
+ {
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aToken = aValue.getToken( 0, '+', nIndex );
+ if ( !aToken.isEmpty() )
+ {
+ if ( aToken == ATTRIBUTE_ITEMSTYLE_TEXT )
+ nItemBits |= css::ui::ItemStyle::TEXT;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_IMAGE )
+ nItemBits |= css::ui::ItemStyle::ICON;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_RADIO )
+ nItemBits |= css::ui::ItemStyle::RADIO_CHECK;
+ }
+ }
+ while ( nIndex >= 0 );
+ }
+ }
+
+ if ( !aCommandId.isEmpty() )
+ {
+ Sequence< PropertyValue > aSubMenuProp( 6 );
+ initPropertyCommon( aSubMenuProp, aCommandId, aHelpId, aLabel, nItemBits );
+ aSubMenuProp.getArray()[2].Value <<= xSubItemContainer;
+
+ m_xMenuBarContainer->insertByIndex( m_xMenuBarContainer->getCount(), Any( aSubMenuProp ) );
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "attribute id for element menu required!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_xReader.set( new OReadMenuHandler( xSubItemContainer, m_xContainerFactory ));
+ m_xReader->startDocument();
+ }
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "element menu expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+}
+
+void SAL_CALL OReadMenuBarHandler::characters(const OUString&)
+{
+}
+
+void OReadMenuBarHandler::endElement( const OUString& aName )
+{
+ if ( !m_bMenuMode )
+ return;
+
+ --m_nElementDepth;
+ if ( 0 == m_nElementDepth )
+ {
+ m_xReader->endDocument();
+ m_xReader.clear();
+ m_bMenuMode = false;
+ if ( aName != ELEMENT_MENU )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menu expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ else
+ m_xReader->endElement( aName );
+}
+
+OReadMenuHandler::OReadMenuHandler(
+ const Reference< XIndexContainer >& rMenuContainer,
+ const Reference< XSingleComponentFactory >& rFactory ) :
+ m_nElementDepth( 0 ),
+ m_bMenuPopupMode( false ),
+ m_xMenuContainer( rMenuContainer ),
+ m_xContainerFactory( rFactory )
+{
+}
+
+OReadMenuHandler::~OReadMenuHandler()
+{
+}
+
+void SAL_CALL OReadMenuHandler::startDocument()
+{
+}
+
+void SAL_CALL OReadMenuHandler::endDocument()
+{
+}
+
+void SAL_CALL OReadMenuHandler::startElement(
+ const OUString& aName, const Reference< XAttributeList > &xAttrList )
+{
+ if ( m_bMenuPopupMode )
+ {
+ ++m_nElementDepth;
+ m_xReader->startElement( aName, xAttrList );
+ }
+ else if ( aName == ELEMENT_MENUPOPUP )
+ {
+ ++m_nElementDepth;
+ m_bMenuPopupMode = true;
+ m_xReader.set( new OReadMenuPopupHandler( m_xMenuContainer, m_xContainerFactory ));
+ m_xReader->startDocument();
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "unknown element found!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+}
+
+void SAL_CALL OReadMenuHandler::characters(const OUString&)
+{
+}
+
+void SAL_CALL OReadMenuHandler::endElement( const OUString& aName )
+{
+ if ( !m_bMenuPopupMode )
+ return;
+
+ --m_nElementDepth;
+ if ( 0 == m_nElementDepth )
+ {
+ m_xReader->endDocument();
+ m_xReader.clear();
+ m_bMenuPopupMode = false;
+ if ( aName != ELEMENT_MENUPOPUP )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menupopup expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ else
+ m_xReader->endElement( aName );
+}
+
+OReadMenuPopupHandler::OReadMenuPopupHandler(
+ const Reference< XIndexContainer >& rMenuContainer,
+ const Reference< XSingleComponentFactory >& rFactory ) :
+ m_nElementDepth( 0 ),
+ m_bMenuMode( false ),
+ m_xMenuContainer( rMenuContainer ),
+ m_xContainerFactory( rFactory ),
+ m_xComponentContext( comphelper::getProcessComponentContext() ),
+ m_nNextElementExpected( ELEM_CLOSE_NONE )
+{
+}
+
+OReadMenuPopupHandler::~OReadMenuPopupHandler()
+{
+}
+
+void SAL_CALL OReadMenuPopupHandler::startDocument()
+{
+}
+
+void SAL_CALL OReadMenuPopupHandler::endDocument()
+{
+}
+
+void SAL_CALL OReadMenuPopupHandler::startElement(
+ const OUString& rName, const Reference< XAttributeList > &xAttrList )
+{
+ ++m_nElementDepth;
+
+ if ( m_bMenuMode )
+ m_xReader->startElement( rName, xAttrList );
+ else if ( rName == ELEMENT_MENU )
+ {
+ OUString aHelpId;
+ OUString aCommandId;
+ OUString aLabel;
+ sal_Int16 nItemBits(0);
+
+ m_bMenuMode = true;
+
+ // Container must be factory to create sub container
+ Reference< XIndexContainer > xSubItemContainer;
+ if ( m_xContainerFactory.is() )
+ xSubItemContainer.set( m_xContainerFactory->createInstanceWithContext( m_xComponentContext ), UNO_QUERY );
+
+ // read attributes for menu
+ for ( sal_Int16 i=0; i< xAttrList->getLength(); i++ )
+ {
+ OUString aName = xAttrList->getNameByIndex( i );
+ const OUString aValue = xAttrList->getValueByIndex( i );
+ if ( aName == ATTRIBUTE_ID )
+ aCommandId = aValue;
+ else if ( aName == ATTRIBUTE_LABEL )
+ aLabel = aValue;
+ else if ( aName == ATTRIBUTE_HELPID )
+ aHelpId = aValue;
+ else if ( aName == ATTRIBUTE_STYLE )
+ {
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aToken = aValue.getToken( 0, '+', nIndex );
+ if ( !aToken.isEmpty() )
+ {
+ if ( aToken == ATTRIBUTE_ITEMSTYLE_TEXT )
+ nItemBits |= css::ui::ItemStyle::TEXT;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_IMAGE )
+ nItemBits |= css::ui::ItemStyle::ICON;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_RADIO )
+ nItemBits |= css::ui::ItemStyle::RADIO_CHECK;
+ }
+ }
+ while ( nIndex >= 0 );
+ }
+
+ }
+
+ if ( !aCommandId.isEmpty() )
+ {
+ Sequence< PropertyValue > aSubMenuProp( 6 );
+ initPropertyCommon( aSubMenuProp, aCommandId, aHelpId, aLabel, nItemBits );
+ aSubMenuProp.getArray()[2].Value <<= xSubItemContainer;
+
+ m_xMenuContainer->insertByIndex( m_xMenuContainer->getCount(), Any( aSubMenuProp ) );
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "attribute id for element menu required!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_xReader.set( new OReadMenuHandler( xSubItemContainer, m_xContainerFactory ));
+ m_xReader->startDocument();
+ }
+ else if ( rName == ELEMENT_MENUITEM )
+ {
+ OUString aHelpId;
+ OUString aCommandId;
+ OUString aLabel;
+ sal_Int16 nItemBits(0);
+ // read attributes for menu item
+ for ( sal_Int16 i=0; i< xAttrList->getLength(); i++ )
+ {
+ OUString aName = xAttrList->getNameByIndex( i );
+ const OUString aValue = xAttrList->getValueByIndex( i );
+ if ( aName == ATTRIBUTE_ID )
+ aCommandId = aValue;
+ else if ( aName == ATTRIBUTE_LABEL )
+ aLabel = aValue;
+ else if ( aName == ATTRIBUTE_HELPID )
+ aHelpId = aValue;
+ else if ( aName == ATTRIBUTE_STYLE )
+ {
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aToken = aValue.getToken( 0, '+', nIndex );
+ if ( !aToken.isEmpty() )
+ {
+ if ( aToken == ATTRIBUTE_ITEMSTYLE_TEXT )
+ nItemBits |= css::ui::ItemStyle::TEXT;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_IMAGE )
+ nItemBits |= css::ui::ItemStyle::ICON;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_RADIO )
+ nItemBits |= css::ui::ItemStyle::RADIO_CHECK;
+ }
+ }
+ while ( nIndex >= 0 );
+ }
+
+ }
+
+ if ( !aCommandId.isEmpty() )
+ {
+ Sequence< PropertyValue > aMenuItem( 6 );
+ initPropertyCommon( aMenuItem, aCommandId, aHelpId, aLabel, nItemBits );
+ aMenuItem.getArray()[2].Value <<= Reference< XIndexContainer >();
+
+ m_xMenuContainer->insertByIndex( m_xMenuContainer->getCount(), Any( aMenuItem ) );
+ }
+
+ m_nNextElementExpected = ELEM_CLOSE_MENUITEM;
+ }
+ else if ( rName == ELEMENT_MENUSEPARATOR )
+ {
+ Sequence< PropertyValue > aMenuSeparator{ comphelper::makePropertyValue(
+ ITEM_DESCRIPTOR_TYPE, css::ui::ItemType::SEPARATOR_LINE) };
+
+ m_xMenuContainer->insertByIndex( m_xMenuContainer->getCount(), Any( aMenuSeparator ) );
+
+ m_nNextElementExpected = ELEM_CLOSE_MENUSEPARATOR;
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "unknown element found!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+}
+
+void SAL_CALL OReadMenuPopupHandler::characters(const OUString&)
+{
+}
+
+void SAL_CALL OReadMenuPopupHandler::endElement( const OUString& aName )
+{
+ --m_nElementDepth;
+ if ( m_bMenuMode )
+ {
+ if ( 0 == m_nElementDepth )
+ {
+ m_xReader->endDocument();
+ m_xReader.clear();
+ m_bMenuMode = false;
+ if ( aName != ELEMENT_MENU )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menu expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ else
+ m_xReader->endElement( aName );
+ }
+ else
+ {
+ if ( m_nNextElementExpected == ELEM_CLOSE_MENUITEM )
+ {
+ if ( aName != ELEMENT_MENUITEM )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menuitem expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ else if ( m_nNextElementExpected == ELEM_CLOSE_MENUSEPARATOR )
+ {
+ if ( aName != ELEMENT_MENUSEPARATOR )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menuseparator expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+
+ m_nNextElementExpected = ELEM_CLOSE_NONE;
+ }
+}
+
+// --------------------------------- Write XML ---------------------------------
+
+OWriteMenuDocumentHandler::OWriteMenuDocumentHandler(
+ const Reference< XIndexAccess >& rMenuBarContainer,
+ const Reference< XDocumentHandler >& rDocumentHandler,
+ bool bIsMenuBar ) :
+ m_xMenuBarContainer( rMenuBarContainer ),
+ m_xWriteDocumentHandler( rDocumentHandler ),
+ m_bIsMenuBar( bIsMenuBar )
+{
+ m_xEmptyList = new ::comphelper::AttributeList;
+ m_aAttributeType = ATTRIBUTE_TYPE_CDATA;
+}
+
+OWriteMenuDocumentHandler::~OWriteMenuDocumentHandler()
+{
+}
+
+void OWriteMenuDocumentHandler::WriteMenuDocument()
+{
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ m_xWriteDocumentHandler->startDocument();
+
+ // write DOCTYPE line!
+ Reference< XExtendedDocumentHandler > xExtendedDocHandler( m_xWriteDocumentHandler, UNO_QUERY );
+ if ( m_bIsMenuBar /*FIXME*/ && xExtendedDocHandler.is() )
+ {
+ xExtendedDocHandler->unknown( MENUBAR_DOCTYPE );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ }
+
+ pList->AddAttribute( ATTRIBUTE_XMLNS_MENU,
+ m_aAttributeType,
+ XMLNS_MENU );
+
+ if ( m_bIsMenuBar ) //FIXME
+ pList->AddAttribute( ATTRIBUTE_NS_ID,
+ m_aAttributeType,
+ "menubar" );
+
+ OUString aRootElement;
+ if ( m_bIsMenuBar )
+ aRootElement = ELEMENT_NS_MENUBAR;
+ else
+ aRootElement = ELEMENT_NS_MENUPOPUP;
+ m_xWriteDocumentHandler->startElement( aRootElement, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+
+ WriteMenu( m_xMenuBarContainer );
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( aRootElement );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endDocument();
+}
+
+void OWriteMenuDocumentHandler::WriteMenu( const Reference< XIndexAccess >& rMenuContainer )
+{
+ sal_Int32 nItemCount = rMenuContainer->getCount();
+ bool bSeparator = false;
+ Any aAny;
+
+ for ( sal_Int32 nItemPos = 0; nItemPos < nItemCount; nItemPos++ )
+ {
+ Sequence< PropertyValue > aProps;
+ aAny = rMenuContainer->getByIndex( nItemPos );
+ if ( aAny >>= aProps )
+ {
+ OUString aCommandURL;
+ OUString aLabel;
+ OUString aHelpURL;
+ sal_Int16 nType( css::ui::ItemType::DEFAULT );
+ sal_Int16 nItemBits( 0 );
+ Reference< XIndexAccess > xSubMenu;
+
+ ExtractMenuParameters( aProps, aCommandURL, aLabel, aHelpURL, xSubMenu, nType, nItemBits );
+ if ( xSubMenu.is() )
+ {
+ if ( !aCommandURL.isEmpty() )
+ {
+ rtl::Reference<::comphelper::AttributeList> pListMenu = new ::comphelper::AttributeList;
+
+ pListMenu->AddAttribute( ATTRIBUTE_NS_ID,
+ m_aAttributeType,
+ aCommandURL );
+
+ if ( !aLabel.isEmpty() )
+ pListMenu->AddAttribute( ATTRIBUTE_NS_LABEL,
+ m_aAttributeType,
+ aLabel );
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_MENU, pListMenu );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_MENUPOPUP, m_xEmptyList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+
+ WriteMenu( xSubMenu );
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_MENUPOPUP );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_MENU );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ bSeparator = false;
+ }
+ }
+ else
+ {
+ if ( nType == css::ui::ItemType::DEFAULT )
+ {
+ if ( !aCommandURL.isEmpty() )
+ {
+ bSeparator = false;
+ WriteMenuItem( aCommandURL, aLabel, aHelpURL, nItemBits );
+ }
+ }
+ else if ( !bSeparator )
+ {
+ // Don't write two separators together
+ WriteMenuSeparator();
+ bSeparator = true;
+ }
+ }
+ }
+ }
+}
+
+void OWriteMenuDocumentHandler::WriteMenuItem( const OUString& aCommandURL, const OUString& aLabel, const OUString& aHelpURL, sal_Int16 nStyle )
+{
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ pList->AddAttribute( ATTRIBUTE_NS_ID,
+ m_aAttributeType,
+ aCommandURL );
+
+ if ( !aHelpURL.isEmpty() )
+ {
+ pList->AddAttribute( ATTRIBUTE_NS_HELPID,
+ m_aAttributeType,
+ aHelpURL );
+ }
+
+ if ( !aLabel.isEmpty() )
+ {
+ pList->AddAttribute( ATTRIBUTE_NS_LABEL,
+ m_aAttributeType,
+ aLabel );
+ }
+ if ( nStyle > 0 )
+ {
+ OUStringBuffer aValue;
+ const MenuStyleItem* pStyle = MenuItemStyles;
+
+ for ( sal_Int32 nIndex = 0; nIndex < nMenuStyleItemEntries; ++nIndex, ++pStyle )
+ {
+ if ( nStyle & pStyle->nBit )
+ {
+ if ( !aValue.isEmpty() )
+ aValue.append("+");
+ aValue.appendAscii( pStyle->attrName );
+ }
+ }
+ pList->AddAttribute( ATTRIBUTE_NS_STYLE,
+ m_aAttributeType,
+ aValue.makeStringAndClear() );
+ }
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_MENUITEM, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_MENUITEM );
+}
+
+void OWriteMenuDocumentHandler::WriteMenuSeparator()
+{
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_MENUSEPARATOR, m_xEmptyList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_MENUSEPARATOR );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/saxnamespacefilter.cxx b/framework/source/fwe/xml/saxnamespacefilter.cxx
new file mode 100644
index 000000000..ac1ff571c
--- /dev/null
+++ b/framework/source/fwe/xml/saxnamespacefilter.cxx
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <vector>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+
+#include <xml/saxnamespacefilter.hxx>
+
+#include <comphelper/attributelist.hxx>
+#include <rtl/ref.hxx>
+
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::uno;
+
+namespace framework{
+
+SaxNamespaceFilter::SaxNamespaceFilter( Reference< XDocumentHandler > const & rSax1DocumentHandler ) :
+ xDocumentHandler( rSax1DocumentHandler )
+{
+}
+
+SaxNamespaceFilter::~SaxNamespaceFilter()
+{
+}
+
+// XDocumentHandler
+void SAL_CALL SaxNamespaceFilter::startDocument()
+{
+}
+
+void SAL_CALL SaxNamespaceFilter::endDocument()
+{
+}
+
+void SAL_CALL SaxNamespaceFilter::startElement(
+ const OUString& rName, const Reference< XAttributeList > &xAttribs )
+{
+ XMLNamespaces aXMLNamespaces;
+ if ( !m_aNamespaceStack.empty() )
+ aXMLNamespaces = m_aNamespaceStack.top();
+
+ rtl::Reference<::comphelper::AttributeList> pNewList = new ::comphelper::AttributeList();
+
+ // examine all namespaces for this level
+ ::std::vector< sal_Int16 > aAttributeIndexes;
+ {
+ for ( sal_Int16 i=0; i< xAttribs->getLength(); i++ )
+ {
+ OUString aName = xAttribs->getNameByIndex( i );
+ if ( aName.startsWith( "xmlns" ) )
+ aXMLNamespaces.addNamespace( aName, xAttribs->getValueByIndex( i ));
+ else
+ aAttributeIndexes.push_back( i );
+ }
+ }
+
+ // current namespaces for this level
+ m_aNamespaceStack.push( aXMLNamespaces );
+
+ try
+ {
+ // apply namespaces to all remaining attributes
+ for (auto const& attributeIndex : aAttributeIndexes)
+ {
+ OUString aAttributeName = xAttribs->getNameByIndex(attributeIndex);
+ OUString aValue = xAttribs->getValueByIndex(attributeIndex);
+ OUString aNamespaceAttributeName = aXMLNamespaces.applyNSToAttributeName( aAttributeName );
+ pNewList->AddAttribute( aNamespaceAttributeName, "CDATA", aValue );
+ }
+ }
+ catch ( SAXException& e )
+ {
+ e.Message = getErrorLineString() + e.Message;
+ throw;
+ }
+
+ OUString aNamespaceElementName;
+
+ try
+ {
+ aNamespaceElementName = aXMLNamespaces.applyNSToElementName( rName );
+ }
+ catch ( SAXException& e )
+ {
+ e.Message = getErrorLineString() + e.Message;
+ throw;
+ }
+
+ xDocumentHandler->startElement( aNamespaceElementName, pNewList );
+}
+
+void SAL_CALL SaxNamespaceFilter::endElement(const OUString& aName)
+{
+ XMLNamespaces& aXMLNamespaces = m_aNamespaceStack.top();
+ OUString aNamespaceElementName;
+
+ try
+ {
+ aNamespaceElementName = aXMLNamespaces.applyNSToElementName( aName );
+ }
+ catch ( SAXException& e )
+ {
+ e.Message = getErrorLineString() + e.Message;
+ throw;
+ }
+
+ xDocumentHandler->endElement( aNamespaceElementName );
+ m_aNamespaceStack.pop();
+}
+
+void SAL_CALL SaxNamespaceFilter::characters(const OUString& aChars)
+{
+ xDocumentHandler->characters( aChars );
+}
+
+void SAL_CALL SaxNamespaceFilter::ignorableWhitespace(const OUString& aWhitespaces)
+{
+ xDocumentHandler->ignorableWhitespace( aWhitespaces );
+}
+
+void SAL_CALL SaxNamespaceFilter::processingInstruction(
+ const OUString& aTarget, const OUString& aData)
+{
+ xDocumentHandler->processingInstruction( aTarget, aData );
+}
+
+void SAL_CALL SaxNamespaceFilter::setDocumentLocator(
+ const Reference< XLocator > &xLocator)
+{
+ m_xLocator = xLocator;
+ xDocumentHandler->setDocumentLocator( xLocator );
+}
+
+OUString SaxNamespaceFilter::getErrorLineString()
+{
+ if ( m_xLocator.is() )
+ return "Line: " + OUString::number( m_xLocator->getLineNumber() ) + " - ";
+ else
+ return OUString();
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/statusbarconfiguration.cxx b/framework/source/fwe/xml/statusbarconfiguration.cxx
new file mode 100644
index 000000000..ce974237b
--- /dev/null
+++ b/framework/source/fwe/xml/statusbarconfiguration.cxx
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <statusbarconfiguration.hxx>
+#include <xml/statusbardocumenthandler.hxx>
+#include <xml/saxnamespacefilter.hxx>
+
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::container;
+
+namespace framework
+{
+
+bool StatusBarConfiguration::LoadStatusBar(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XInputStream >& xInputStream,
+ const Reference< XIndexContainer >& rStatusbarConfiguration )
+{
+ Reference< XParser > xParser = Parser::create(rxContext);
+
+ // connect stream to input stream to the parser
+ InputSource aInputSource;
+ aInputSource.aInputStream = xInputStream;
+
+ // create namespace filter and set menudocument handler inside to support xml namespaces
+ Reference< XDocumentHandler > xDocHandler( new OReadStatusBarDocumentHandler( rStatusbarConfiguration ));
+ Reference< XDocumentHandler > xFilter( new SaxNamespaceFilter( xDocHandler ));
+
+ // connect parser and filter
+ xParser->setDocumentHandler( xFilter );
+
+ try
+ {
+ xParser->parseStream( aInputSource );
+ return true;
+ }
+ catch ( const RuntimeException& )
+ {
+ return false;
+ }
+ catch( const SAXException& )
+ {
+ return false;
+ }
+ catch( const css::io::IOException& )
+ {
+ return false;
+ }
+}
+
+bool StatusBarConfiguration::StoreStatusBar(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XOutputStream >& xOutputStream,
+ const Reference< XIndexAccess >& rStatusbarConfiguration )
+{
+ Reference< XWriter > xWriter = Writer::create( rxContext );
+ xWriter->setOutputStream( xOutputStream );
+
+ try
+ {
+ OWriteStatusBarDocumentHandler aWriteStatusBarDocumentHandler( rStatusbarConfiguration, xWriter );
+ aWriteStatusBarDocumentHandler.WriteStatusBarDocument();
+ return true;
+ }
+ catch ( const RuntimeException& )
+ {
+ return false;
+ }
+ catch ( const SAXException& )
+ {
+ return false;
+ }
+ catch ( const css::io::IOException& )
+ {
+ return false;
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/statusbardocumenthandler.cxx b/framework/source/fwe/xml/statusbardocumenthandler.cxx
new file mode 100644
index 000000000..3786e2fbd
--- /dev/null
+++ b/framework/source/fwe/xml/statusbardocumenthandler.cxx
@@ -0,0 +1,632 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xml/statusbardocumenthandler.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
+#include <com/sun/star/ui/ItemStyle.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+
+#include <vcl/status.hxx>
+
+#include <comphelper/attributelist.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::ui;
+using namespace ::com::sun::star::container;
+
+constexpr OUStringLiteral XMLNS_STATUSBAR = u"http://openoffice.org/2001/statusbar";
+constexpr OUStringLiteral XMLNS_XLINK = u"http://www.w3.org/1999/xlink";
+constexpr OUStringLiteral XMLNS_STATUSBAR_PREFIX = u"statusbar:";
+constexpr OUStringLiteral XMLNS_XLINK_PREFIX = u"xlink:";
+
+constexpr OUStringLiteral XMLNS_FILTER_SEPARATOR = u"^";
+
+#define ELEMENT_STATUSBAR "statusbar"
+#define ELEMENT_STATUSBARITEM "statusbaritem"
+
+#define ATTRIBUTE_ALIGN "align"
+#define ATTRIBUTE_STYLE "style"
+#define ATTRIBUTE_URL "href"
+#define ATTRIBUTE_WIDTH "width"
+#define ATTRIBUTE_OFFSET "offset"
+#define ATTRIBUTE_AUTOSIZE "autosize"
+#define ATTRIBUTE_OWNERDRAW "ownerdraw"
+#define ATTRIBUTE_HELPURL "helpid"
+#define ATTRIBUTE_MANDATORY "mandatory"
+
+constexpr OUStringLiteral ELEMENT_NS_STATUSBAR = u"statusbar:statusbar";
+constexpr OUStringLiteral ELEMENT_NS_STATUSBARITEM = u"statusbar:statusbaritem";
+
+constexpr OUStringLiteral ATTRIBUTE_XMLNS_STATUSBAR = u"xmlns:statusbar";
+constexpr OUStringLiteral ATTRIBUTE_XMLNS_XLINK = u"xmlns:xlink";
+
+constexpr OUStringLiteral ATTRIBUTE_TYPE_CDATA = u"CDATA";
+
+constexpr OUStringLiteral ATTRIBUTE_BOOLEAN_TRUE = u"true";
+constexpr OUStringLiteral ATTRIBUTE_BOOLEAN_FALSE = u"false";
+
+constexpr OUStringLiteral ATTRIBUTE_ALIGN_LEFT = u"left";
+constexpr OUStringLiteral ATTRIBUTE_ALIGN_RIGHT = u"right";
+constexpr OUStringLiteral ATTRIBUTE_ALIGN_CENTER = u"center";
+
+constexpr OUStringLiteral ATTRIBUTE_STYLE_IN = u"in";
+constexpr OUStringLiteral ATTRIBUTE_STYLE_OUT = u"out";
+constexpr OUStringLiteral ATTRIBUTE_STYLE_FLAT = u"flat";
+
+constexpr OUStringLiteral STATUSBAR_DOCTYPE = u"<!DOCTYPE statusbar:statusbar PUBLIC \"-//OpenOffice.org//DTD OfficeDocument 1.0//EN\" \"statusbar.dtd\">";
+
+namespace framework
+{
+
+// Property names of a menu/menu item ItemDescriptor
+constexpr OUStringLiteral ITEM_DESCRIPTOR_COMMANDURL = u"CommandURL";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_HELPURL = u"HelpURL";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_OFFSET = u"Offset";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_STYLE = u"Style";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_WIDTH = u"Width";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_TYPE = u"Type";
+
+static void ExtractStatusbarItemParameters(
+ const Sequence< PropertyValue >& rProp,
+ OUString& rCommandURL,
+ OUString& rHelpURL,
+ sal_Int16& rOffset,
+ sal_Int16& rStyle,
+ sal_Int16& rWidth )
+{
+ for ( const PropertyValue& rEntry : rProp )
+ {
+ if ( rEntry.Name == ITEM_DESCRIPTOR_COMMANDURL )
+ {
+ rEntry.Value >>= rCommandURL;
+ rCommandURL = rCommandURL.intern();
+ }
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_HELPURL )
+ {
+ rEntry.Value >>= rHelpURL;
+ }
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_OFFSET )
+ {
+ rEntry.Value >>= rOffset;
+ }
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_STYLE )
+ {
+ rEntry.Value >>= rStyle;
+ }
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_WIDTH )
+ {
+ rEntry.Value >>= rWidth;
+ }
+ }
+}
+
+namespace {
+
+struct StatusBarEntryProperty
+{
+ OReadStatusBarDocumentHandler::StatusBar_XML_Namespace nNamespace;
+ char aEntryName[20];
+};
+
+}
+
+StatusBarEntryProperty const StatusBarEntries[OReadStatusBarDocumentHandler::SB_XML_ENTRY_COUNT] =
+{
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ELEMENT_STATUSBAR },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ELEMENT_STATUSBARITEM },
+ { OReadStatusBarDocumentHandler::SB_NS_XLINK, ATTRIBUTE_URL },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_ALIGN },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_STYLE },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_AUTOSIZE },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_OWNERDRAW },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_WIDTH },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_OFFSET },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_HELPURL },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_MANDATORY }
+};
+
+OReadStatusBarDocumentHandler::OReadStatusBarDocumentHandler(
+ const Reference< XIndexContainer >& rStatusBarItems ) :
+ m_aStatusBarItems( rStatusBarItems )
+{
+ // create hash map
+ for ( int i = 0; i < SB_XML_ENTRY_COUNT; i++ )
+ {
+ if ( StatusBarEntries[i].nNamespace == SB_NS_STATUSBAR )
+ {
+ OUString temp = XMLNS_STATUSBAR + XMLNS_FILTER_SEPARATOR +
+ OUString::createFromAscii( StatusBarEntries[i].aEntryName );
+ m_aStatusBarMap.emplace( temp, static_cast<StatusBar_XML_Entry>(i) );
+ }
+ else
+ {
+ OUString temp = XMLNS_XLINK + XMLNS_FILTER_SEPARATOR +
+ OUString::createFromAscii( StatusBarEntries[i].aEntryName );
+ m_aStatusBarMap.emplace( temp, static_cast<StatusBar_XML_Entry>(i) );
+ }
+ }
+
+ m_bStatusBarStartFound = false;
+ m_bStatusBarItemStartFound = false;
+}
+
+OReadStatusBarDocumentHandler::~OReadStatusBarDocumentHandler()
+{
+}
+
+// XDocumentHandler
+void SAL_CALL OReadStatusBarDocumentHandler::startDocument()
+{
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::endDocument()
+{
+ if ( m_bStatusBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "No matching start or end element 'statusbar' found!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::startElement(
+ const OUString& aName, const Reference< XAttributeList > &xAttribs )
+{
+ StatusBarHashMap::const_iterator pStatusBarEntry = m_aStatusBarMap.find( aName );
+ if ( pStatusBarEntry == m_aStatusBarMap.end() )
+ return;
+
+ switch ( pStatusBarEntry->second )
+ {
+ case SB_ELEMENT_STATUSBAR:
+ {
+ if ( m_bStatusBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element 'statusbar:statusbar' cannot be embedded into 'statusbar:statusbar'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bStatusBarStartFound = true;
+ }
+ break;
+
+ case SB_ELEMENT_STATUSBARITEM:
+ {
+ if ( !m_bStatusBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element 'statusbar:statusbaritem' must be embedded into element 'statusbar:statusbar'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ if ( m_bStatusBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element statusbar:statusbaritem is not a container!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ OUString aCommandURL;
+ OUString aHelpURL;
+ sal_Int16 nItemBits( ItemStyle::ALIGN_CENTER|ItemStyle::DRAW_IN3D|ItemStyle::MANDATORY );
+ sal_Int16 nWidth( 0 );
+ sal_Int16 nOffset( STATUSBAR_OFFSET );
+ bool bCommandURL( false );
+
+ m_bStatusBarItemStartFound = true;
+ for ( sal_Int16 n = 0; n < xAttribs->getLength(); n++ )
+ {
+ pStatusBarEntry = m_aStatusBarMap.find( xAttribs->getNameByIndex( n ) );
+ if ( pStatusBarEntry != m_aStatusBarMap.end() )
+ {
+ switch ( pStatusBarEntry->second )
+ {
+ case SB_ATTRIBUTE_URL:
+ {
+ bCommandURL = true;
+ aCommandURL = xAttribs->getValueByIndex( n );
+ }
+ break;
+
+ case SB_ATTRIBUTE_ALIGN:
+ {
+ if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_ALIGN_LEFT )
+ {
+ nItemBits |= ItemStyle::ALIGN_LEFT;
+ nItemBits &= ~ItemStyle::ALIGN_CENTER;
+ }
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_ALIGN_CENTER )
+ {
+ nItemBits |= ItemStyle::ALIGN_CENTER;
+ nItemBits &= ~ItemStyle::ALIGN_LEFT;
+ }
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_ALIGN_RIGHT )
+ {
+ nItemBits |= ItemStyle::ALIGN_RIGHT;
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:align must have one value of 'left','right' or 'center'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ break;
+
+ case SB_ATTRIBUTE_STYLE:
+ {
+ if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_STYLE_IN )
+ {
+ nItemBits |= ItemStyle::DRAW_IN3D;
+ nItemBits &= ~ItemStyle::DRAW_OUT3D;
+ }
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_STYLE_OUT )
+ {
+ nItemBits |= ItemStyle::DRAW_OUT3D;
+ nItemBits &= ~ItemStyle::DRAW_IN3D;
+ }
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_STYLE_FLAT )
+ {
+ nItemBits |= ItemStyle::DRAW_FLAT;
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:autosize must have value 'true' or 'false'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ break;
+
+ case SB_ATTRIBUTE_AUTOSIZE:
+ {
+ if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_TRUE )
+ nItemBits |= ItemStyle::AUTO_SIZE;
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_FALSE )
+ nItemBits &= ~ItemStyle::AUTO_SIZE;
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:autosize must have value 'true' or 'false'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ break;
+
+ case SB_ATTRIBUTE_OWNERDRAW:
+ {
+ if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_TRUE )
+ nItemBits |= ItemStyle::OWNER_DRAW;
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_FALSE )
+ nItemBits &= ~ItemStyle::OWNER_DRAW;
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:ownerdraw must have value 'true' or 'false'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ break;
+
+ case SB_ATTRIBUTE_WIDTH:
+ {
+ nWidth = static_cast<sal_Int16>(xAttribs->getValueByIndex( n ).toInt32());
+ }
+ break;
+
+ case SB_ATTRIBUTE_OFFSET:
+ {
+ nOffset = static_cast<sal_Int16>(xAttribs->getValueByIndex( n ).toInt32());
+ }
+ break;
+
+ case SB_ATTRIBUTE_HELPURL:
+ {
+ aHelpURL = xAttribs->getValueByIndex( n );
+ }
+ break;
+
+ case SB_ATTRIBUTE_MANDATORY:
+ {
+ if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_TRUE )
+ nItemBits |= ItemStyle::MANDATORY;
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_FALSE )
+ nItemBits &= ~ItemStyle::MANDATORY;
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:mandatory must have value 'true' or 'false'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ } // for
+
+ if ( !bCommandURL )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Required attribute statusbar:url must have a value!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ else
+ {
+ Sequence< PropertyValue > aStatusbarItemProp{
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_COMMANDURL, aCommandURL),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_HELPURL, aHelpURL),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_OFFSET, nOffset),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_STYLE, nItemBits),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_WIDTH, nWidth),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_TYPE, css::ui::ItemType::DEFAULT)
+ };
+
+ m_aStatusBarItems->insertByIndex( m_aStatusBarItems->getCount(), Any( aStatusbarItemProp ) );
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::endElement(const OUString& aName)
+{
+ StatusBarHashMap::const_iterator pStatusBarEntry = m_aStatusBarMap.find( aName );
+ if ( pStatusBarEntry == m_aStatusBarMap.end() )
+ return;
+
+ switch ( pStatusBarEntry->second )
+ {
+ case SB_ELEMENT_STATUSBAR:
+ {
+ if ( !m_bStatusBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'statusbar' found, but no start element 'statusbar'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bStatusBarStartFound = false;
+ }
+ break;
+
+ case SB_ELEMENT_STATUSBARITEM:
+ {
+ if ( !m_bStatusBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'statusbar:statusbaritem' found, but no start element 'statusbar:statusbaritem'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bStatusBarItemStartFound = false;
+ }
+ break;
+
+ default: break;
+ }
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::characters(const OUString&)
+{
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::ignorableWhitespace(const OUString&)
+{
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::processingInstruction(
+ const OUString& /*aTarget*/, const OUString& /*aData*/ )
+{
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::setDocumentLocator(
+ const Reference< XLocator > &xLocator)
+{
+ m_xLocator = xLocator;
+}
+
+OUString OReadStatusBarDocumentHandler::getErrorLineString()
+{
+ if ( m_xLocator.is() )
+ return "Line: " + OUString::number( m_xLocator->getLineNumber() ) + " - ";
+ else
+ return OUString();
+}
+
+// OWriteStatusBarDocumentHandler
+
+OWriteStatusBarDocumentHandler::OWriteStatusBarDocumentHandler(
+ const Reference< XIndexAccess >& aStatusBarItems,
+ const Reference< XDocumentHandler >& rWriteDocumentHandler ) :
+ m_aStatusBarItems( aStatusBarItems ),
+ m_xWriteDocumentHandler( rWriteDocumentHandler )
+{
+ m_xEmptyList = new ::comphelper::AttributeList;
+ m_aAttributeType = ATTRIBUTE_TYPE_CDATA;
+ m_aXMLXlinkNS = XMLNS_XLINK_PREFIX;
+ m_aXMLStatusBarNS = XMLNS_STATUSBAR_PREFIX;
+}
+
+OWriteStatusBarDocumentHandler::~OWriteStatusBarDocumentHandler()
+{
+}
+
+void OWriteStatusBarDocumentHandler::WriteStatusBarDocument()
+{
+ m_xWriteDocumentHandler->startDocument();
+
+ // write DOCTYPE line!
+ Reference< XExtendedDocumentHandler > xExtendedDocHandler( m_xWriteDocumentHandler, UNO_QUERY );
+ if ( xExtendedDocHandler.is() )
+ {
+ xExtendedDocHandler->unknown( STATUSBAR_DOCTYPE );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ }
+
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ pList->AddAttribute( ATTRIBUTE_XMLNS_STATUSBAR,
+ m_aAttributeType,
+ XMLNS_STATUSBAR );
+
+ pList->AddAttribute( ATTRIBUTE_XMLNS_XLINK,
+ m_aAttributeType,
+ XMLNS_XLINK );
+
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_STATUSBAR, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+
+ sal_Int32 nItemCount = m_aStatusBarItems->getCount();
+ Any aAny;
+
+ for ( sal_Int32 nItemPos = 0; nItemPos < nItemCount; nItemPos++ )
+ {
+ Sequence< PropertyValue > aProps;
+ aAny = m_aStatusBarItems->getByIndex( nItemPos );
+ if ( aAny >>= aProps )
+ {
+ OUString aCommandURL;
+ OUString aHelpURL;
+ sal_Int16 nStyle( ItemStyle::ALIGN_CENTER|ItemStyle::DRAW_IN3D );
+ sal_Int16 nWidth( 0 );
+ sal_Int16 nOffset( STATUSBAR_OFFSET );
+
+ ExtractStatusbarItemParameters(
+ aProps,
+ aCommandURL,
+ aHelpURL,
+ nOffset,
+ nStyle,
+ nWidth );
+
+ if ( !aCommandURL.isEmpty() )
+ WriteStatusBarItem( aCommandURL, nOffset, nStyle, nWidth );
+ }
+ }
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_STATUSBAR );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endDocument();
+}
+
+// protected member functions
+
+void OWriteStatusBarDocumentHandler::WriteStatusBarItem(
+ const OUString& rCommandURL,
+ sal_Int16 nOffset,
+ sal_Int16 nStyle,
+ sal_Int16 nWidth )
+{
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ if (m_aAttributeURL.isEmpty() )
+ {
+ m_aAttributeURL = m_aXMLXlinkNS + ATTRIBUTE_URL;
+ }
+
+ // save required attribute (URL)
+ pList->AddAttribute( m_aAttributeURL, m_aAttributeType, rCommandURL );
+
+ // alignment
+ if ( nStyle & ItemStyle::ALIGN_RIGHT )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_ALIGN,
+ m_aAttributeType,
+ ATTRIBUTE_ALIGN_RIGHT );
+ }
+ else if ( nStyle & ItemStyle::ALIGN_CENTER )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_ALIGN,
+ m_aAttributeType,
+ ATTRIBUTE_ALIGN_CENTER );
+ }
+ else
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_ALIGN,
+ m_aAttributeType,
+ ATTRIBUTE_ALIGN_LEFT );
+ }
+
+ // style ( StatusBarItemBits::In is default )
+ if ( nStyle & ItemStyle::DRAW_FLAT )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_STYLE,
+ m_aAttributeType,
+ ATTRIBUTE_STYLE_FLAT );
+ }
+ else if ( nStyle & ItemStyle::DRAW_OUT3D )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_STYLE,
+ m_aAttributeType,
+ ATTRIBUTE_STYLE_OUT );
+ }
+
+ // autosize (default sal_False)
+ if ( nStyle & ItemStyle::AUTO_SIZE )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_AUTOSIZE,
+ m_aAttributeType,
+ ATTRIBUTE_BOOLEAN_TRUE );
+ }
+
+ // ownerdraw (default sal_False)
+ if ( nStyle & ItemStyle::OWNER_DRAW )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_OWNERDRAW,
+ m_aAttributeType,
+ ATTRIBUTE_BOOLEAN_TRUE );
+ }
+
+ // width (default 0)
+ if ( nWidth > 0 )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_WIDTH,
+ m_aAttributeType,
+ OUString::number( nWidth ) );
+ }
+
+ // offset (default STATUSBAR_OFFSET)
+ if ( nOffset != STATUSBAR_OFFSET )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_OFFSET,
+ m_aAttributeType,
+ OUString::number( nOffset ) );
+ }
+
+ // mandatory (default sal_True)
+ if ( !( nStyle & ItemStyle::MANDATORY ) )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_MANDATORY,
+ m_aAttributeType,
+ ATTRIBUTE_BOOLEAN_FALSE );
+ }
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_STATUSBARITEM, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_STATUSBARITEM );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/toolboxconfiguration.cxx b/framework/source/fwe/xml/toolboxconfiguration.cxx
new file mode 100644
index 000000000..d9e34baab
--- /dev/null
+++ b/framework/source/fwe/xml/toolboxconfiguration.cxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <toolboxconfiguration.hxx>
+#include <xml/toolboxdocumenthandler.hxx>
+#include <xml/saxnamespacefilter.hxx>
+
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::container;
+
+namespace framework
+{
+bool ToolBoxConfiguration::LoadToolBox(
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::io::XInputStream>& rInputStream,
+ const css::uno::Reference<css::container::XIndexContainer>& rToolbarConfiguration)
+{
+ Reference<XParser> xParser = Parser::create(rxContext);
+
+ // connect stream to input stream to the parser
+ InputSource aInputSource;
+
+ aInputSource.aInputStream = rInputStream;
+
+ // create namespace filter and set menudocument handler inside to support xml namespaces
+ Reference<XDocumentHandler> xDocHandler(new OReadToolBoxDocumentHandler(rToolbarConfiguration));
+ Reference<XDocumentHandler> xFilter(new SaxNamespaceFilter(xDocHandler));
+
+ // connect parser and filter
+ xParser->setDocumentHandler(xFilter);
+
+ try
+ {
+ xParser->parseStream(aInputSource);
+ return true;
+ }
+ catch (const RuntimeException&)
+ {
+ return false;
+ }
+ catch (const SAXException&)
+ {
+ return false;
+ }
+ catch (const css::io::IOException&)
+ {
+ return false;
+ }
+}
+
+bool ToolBoxConfiguration::StoreToolBox(
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::io::XOutputStream>& rOutputStream,
+ const css::uno::Reference<css::container::XIndexAccess>& rToolbarConfiguration)
+{
+ Reference<XWriter> xWriter = Writer::create(rxContext);
+ xWriter->setOutputStream(rOutputStream);
+
+ try
+ {
+ Reference<XDocumentHandler> xHandler(xWriter, UNO_QUERY_THROW);
+ OWriteToolBoxDocumentHandler aWriteToolBoxDocumentHandler(rToolbarConfiguration, xHandler);
+ aWriteToolBoxDocumentHandler.WriteToolBoxDocument();
+ return true;
+ }
+ catch (const RuntimeException&)
+ {
+ return false;
+ }
+ catch (const SAXException&)
+ {
+ return false;
+ }
+ catch (const css::io::IOException&)
+ {
+ return false;
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/toolboxdocumenthandler.cxx b/framework/source/fwe/xml/toolboxdocumenthandler.cxx
new file mode 100644
index 000000000..62202dea6
--- /dev/null
+++ b/framework/source/fwe/xml/toolboxdocumenthandler.cxx
@@ -0,0 +1,734 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xml/toolboxdocumenthandler.hxx>
+#include <xml/toolboxconfigurationdefines.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <com/sun/star/ui/ItemStyle.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+
+#include <sal/config.h>
+#include <sal/macros.h>
+#include <vcl/settings.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <comphelper/attributelist.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::xml::sax;
+
+constexpr OUStringLiteral TOOLBAR_DOCTYPE = u"<!DOCTYPE toolbar:toolbar PUBLIC \"-//OpenOffice.org//DTD OfficeDocument 1.0//EN\" \"toolbar.dtd\">";
+
+namespace framework
+{
+
+// Property names of a menu/menu item ItemDescriptor
+constexpr OUStringLiteral ITEM_DESCRIPTOR_COMMANDURL = u"CommandURL";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_LABEL = u"Label";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_TYPE = u"Type";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_STYLE = u"Style";
+constexpr OUStringLiteral ITEM_DESCRIPTOR_VISIBLE = u"IsVisible";
+
+static void ExtractToolbarParameters( const Sequence< PropertyValue >& rProp,
+ OUString& rCommandURL,
+ OUString& rLabel,
+ sal_Int16& rStyle,
+ bool& rVisible,
+ sal_Int16& rType )
+{
+ for ( const PropertyValue& rEntry : rProp )
+ {
+ if ( rEntry.Name == ITEM_DESCRIPTOR_COMMANDURL )
+ {
+ rEntry.Value >>= rCommandURL;
+ rCommandURL = rCommandURL.intern();
+ }
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_LABEL )
+ rEntry.Value >>= rLabel;
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_TYPE )
+ rEntry.Value >>= rType;
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_VISIBLE )
+ rEntry.Value >>= rVisible;
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_STYLE )
+ rEntry.Value >>= rStyle;
+ }
+}
+
+namespace {
+
+struct ToolboxStyleItem
+{
+ sal_Int16 nBit;
+ rtl::OUStringConstExpr attrName;
+};
+
+}
+
+constexpr ToolboxStyleItem Styles[ ] = {
+ { css::ui::ItemStyle::RADIO_CHECK, ATTRIBUTE_ITEMSTYLE_RADIO },
+ { css::ui::ItemStyle::ALIGN_LEFT, ATTRIBUTE_ITEMSTYLE_LEFT },
+ { css::ui::ItemStyle::AUTO_SIZE, ATTRIBUTE_ITEMSTYLE_AUTO },
+ { css::ui::ItemStyle::REPEAT, ATTRIBUTE_ITEMSTYLE_REPEAT },
+ { css::ui::ItemStyle::DROPDOWN_ONLY, ATTRIBUTE_ITEMSTYLE_DROPDOWNONLY },
+ { css::ui::ItemStyle::DROP_DOWN, ATTRIBUTE_ITEMSTYLE_DROPDOWN },
+ { css::ui::ItemStyle::ICON, ATTRIBUTE_ITEMSTYLE_IMAGE },
+ { css::ui::ItemStyle::TEXT, ATTRIBUTE_ITEMSTYLE_TEXT },
+};
+
+sal_Int32 const nStyleItemEntries = SAL_N_ELEMENTS(Styles);
+
+namespace {
+
+struct ToolBarEntryProperty
+{
+ OReadToolBoxDocumentHandler::ToolBox_XML_Namespace nNamespace;
+ char aEntryName[20];
+};
+
+}
+
+ToolBarEntryProperty const ToolBoxEntries[OReadToolBoxDocumentHandler::TB_XML_ENTRY_COUNT] =
+{
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBAR },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBARITEM },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBARSPACE },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBARBREAK },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBARSEPARATOR },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ATTRIBUTE_TEXT },
+ { OReadToolBoxDocumentHandler::TB_NS_XLINK, ATTRIBUTE_URL },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ATTRIBUTE_VISIBLE },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ATTRIBUTE_ITEMSTYLE },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ATTRIBUTE_UINAME },
+};
+
+OReadToolBoxDocumentHandler::OReadToolBoxDocumentHandler( const Reference< XIndexContainer >& rItemContainer ) :
+ m_rItemContainer( rItemContainer ),
+ m_aType( ITEM_DESCRIPTOR_TYPE ),
+ m_aLabel( ITEM_DESCRIPTOR_LABEL ),
+ m_aStyle( ITEM_DESCRIPTOR_STYLE ),
+ m_aIsVisible( ITEM_DESCRIPTOR_VISIBLE ),
+ m_aCommandURL( ITEM_DESCRIPTOR_COMMANDURL )
+ {
+ // create hash map
+ for ( int i = 0; i < TB_XML_ENTRY_COUNT; i++ )
+ {
+ if ( ToolBoxEntries[i].nNamespace == TB_NS_TOOLBAR )
+ {
+ OUString temp = XMLNS_TOOLBAR XMLNS_FILTER_SEPARATOR +
+ OUString::createFromAscii( ToolBoxEntries[i].aEntryName );
+ m_aToolBoxMap.emplace( temp, static_cast<ToolBox_XML_Entry>(i) );
+ }
+ else
+ {
+ OUString temp = XMLNS_XLINK XMLNS_FILTER_SEPARATOR +
+ OUString::createFromAscii( ToolBoxEntries[i].aEntryName );
+ m_aToolBoxMap.emplace( temp, static_cast<ToolBox_XML_Entry>(i) );
+ }
+ }
+
+ // pre-calculate a hash code for all style strings to speed up xml read process
+ m_nHashCode_Style_Radio = OUString( ATTRIBUTE_ITEMSTYLE_RADIO ).hashCode();
+ m_nHashCode_Style_Left = OUString( ATTRIBUTE_ITEMSTYLE_LEFT ).hashCode();
+ m_nHashCode_Style_AutoSize = OUString( ATTRIBUTE_ITEMSTYLE_AUTOSIZE ).hashCode();
+ m_nHashCode_Style_DropDown = OUString( ATTRIBUTE_ITEMSTYLE_DROPDOWN ).hashCode();
+ m_nHashCode_Style_Repeat = OUString( ATTRIBUTE_ITEMSTYLE_REPEAT ).hashCode();
+ m_nHashCode_Style_DropDownOnly = OUString( ATTRIBUTE_ITEMSTYLE_DROPDOWNONLY ).hashCode();
+ m_nHashCode_Style_Text = OUString( ATTRIBUTE_ITEMSTYLE_TEXT ).hashCode();
+ m_nHashCode_Style_Image = OUString( ATTRIBUTE_ITEMSTYLE_IMAGE ).hashCode();
+
+ m_bToolBarStartFound = false;
+ m_bToolBarItemStartFound = false;
+ m_bToolBarSpaceStartFound = false;
+ m_bToolBarBreakStartFound = false;
+ m_bToolBarSeparatorStartFound = false;
+}
+
+OReadToolBoxDocumentHandler::~OReadToolBoxDocumentHandler()
+{
+}
+
+// XDocumentHandler
+void SAL_CALL OReadToolBoxDocumentHandler::startDocument()
+{
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::endDocument()
+{
+ if ( m_bToolBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "No matching start or end element 'toolbar' found!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::startElement(
+ const OUString& aName, const Reference< XAttributeList > &xAttribs )
+{
+ ToolBoxHashMap::const_iterator pToolBoxEntry = m_aToolBoxMap.find( aName );
+ if ( pToolBoxEntry == m_aToolBoxMap.end() )
+ return;
+
+ switch ( pToolBoxEntry->second )
+ {
+ case TB_ELEMENT_TOOLBAR:
+ {
+ if ( m_bToolBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element 'toolbar:toolbar' cannot be embedded into 'toolbar:toolbar'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ else
+ {
+ // Check if we have a UI name set in our XML file
+ OUString aUIName;
+ for ( sal_Int16 n = 0; n < xAttribs->getLength(); n++ )
+ {
+ pToolBoxEntry = m_aToolBoxMap.find( xAttribs->getNameByIndex( n ) );
+ if ( pToolBoxEntry != m_aToolBoxMap.end() )
+ {
+ switch ( pToolBoxEntry->second )
+ {
+ case TB_ATTRIBUTE_UINAME:
+ aUIName = xAttribs->getValueByIndex( n );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if ( !aUIName.isEmpty() )
+ {
+ // Try to set UI name as a container property
+ Reference< XPropertySet > xPropSet( m_rItemContainer, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ xPropSet->setPropertyValue("UIName", Any( aUIName ) );
+ }
+ catch ( const UnknownPropertyException& )
+ {
+ }
+ }
+
+ }
+ }
+ m_bToolBarStartFound = true;
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARITEM:
+ {
+ if ( !m_bToolBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element 'toolbar:toolbaritem' must be embedded into element 'toolbar:toolbar'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ if ( m_bToolBarSeparatorStartFound ||
+ m_bToolBarBreakStartFound ||
+ m_bToolBarSpaceStartFound ||
+ m_bToolBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element toolbar:toolbaritem is not a container!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ bool bAttributeURL = false;
+
+ m_bToolBarItemStartFound = true;
+ OUString aLabel;
+ OUString aCommandURL;
+ sal_uInt16 nItemBits( 0 );
+ bool bVisible( true );
+
+ for ( sal_Int16 n = 0; n < xAttribs->getLength(); n++ )
+ {
+ pToolBoxEntry = m_aToolBoxMap.find( xAttribs->getNameByIndex( n ) );
+ if ( pToolBoxEntry != m_aToolBoxMap.end() )
+ {
+ switch ( pToolBoxEntry->second )
+ {
+ case TB_ATTRIBUTE_TEXT:
+ {
+ aLabel = xAttribs->getValueByIndex( n );
+ }
+ break;
+
+ case TB_ATTRIBUTE_URL:
+ {
+ bAttributeURL = true;
+ aCommandURL = xAttribs->getValueByIndex( n ).intern();
+ }
+ break;
+
+ case TB_ATTRIBUTE_VISIBLE:
+ {
+ if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_TRUE )
+ bVisible = true;
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_FALSE )
+ bVisible = false;
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() + "Attribute toolbar:visible must have value 'true' or 'false'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ break;
+
+ case TB_ATTRIBUTE_STYLE:
+ {
+ // read space separated item style list
+ OUString aTemp = xAttribs->getValueByIndex( n );
+ sal_Int32 nIndex = 0;
+
+ do
+ {
+ OUString aToken = aTemp.getToken( 0, ' ', nIndex );
+ if ( !aToken.isEmpty() )
+ {
+ sal_Int32 nHashCode = aToken.hashCode();
+ if ( nHashCode == m_nHashCode_Style_Radio )
+ nItemBits |= css::ui::ItemStyle::RADIO_CHECK;
+ else if ( nHashCode == m_nHashCode_Style_Left )
+ nItemBits |= css::ui::ItemStyle::ALIGN_LEFT;
+ else if ( nHashCode == m_nHashCode_Style_AutoSize )
+ nItemBits |= css::ui::ItemStyle::AUTO_SIZE;
+ else if ( nHashCode == m_nHashCode_Style_Repeat )
+ nItemBits |= css::ui::ItemStyle::REPEAT;
+ else if ( nHashCode == m_nHashCode_Style_DropDownOnly )
+ nItemBits |= css::ui::ItemStyle::DROPDOWN_ONLY;
+ else if ( nHashCode == m_nHashCode_Style_DropDown )
+ nItemBits |= css::ui::ItemStyle::DROP_DOWN;
+ else if ( nHashCode == m_nHashCode_Style_Text )
+ nItemBits |= css::ui::ItemStyle::TEXT;
+ else if ( nHashCode == m_nHashCode_Style_Image )
+ nItemBits |= css::ui::ItemStyle::ICON;
+ }
+ }
+ while ( nIndex >= 0 );
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ } // for
+
+ if ( !bAttributeURL )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Required attribute toolbar:url must have a value!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ if ( !aCommandURL.isEmpty() )
+ {
+ //fix for fdo#39370
+ /// check whether RTL interface or not
+ if(AllSettings::GetLayoutRTL()){
+ if (aCommandURL == ".uno:ParaLeftToRight")
+ aCommandURL = ".uno:ParaRightToLeft";
+ else if (aCommandURL == ".uno:ParaRightToLeft")
+ aCommandURL = ".uno:ParaLeftToRight";
+ else if (aCommandURL == ".uno:LeftPara")
+ aCommandURL = ".uno:RightPara";
+ else if (aCommandURL == ".uno:RightPara")
+ aCommandURL = ".uno:LeftPara";
+ else if (aCommandURL == ".uno:AlignLeft")
+ aCommandURL = ".uno:AlignRight";
+ else if (aCommandURL == ".uno:AlignRight")
+ aCommandURL = ".uno:AlignLeft";
+ else if (aCommandURL == ".uno:WrapLeft")
+ aCommandURL = ".uno:WrapRight";
+ else if (aCommandURL == ".uno:WrapRight")
+ aCommandURL = ".uno:WrapLeft";
+ }
+
+ auto aToolbarItemProp( comphelper::InitPropertySequence( {
+ { m_aCommandURL, css::uno::Any( aCommandURL ) },
+ { m_aLabel, css::uno::Any( aLabel ) },
+ { m_aType, css::uno::Any( css::ui::ItemType::DEFAULT ) },
+ { m_aStyle, css::uno::Any( nItemBits ) },
+ { m_aIsVisible, css::uno::Any( bVisible ) },
+ } ) );
+
+ m_rItemContainer->insertByIndex( m_rItemContainer->getCount(), Any( aToolbarItemProp ) );
+ }
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARSPACE:
+ {
+ if ( m_bToolBarSeparatorStartFound ||
+ m_bToolBarBreakStartFound ||
+ m_bToolBarSpaceStartFound ||
+ m_bToolBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element toolbar:toolbarspace is not a container!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarSpaceStartFound = true;
+
+ Sequence< PropertyValue > aToolbarItemProp{
+ comphelper::makePropertyValue(m_aCommandURL, OUString()),
+ comphelper::makePropertyValue(m_aType, css::ui::ItemType::SEPARATOR_SPACE)
+ };
+
+ m_rItemContainer->insertByIndex( m_rItemContainer->getCount(), Any( aToolbarItemProp ) );
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARBREAK:
+ {
+ if ( m_bToolBarSeparatorStartFound ||
+ m_bToolBarBreakStartFound ||
+ m_bToolBarSpaceStartFound ||
+ m_bToolBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element toolbar:toolbarbreak is not a container!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarBreakStartFound = true;
+
+ Sequence< PropertyValue > aToolbarItemProp{
+ comphelper::makePropertyValue(m_aCommandURL, OUString()),
+ comphelper::makePropertyValue(m_aType, css::ui::ItemType::SEPARATOR_LINEBREAK)
+ };
+
+ m_rItemContainer->insertByIndex( m_rItemContainer->getCount(), Any( aToolbarItemProp ) );
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARSEPARATOR:
+ {
+ if ( m_bToolBarSeparatorStartFound ||
+ m_bToolBarBreakStartFound ||
+ m_bToolBarSpaceStartFound ||
+ m_bToolBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element toolbar:toolbarseparator is not a container!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarSeparatorStartFound = true;
+
+ Sequence< PropertyValue > aToolbarItemProp{
+ comphelper::makePropertyValue(m_aCommandURL, OUString()),
+ comphelper::makePropertyValue(m_aType, css::ui::ItemType::SEPARATOR_LINE)
+ };
+
+ m_rItemContainer->insertByIndex( m_rItemContainer->getCount(), Any( aToolbarItemProp ) );
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::endElement(const OUString& aName)
+{
+ ToolBoxHashMap::const_iterator pToolBoxEntry = m_aToolBoxMap.find( aName );
+ if ( pToolBoxEntry == m_aToolBoxMap.end() )
+ return;
+
+ switch ( pToolBoxEntry->second )
+ {
+ case TB_ELEMENT_TOOLBAR:
+ {
+ if ( !m_bToolBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'toolbar' found, but no start element 'toolbar'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarStartFound = false;
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARITEM:
+ {
+ if ( !m_bToolBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'toolbar:toolbaritem' found, but no start element 'toolbar:toolbaritem'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarItemStartFound = false;
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARBREAK:
+ {
+ if ( !m_bToolBarBreakStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'toolbar:toolbarbreak' found, but no start element 'toolbar:toolbarbreak'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarBreakStartFound = false;
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARSPACE:
+ {
+ if ( !m_bToolBarSpaceStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'toolbar:toolbarspace' found, but no start element 'toolbar:toolbarspace'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarSpaceStartFound = false;
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARSEPARATOR:
+ {
+ if ( !m_bToolBarSeparatorStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'toolbar:toolbarseparator' found, but no start element 'toolbar:toolbarseparator'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarSeparatorStartFound = false;
+ }
+ break;
+
+ default: break;
+ }
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::characters(const OUString&)
+{
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::ignorableWhitespace(const OUString&)
+{
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::processingInstruction(
+ const OUString& /*aTarget*/, const OUString& /*aData*/ )
+{
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::setDocumentLocator(
+ const Reference< XLocator > &xLocator)
+{
+ m_xLocator = xLocator;
+}
+
+OUString OReadToolBoxDocumentHandler::getErrorLineString()
+{
+ if ( m_xLocator.is() )
+ return "Line: " + OUString::number( m_xLocator->getLineNumber() ) + " - ";
+ else
+ return OUString();
+}
+
+// OWriteToolBoxDocumentHandler
+
+OWriteToolBoxDocumentHandler::OWriteToolBoxDocumentHandler(
+ const Reference< XIndexAccess >& rItemAccess,
+ Reference< XDocumentHandler > const & rWriteDocumentHandler ) :
+ m_xWriteDocumentHandler( rWriteDocumentHandler ),
+ m_rItemAccess( rItemAccess )
+{
+ m_xEmptyList = new ::comphelper::AttributeList;
+ m_aAttributeType = ATTRIBUTE_TYPE_CDATA;
+ m_aXMLXlinkNS = XMLNS_XLINK_PREFIX;
+ m_aXMLToolbarNS = XMLNS_TOOLBAR_PREFIX;
+}
+
+OWriteToolBoxDocumentHandler::~OWriteToolBoxDocumentHandler()
+{
+}
+
+void OWriteToolBoxDocumentHandler::WriteToolBoxDocument()
+{
+ m_xWriteDocumentHandler->startDocument();
+
+ // write DOCTYPE line!
+ Reference< XExtendedDocumentHandler > xExtendedDocHandler( m_xWriteDocumentHandler, UNO_QUERY );
+ if ( xExtendedDocHandler.is() )
+ {
+ xExtendedDocHandler->unknown( TOOLBAR_DOCTYPE );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ }
+
+ OUString aUIName;
+ Reference< XPropertySet > xPropSet( m_rItemAccess, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ xPropSet->getPropertyValue("UIName") >>= aUIName;
+ }
+ catch ( const UnknownPropertyException& )
+ {
+ }
+ }
+
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ pList->AddAttribute( ATTRIBUTE_XMLNS_TOOLBAR,
+ m_aAttributeType,
+ XMLNS_TOOLBAR );
+
+ pList->AddAttribute( ATTRIBUTE_XMLNS_XLINK,
+ m_aAttributeType,
+ XMLNS_XLINK );
+
+ if ( !aUIName.isEmpty() )
+ pList->AddAttribute( m_aXMLToolbarNS + ATTRIBUTE_UINAME,
+ m_aAttributeType,
+ aUIName );
+
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBAR, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+
+ sal_Int32 nItemCount = m_rItemAccess->getCount();
+ Any aAny;
+
+ for ( sal_Int32 nItemPos = 0; nItemPos < nItemCount; nItemPos++ )
+ {
+ Sequence< PropertyValue > aProps;
+ aAny = m_rItemAccess->getByIndex( nItemPos );
+ if ( aAny >>= aProps )
+ {
+ OUString aCommandURL;
+ OUString aLabel;
+ bool bVisible( true );
+ sal_Int16 nType( css::ui::ItemType::DEFAULT );
+ sal_Int16 nStyle( 0 );
+
+ ExtractToolbarParameters( aProps, aCommandURL, aLabel, nStyle, bVisible, nType );
+ if ( nType == css::ui::ItemType::DEFAULT )
+ WriteToolBoxItem( aCommandURL, aLabel, nStyle, bVisible );
+ else if ( nType == css::ui::ItemType::SEPARATOR_SPACE )
+ WriteToolBoxSpace();
+ else if ( nType == css::ui::ItemType::SEPARATOR_LINE )
+ WriteToolBoxSeparator();
+ else if ( nType == css::ui::ItemType::SEPARATOR_LINEBREAK )
+ WriteToolBoxBreak();
+ }
+ }
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBAR );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endDocument();
+}
+
+// protected member functions
+
+void OWriteToolBoxDocumentHandler::WriteToolBoxItem(
+ const OUString& rCommandURL,
+ const OUString& rLabel,
+ sal_Int16 nStyle,
+ bool bVisible )
+{
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ if ( m_aAttributeURL.isEmpty() )
+ {
+ m_aAttributeURL = m_aXMLXlinkNS + ATTRIBUTE_URL;
+ }
+
+ // save required attribute (URL)
+ pList->AddAttribute( m_aAttributeURL, m_aAttributeType, rCommandURL );
+
+ if ( !rLabel.isEmpty() )
+ {
+ pList->AddAttribute( m_aXMLToolbarNS + ATTRIBUTE_TEXT,
+ m_aAttributeType,
+ rLabel );
+ }
+
+ if ( !bVisible )
+ {
+ pList->AddAttribute( m_aXMLToolbarNS + ATTRIBUTE_VISIBLE,
+ m_aAttributeType,
+ ATTRIBUTE_BOOLEAN_FALSE );
+ }
+
+ if ( nStyle > 0 )
+ {
+ OUStringBuffer aValue;
+ const ToolboxStyleItem* pStyle = Styles;
+
+ for ( sal_Int32 nIndex = 0; nIndex < nStyleItemEntries; ++nIndex, ++pStyle )
+ {
+ if ( nStyle & pStyle->nBit )
+ {
+ if ( !aValue.isEmpty() )
+ aValue.append(" ");
+ aValue.append( OUString(pStyle->attrName) );
+ }
+ }
+ pList->AddAttribute( m_aXMLToolbarNS + ATTRIBUTE_ITEMSTYLE,
+ m_aAttributeType,
+ aValue.makeStringAndClear() );
+ }
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBARITEM, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBARITEM );
+}
+
+void OWriteToolBoxDocumentHandler::WriteToolBoxSpace()
+{
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBARSPACE, m_xEmptyList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBARSPACE );
+}
+
+void OWriteToolBoxDocumentHandler::WriteToolBoxBreak()
+{
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBARBREAK, m_xEmptyList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBARBREAK );
+}
+
+void OWriteToolBoxDocumentHandler::WriteToolBoxSeparator()
+{
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBARSEPARATOR, m_xEmptyList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBARSEPARATOR );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/xmlnamespaces.cxx b/framework/source/fwe/xml/xmlnamespaces.cxx
new file mode 100644
index 000000000..7faa6c48d
--- /dev/null
+++ b/framework/source/fwe/xml/xmlnamespaces.cxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xml/xmlnamespaces.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::uno;
+
+namespace framework
+{
+
+void XMLNamespaces::addNamespace( const OUString& aName, const OUString& aValue )
+{
+ NamespaceMap::iterator p;
+ OUString aNamespaceName( aName );
+
+ // delete preceding "xmlns"
+ constexpr char aXMLAttributeNamespace[] = "xmlns";
+ if ( aNamespaceName.startsWith( aXMLAttributeNamespace ) )
+ {
+ constexpr sal_Int32 nXMLNamespaceLength = RTL_CONSTASCII_LENGTH(aXMLAttributeNamespace);
+ if ( aNamespaceName.getLength() == nXMLNamespaceLength )
+ {
+ aNamespaceName.clear();
+ }
+ else if ( aNamespaceName.getLength() >= nXMLNamespaceLength+2 )
+ {
+ aNamespaceName = aNamespaceName.copy( nXMLNamespaceLength+1 );
+ }
+ else
+ {
+ // a xml namespace without name is not allowed (e.g. "xmlns:" )
+ throw SAXException( "A xml namespace without name is not allowed!", Reference< XInterface >(), Any() );
+ }
+ }
+
+ if ( aValue.isEmpty() && !aNamespaceName.isEmpty() )
+ {
+ // namespace should be reset - as xml draft states this is only allowed
+ // for the default namespace - check and throw exception if check fails
+ throw SAXException( "Clearing xml namespace only allowed for default namespace!", Reference< XInterface >(), Any() );
+ }
+
+ if ( aNamespaceName.isEmpty() )
+ m_aDefaultNamespace = aValue;
+ else
+ {
+ p = m_aNamespaceMap.find( aNamespaceName );
+ if ( p != m_aNamespaceMap.end() )
+ {
+ // replace current namespace definition
+ m_aNamespaceMap.erase( p );
+ m_aNamespaceMap.emplace( aNamespaceName, aValue );
+ }
+ else
+ {
+ m_aNamespaceMap.emplace( aNamespaceName, aValue );
+ }
+ }
+}
+
+OUString XMLNamespaces::applyNSToAttributeName( const OUString& aName ) const
+{
+ // xml draft: there is no default namespace for attributes!
+
+ int index;
+ if (( index = aName.indexOf( ':' )) > 0 )
+ {
+ if ( aName.getLength() <= index+1 )
+ {
+ // attribute with namespace but without name "namespace:" is not allowed!!
+ throw SAXException( "Attribute has no name only preceding namespace!", Reference< XInterface >(), Any() );
+ }
+ OUString aAttributeName = getNamespaceValue( aName.copy( 0, index )) + "^" + aName.subView( index+1);
+ return aAttributeName;
+ }
+
+ return aName;
+}
+
+OUString XMLNamespaces::applyNSToElementName( const OUString& aName ) const
+{
+ // xml draft: element names can have a default namespace
+
+ int index = aName.indexOf( ':' );
+ OUString aNamespace;
+ OUString aElementName = aName;
+
+ if ( index > 0 )
+ aNamespace = getNamespaceValue( aName.copy( 0, index ) );
+ else
+ aNamespace = m_aDefaultNamespace;
+
+ if ( !aNamespace.isEmpty() )
+ {
+ aElementName = aNamespace + "^";
+ }
+ else
+ return aName;
+
+ if ( index > 0 )
+ {
+ if ( aName.getLength() <= index+1 )
+ {
+ // attribute with namespace but without a name is not allowed (e.g. "cfg:" )
+ throw SAXException( "Attribute has no name only preceding namespace!", Reference< XInterface >(), Any() );
+ }
+ aElementName += aName.subView( index+1 );
+ }
+ else
+ aElementName += aName;
+
+ return aElementName;
+}
+
+OUString const & XMLNamespaces::getNamespaceValue( const OUString& aNamespace ) const
+{
+ if ( aNamespace.isEmpty() )
+ return m_aDefaultNamespace;
+ else
+ {
+ NamespaceMap::const_iterator p = m_aNamespaceMap.find( aNamespace );
+ if ( p == m_aNamespaceMap.end() )
+ {
+ // namespace not defined => throw exception!
+ throw SAXException( "XML namespace used but not defined!", Reference< XInterface >(), Any() );
+ }
+ return p->second;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwi/classes/converter.cxx b/framework/source/fwi/classes/converter.cxx
new file mode 100644
index 000000000..a29c5b127
--- /dev/null
+++ b/framework/source/fwi/classes/converter.cxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <classes/converter.hxx>
+#include <rtl/ustrbuf.hxx>
+
+namespace framework{
+
+/**
+ * converts a sequence of PropertyValue to a sequence of NamedValue.
+ */
+css::uno::Sequence< css::beans::NamedValue > Converter::convert_seqPropVal2seqNamedVal( const css::uno::Sequence< css::beans::PropertyValue >& lSource )
+{
+ sal_Int32 nCount = lSource.getLength();
+ css::uno::Sequence< css::beans::NamedValue > lDestination(nCount);
+ auto lDestinationRange = asNonConstRange(lDestination);
+ for (sal_Int32 nItem=0; nItem<nCount; ++nItem)
+ {
+ lDestinationRange[nItem].Name = lSource[nItem].Name;
+ lDestinationRange[nItem].Value = lSource[nItem].Value;
+ }
+ return lDestination;
+}
+
+/**
+ * converts a sequence of unicode strings into a vector of such items
+ */
+std::vector<OUString> Converter::convert_seqOUString2OUStringList( const css::uno::Sequence< OUString >& lSource )
+{
+ std::vector<OUString> lDestination;
+ sal_Int32 nCount = lSource.getLength();
+
+ lDestination.reserve(nCount);
+ for (sal_Int32 nItem = 0; nItem < nCount; ++nItem)
+ {
+ lDestination.push_back(lSource[nItem]);
+ }
+
+ return lDestination;
+}
+
+OUString Converter::convert_DateTime2ISO8601( const DateTime& aSource )
+{
+ OUStringBuffer sBuffer(25);
+
+ sal_Int32 nYear = aSource.GetYear();
+ sal_Int32 nMonth = aSource.GetMonth();
+ sal_Int32 nDay = aSource.GetDay();
+
+ sal_Int32 nHour = aSource.GetHour();
+ sal_Int32 nMin = aSource.GetMin();
+ sal_Int32 nSec = aSource.GetSec();
+
+ // write year formatted as "YYYY"
+ if (nYear<10)
+ sBuffer.append("000");
+ else if (nYear<100)
+ sBuffer.append("00");
+ else if (nYear<1000)
+ sBuffer.append("0");
+ sBuffer.append( nYear );
+
+ sBuffer.append("-");
+ // write month formatted as "MM"
+ if (nMonth<10)
+ sBuffer.append("0");
+ sBuffer.append( nMonth );
+
+ sBuffer.append("-");
+ // write day formatted as "DD"
+ if (nDay<10)
+ sBuffer.append("0");
+ sBuffer.append( nDay );
+
+ sBuffer.append("T");
+ // write hours formatted as "hh"
+ if (nHour<10)
+ sBuffer.append("0");
+ sBuffer.append( nHour );
+
+ sBuffer.append(":");
+ // write min formatted as "mm"
+ if (nMin<10)
+ sBuffer.append("0");
+ sBuffer.append( nMin );
+
+ sBuffer.append(":");
+ // write sec formatted as "ss"
+ if (nSec<10)
+ sBuffer.append("0");
+ sBuffer.append( nSec );
+
+ sBuffer.append("Z");
+
+ return sBuffer.makeStringAndClear();
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwi/classes/protocolhandlercache.cxx b/framework/source/fwi/classes/protocolhandlercache.cxx
new file mode 100644
index 000000000..7619d4323
--- /dev/null
+++ b/framework/source/fwi/classes/protocolhandlercache.cxx
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/*TODO
+ - change "singleton" behaviour by using new helper ::comhelper::SingletonRef
+ - rename method exist() to existHandlerForURL() or similar one
+ - may it's a good idea to replace struct ProtocolHandler by css::beans::NamedValue type?!
+*/
+
+#include <classes/protocolhandlercache.hxx>
+#include <classes/converter.hxx>
+
+#include <tools/wldcrd.hxx>
+#include <unotools/configpaths.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+
+constexpr OUStringLiteral SETNAME_HANDLER = u"HandlerSet"; // name of configuration set inside package
+
+namespace framework{
+
+/**
+ @short overloaded index operator of hash map to support pattern key search
+ @descr All keys inside this hash map are URL pattern which points to a uno
+ implementation name of a protocol handler service which is registered
+ for this pattern. This operator makes it easy to find such registered
+ handler by using a full qualified URL and compare it with all pattern
+ keys.
+
+ @param sURL
+ the full qualified URL which should match to a registered pattern
+
+ @return An iterator which points to the found item inside the hash or PatternHash::end()
+ if no pattern match this given <var>sURL</var>.
+ */
+namespace {
+
+PatternHash::const_iterator findPatternKey(PatternHash const * hash, const OUString& sURL)
+{
+ return std::find_if(hash->begin(), hash->end(),
+ [&sURL](const PatternHash::value_type& rEntry) {
+ WildCard aPattern(rEntry.first);
+ return aPattern.Matches(sURL);
+ });
+}
+
+}
+
+/**
+ @short initialize static member of class HandlerCache
+ @descr We use a singleton pattern to implement this handler cache.
+ That means it use two static member list to hold all necessary information
+ and a ref count mechanism to create/destroy it on demand.
+ */
+std::optional<HandlerHash> HandlerCache::s_pHandler;
+std::optional<PatternHash> HandlerCache::s_pPattern;
+sal_Int32 HandlerCache::m_nRefCount = 0;
+HandlerCFGAccess* HandlerCache::s_pConfig = nullptr;
+
+/**
+ @short ctor of the cache of all registered protocol handler
+ @descr It tries to open the right configuration package automatically
+ and fill the internal structures. After that the cache can be
+ used for read access on this data and perform some search
+ operations on it.
+ */
+HandlerCache::HandlerCache()
+{
+ SolarMutexGuard aGuard;
+
+ if (m_nRefCount==0)
+ {
+ s_pHandler.emplace();
+ s_pPattern.emplace();
+ s_pConfig = new HandlerCFGAccess(PACKAGENAME_PROTOCOLHANDLER);
+ s_pConfig->read(*s_pHandler, *s_pPattern);
+ s_pConfig->setCache(this);
+ }
+
+ ++m_nRefCount;
+}
+
+/**
+ @short dtor of the cache
+ @descr It frees all used memory. In further implementations (may if we support write access too)
+ it's a good place to flush changes back to the configuration - but not needed yet.
+ */
+HandlerCache::~HandlerCache()
+{
+ SolarMutexGuard aGuard;
+
+ if( m_nRefCount==1)
+ {
+ s_pConfig->setCache(nullptr);
+
+ delete s_pConfig;
+ s_pConfig = nullptr;
+ s_pHandler.reset();
+ s_pPattern.reset();
+ }
+
+ --m_nRefCount;
+}
+
+/**
+ @short dtor of the cache
+ @descr It frees all used memory. In further implementations (may if we support write access too)
+ it's a good place to flush changes back to the configuration - but not needed yet.
+ */
+bool HandlerCache::search( const OUString& sURL, ProtocolHandler* pReturn ) const
+{
+ bool bFound = false;
+
+ SolarMutexGuard aGuard;
+
+ PatternHash::const_iterator pItem = findPatternKey(s_pPattern ? &*s_pPattern : nullptr, sURL);
+ if (pItem != s_pPattern->end())
+ {
+ *pReturn = (*s_pHandler)[pItem->second];
+ bFound = true;
+ }
+
+ return bFound;
+}
+
+/**
+ @short search for a registered handler by using a URL struct
+ @descr We combine necessary parts of this struct to a valid URL string
+ and call our other search method ...
+ It's a helper for outside code.
+ */
+bool HandlerCache::search( const css::util::URL& aURL, ProtocolHandler* pReturn ) const
+{
+ return search( aURL.Complete, pReturn );
+}
+
+void HandlerCache::takeOver(HandlerHash aHandler, PatternHash aPattern)
+{
+ SolarMutexGuard aGuard;
+
+ s_pHandler = std::move(aHandler);
+ s_pPattern = std::move(aPattern);
+}
+
+/**
+ @short dtor of the config access class
+ @descr It opens the configuration package automatically by using base class mechanism.
+ After that "read()" method of this class should be called to use it.
+
+ @param sPackage
+ specifies the package name of the configuration data which should be used
+ */
+HandlerCFGAccess::HandlerCFGAccess( const OUString& sPackage )
+ : ConfigItem(sPackage)
+ , m_pCache(nullptr)
+{
+ css::uno::Sequence< OUString > lListenPaths { SETNAME_HANDLER };
+ EnableNotification(lListenPaths);
+}
+
+/**
+ @short use base class mechanism to fill given structures
+ @descr User use us as a wrapper between configuration api and his internal structures.
+ He give us some pointer to his member and we fill it.
+
+ @param rHandlerHash
+ list of protocol handler infos
+
+ @param rPatternHash
+ reverse map of handler pattern to her uno names
+ */
+void HandlerCFGAccess::read( HandlerHash& rHandlerHash, PatternHash& rPatternHash )
+{
+ // list of all uno implementation names without encoding
+ css::uno::Sequence< OUString > lNames = GetNodeNames( SETNAME_HANDLER, ::utl::ConfigNameFormat::LocalPath );
+ sal_Int32 nSourceCount = lNames.getLength();
+ sal_Int32 nTargetCount = nSourceCount;
+ // list of all full qualified path names of configuration entries
+ css::uno::Sequence< OUString > lFullNames ( nTargetCount );
+ auto lFullNamesRange = asNonConstRange(lFullNames);
+ // expand names to full path names
+ sal_Int32 nSource=0;
+ sal_Int32 nTarget=0;
+ for( nSource=0; nSource<nSourceCount; ++nSource )
+ {
+ lFullNamesRange[nTarget] =
+ SETNAME_HANDLER +
+ CFG_PATH_SEPARATOR +
+ lNames[nSource] +
+ CFG_PATH_SEPARATOR
+ PROPERTY_PROTOCOLS;
+
+ ++nTarget;
+ }
+
+ // get values at all
+ css::uno::Sequence< css::uno::Any > lValues = GetProperties( lFullNames );
+ SAL_WARN_IF( lFullNames.getLength()!=lValues.getLength(), "fwk", "HandlerCFGAccess::read(): Miss some configuration values of handler set!" );
+
+ // fill structures
+ nSource = 0;
+ for( nTarget=0; nTarget<nTargetCount; ++nTarget )
+ {
+ // create it new for every loop to guarantee a real empty object!
+ ProtocolHandler aHandler;
+ aHandler.m_sUNOName = ::utl::extractFirstFromConfigurationPath(lNames[nSource]);
+
+ // unpack all values of this handler
+ css::uno::Sequence< OUString > lTemp;
+ lValues[nTarget] >>= lTemp;
+ aHandler.m_lProtocols = Converter::convert_seqOUString2OUStringList(lTemp);
+
+ // register his pattern into the performance search hash
+ for (auto const& item : aHandler.m_lProtocols)
+ {
+ rPatternHash[item] = lNames[nSource];
+ }
+
+ // insert the handler info into the normal handler cache
+ rHandlerHash[lNames[nSource]] = aHandler;
+ ++nSource;
+ }
+}
+
+void HandlerCFGAccess::Notify(const css::uno::Sequence< OUString >& /*lPropertyNames*/)
+{
+ HandlerHash aHandler;
+ PatternHash aPattern;
+
+ read(aHandler, aPattern);
+ if (m_pCache)
+ m_pCache->takeOver(std::move(aHandler), std::move(aPattern));
+}
+
+void HandlerCFGAccess::ImplCommit()
+{
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwi/helper/mischelper.cxx b/framework/source/fwi/helper/mischelper.cxx
new file mode 100644
index 000000000..e9c664d47
--- /dev/null
+++ b/framework/source/fwi/helper/mischelper.cxx
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/document/XDocumentLanguages.hpp>
+#include <com/sun/star/linguistic2/LanguageGuessing.hpp>
+
+#include <sal/log.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <svtools/langtab.hxx>
+#include <helper/mischelper.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+
+namespace framework
+{
+
+uno::Reference< linguistic2::XLanguageGuessing > const & LanguageGuessingHelper::GetGuesser() const
+{
+ if (!m_xLanguageGuesser.is())
+ {
+ try
+ {
+ m_xLanguageGuesser = linguistic2::LanguageGuessing::create( m_xContext );
+ }
+ catch (const uno::Exception &)
+ {
+ SAL_WARN( "fwk", "failed to get language guessing component" );
+ }
+ }
+ return m_xLanguageGuesser;
+}
+
+void FillLangItems( std::set< OUString > &rLangItems,
+ const uno::Reference< frame::XFrame > & rxFrame,
+ const LanguageGuessingHelper & rLangGuessHelper,
+ SvtScriptType nScriptType,
+ const OUString & rCurLang,
+ const OUString & rKeyboardLang,
+ const OUString & rGuessedTextLang )
+{
+ rLangItems.clear();
+
+ //1--add current language
+ if( !rCurLang.isEmpty() &&
+ LANGUAGE_DONTKNOW != SvtLanguageTable::GetLanguageType( rCurLang ))
+ rLangItems.insert( rCurLang );
+
+ //2--System
+ const AllSettings& rAllSettings = Application::GetSettings();
+ LanguageType rSystemLanguage = rAllSettings.GetLanguageTag().getLanguageType();
+ if( rSystemLanguage != LANGUAGE_DONTKNOW )
+ {
+ if ( IsScriptTypeMatchingToLanguage( nScriptType, rSystemLanguage ))
+ rLangItems.insert( SvtLanguageTable::GetLanguageString( rSystemLanguage ) );
+ }
+
+ //3--UI
+ LanguageType rUILanguage = rAllSettings.GetUILanguageTag().getLanguageType();
+ if( rUILanguage != LANGUAGE_DONTKNOW )
+ {
+ if ( IsScriptTypeMatchingToLanguage( nScriptType, rUILanguage ))
+ rLangItems.insert( SvtLanguageTable::GetLanguageString( rUILanguage ) );
+ }
+
+ //4--guessed language
+ const uno::Reference< linguistic2::XLanguageGuessing >& xLangGuesser( rLangGuessHelper.GetGuesser() );
+ if ( xLangGuesser.is() && !rGuessedTextLang.isEmpty())
+ {
+ css::lang::Locale aLocale(xLangGuesser->guessPrimaryLanguage( rGuessedTextLang, 0, rGuessedTextLang.getLength()) );
+ LanguageType nLang = LanguageTag( aLocale ).makeFallback().getLanguageType();
+ if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_NONE && nLang != LANGUAGE_SYSTEM
+ && IsScriptTypeMatchingToLanguage( nScriptType, nLang ))
+ rLangItems.insert( SvtLanguageTable::GetLanguageString( nLang ));
+ }
+
+ //5--keyboard language
+ if( !rKeyboardLang.isEmpty() )
+ {
+ if ( IsScriptTypeMatchingToLanguage( nScriptType, SvtLanguageTable::GetLanguageType( rKeyboardLang )))
+ rLangItems.insert( rKeyboardLang );
+ }
+
+ //6--all languages used in current document
+ Reference< css::frame::XModel > xModel;
+ if ( rxFrame.is() )
+ {
+ Reference< css::frame::XController > xController = rxFrame->getController();
+ if ( xController.is() )
+ xModel = xController->getModel();
+ }
+ Reference< document::XDocumentLanguages > xDocumentLanguages( xModel, UNO_QUERY );
+ /*the description of nScriptType
+ LATIN : 0x001
+ ASIAN : 0x002
+ COMPLEX: 0x004
+ */
+ const sal_Int16 nMaxCount = 7;
+ if ( !xDocumentLanguages.is() )
+ return;
+
+ const Sequence< Locale > rLocales( xDocumentLanguages->getDocumentLanguages( static_cast<sal_Int16>(nScriptType), nMaxCount ));
+ for ( const Locale& rLocale : rLocales )
+ {
+ if ( rLangItems.size() == static_cast< size_t >(nMaxCount) )
+ break;
+ if( IsScriptTypeMatchingToLanguage( nScriptType, SvtLanguageTable::GetLanguageType( rLocale.Language )))
+ rLangItems.insert( rLocale.Language );
+ }
+}
+
+auto (*g_pGetMultiplexerListener)(
+ css::uno::Reference<css::uno::XComponentContext> const & xComponentContext,
+ uno::Reference<uno::XInterface> const&,
+ std::function<bool (uno::Reference<ui::XContextChangeEventListener> const&)> const&)
+ -> uno::Reference<ui::XContextChangeEventListener> = nullptr;
+
+uno::Reference<ui::XContextChangeEventListener>
+GetFirstListenerWith_Impl(
+ css::uno::Reference<css::uno::XComponentContext> const & xComponentContext,
+ uno::Reference<uno::XInterface> const& xEventFocus,
+ std::function<bool (uno::Reference<ui::XContextChangeEventListener> const&)> const& rPredicate)
+{
+ assert(g_pGetMultiplexerListener != nullptr); // should not be called too early, nor too late
+ return g_pGetMultiplexerListener(xComponentContext, xEventFocus, rPredicate);
+}
+
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwi/helper/shareablemutex.cxx b/framework/source/fwi/helper/shareablemutex.cxx
new file mode 100644
index 000000000..cd3b6bd18
--- /dev/null
+++ b/framework/source/fwi/helper/shareablemutex.cxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/shareablemutex.hxx>
+
+namespace framework
+{
+ShareableMutex::ShareableMutex()
+{
+ m_pMutexRef = new MutexRef;
+ m_pMutexRef->acquire();
+}
+
+ShareableMutex::ShareableMutex(const ShareableMutex& rShareableMutex)
+{
+ m_pMutexRef = rShareableMutex.m_pMutexRef;
+ m_pMutexRef->acquire();
+}
+
+ShareableMutex& ShareableMutex::operator=(const ShareableMutex& rShareableMutex)
+{
+ rShareableMutex.m_pMutexRef->acquire();
+ m_pMutexRef->release();
+ m_pMutexRef = rShareableMutex.m_pMutexRef;
+ return *this;
+}
+
+void ShareableMutex::acquire() { m_pMutexRef->m_oslMutex.acquire(); }
+
+void ShareableMutex::release() { m_pMutexRef->m_oslMutex.release(); }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwi/jobs/configaccess.cxx b/framework/source/fwi/jobs/configaccess.cxx
new file mode 100644
index 000000000..fde891293
--- /dev/null
+++ b/framework/source/fwi/jobs/configaccess.cxx
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <jobs/configaccess.hxx>
+#include <services.h>
+
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <utility>
+
+#include <tools/diagnose_ex.h>
+
+namespace framework{
+
+/**
+ @short open the configuration of this job
+ @descr We open the configuration of this job only. Not the whole package or the whole
+ job set. We are interested on our own properties only.
+ We set the opened configuration access as our member. So any following method,
+ which needs cfg access, can use it. That prevent us against multiple open/close requests.
+ But you can use this method to upgrade an already opened configuration too.
+
+ @param eMode
+ force opening of the configuration access in readonly or in read/write mode
+ */
+ConfigAccess::ConfigAccess( /*IN*/ css::uno::Reference< css::uno::XComponentContext > xContext,
+ /*IN*/ OUString sRoot )
+ : m_xContext (std::move( xContext))
+ , m_sRoot (std::move( sRoot ))
+ , m_eMode ( E_CLOSED )
+{
+}
+
+/**
+ @short last chance to close an open configuration access point
+ @descr In case our user forgot to close this configuration point
+ in the right way, normally he will run into some trouble -
+ e.g. losing data.
+ */
+ConfigAccess::~ConfigAccess()
+{
+ close();
+}
+
+/**
+ @short return the internal mode of this instance
+ @descr May be the outside user need any information about successfully opened
+ or closed config access point objects. He can control the internal mode to do so.
+
+ @return The internal open state of this object.
+ */
+ConfigAccess::EOpenMode ConfigAccess::getMode() const
+{
+ std::unique_lock g(m_mutex);
+ return m_eMode;
+}
+
+/**
+ @short open the configuration access in the specified mode
+ @descr We set the opened configuration access as our member. So any following method,
+ which needs cfg access, can use it. That prevent us against multiple open/close requests.
+ But you can use this method to upgrade an already opened configuration too.
+ It's possible to open a config access in READONLY mode first and "open" it at a second
+ time within the mode READWRITE. Then we will upgrade it. Downgrade will be possible too.
+
+ But note: closing will be done explicitly by calling method close() ... not by
+ downgrading with mode CLOSED!
+
+ @param eMode
+ force (re)opening of the configuration access in readonly or in read/write mode
+ */
+void ConfigAccess::open( /*IN*/ EOpenMode eMode )
+{
+ std::unique_lock g(m_mutex);
+
+ // check if configuration is already open in the right mode.
+ // By the way: Don't allow closing by using this method!
+ if ( eMode == E_CLOSED || m_eMode == eMode )
+ return;
+
+ // We have to close the old access point without any question here.
+ // It will be open again using the new mode.
+ // can be called without checks! It does the checks by itself ...
+ // e.g. for already closed or not opened configuration.
+ // Flushing of all made changes will be done here too.
+ closeImpl();
+
+ // create the configuration provider, which provides sub access points
+ css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider = css::configuration::theDefaultProvider::get(m_xContext);
+ css::beans::PropertyValue aParam;
+ aParam.Name = "nodepath";
+ aParam.Value <<= m_sRoot;
+
+ css::uno::Sequence< css::uno::Any > lParams{ css::uno::Any(aParam) };
+
+ // open it
+ try
+ {
+ if (eMode==E_READONLY)
+ m_xConfig = xConfigProvider->createInstanceWithArguments(SERVICENAME_CFGREADACCESS , lParams);
+ else
+ if (eMode==E_READWRITE)
+ m_xConfig = xConfigProvider->createInstanceWithArguments(SERVICENAME_CFGUPDATEACCESS, lParams);
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_INFO_EXCEPTION("fwk", "open config");
+ }
+
+ m_eMode = E_CLOSED;
+ if (m_xConfig.is())
+ m_eMode = eMode;
+}
+
+/**
+ @short close the internal opened configuration access and flush all changes
+ @descr It checks, if the given access is valid and react in the right way.
+ It flushes all changes ... so nobody else must know this state.
+ */
+void ConfigAccess::close()
+{
+ std::unique_lock g(m_mutex);
+ closeImpl();
+}
+
+void ConfigAccess::closeImpl()
+{
+ // check already closed configuration
+ if (m_xConfig.is())
+ {
+ css::uno::Reference< css::util::XChangesBatch > xFlush(m_xConfig, css::uno::UNO_QUERY);
+ if (xFlush.is())
+ xFlush->commitChanges();
+ m_xConfig.clear();
+ m_eMode = E_CLOSED;
+ }
+}
+
+/**
+ @short provides an access to the internal wrapped configuration access
+ @descr It's not allowed to safe this c++ (!) reference outside. You have
+ to use it directly. Further you must use our public lock member m_aLock
+ to synchronize your code with our internal structures and our interface
+ methods. Acquire it before you call cfg() and release it afterwards immediately.
+
+ E.g.: ConfigAccess aAccess(...);
+ Guard aReadLock(aAccess.m_aLock);
+ Reference< XPropertySet > xSet(aAccess.cfg(), UNO_QUERY);
+ Any aProp = xSet->getPropertyValue("...");
+ aReadLock.unlock();
+
+ @attention During this time it's not allowed to call the methods open() or close()!
+ Otherwise you will change your own referenced config access. Anything will
+ be possible then.
+
+ @return A c++(!) reference to the uno instance of the configuration access point.
+ */
+const css::uno::Reference< css::uno::XInterface >& ConfigAccess::cfg()
+{
+ // must be synchronized from outside!
+ // => no lock here ...
+ return m_xConfig;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwi/threadhelp/transactionmanager.cxx b/framework/source/fwi/threadhelp/transactionmanager.cxx
new file mode 100644
index 000000000..86d5b354a
--- /dev/null
+++ b/framework/source/fwi/threadhelp/transactionmanager.cxx
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <framework/transactionmanager.hxx>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+
+namespace framework{
+
+/*-************************************************************************************************************
+ @short standard ctor
+ @descr Initialize instance with right start values for correct working.
+*//*-*************************************************************************************************************/
+TransactionManager::TransactionManager()
+ : m_eWorkingMode ( E_INIT )
+ , m_nTransactionCount ( 0 )
+{
+ m_aBarrier.open();
+}
+
+/*-************************************************************************************************************
+ @short standard dtor
+*//*-*************************************************************************************************************/
+TransactionManager::~TransactionManager()
+{
+}
+
+/*-****************************************************************************************************
+ @short set new working mode
+ @descr These implementation knows for states of working: E_INIT, E_WORK, E_BEFORECLOSE, E_CLOSE
+ You can step during this ones only from the left to the right side and start at left side again!
+ (This is necessary e.g. for refcounted objects!)
+ This call will block till all current existing transactions was finished.
+ Following results can occur:
+ E_INIT : All requests on this implementation are refused.
+ It's your decision to react in a right way.
+
+ E_WORK : The object can work now. The full functionality is available.
+
+ E_BEFORECLOSE : The object start the closing mechanism ... but sometimes
+ e.g. the dispose() method need to call some private methods.
+ These some special methods should use E_SOFTEXCEPTIONS
+ to detect this special case!
+
+ E_CLOSE : Object is already dead! All further requests will be refused.
+ It's your decision to react in a right way.
+ @param "eMode", is the new mode - but we don't accept setting mode in wrong order!
+ @onerror We do nothing.
+*//*-*****************************************************************************************************/
+void TransactionManager::setWorkingMode( EWorkingMode eMode )
+{
+ // Safe member access.
+ bool bWaitFor = false;
+ {
+ std::unique_lock aAccessGuard(m_aAccessLock);
+ // Change working mode first!
+ if (
+ (m_eWorkingMode == E_INIT && eMode == E_WORK) ||
+ ((m_eWorkingMode == E_WORK || m_eWorkingMode == E_INIT) && eMode == E_BEFORECLOSE) ||
+ (m_eWorkingMode == E_BEFORECLOSE && eMode == E_CLOSE) ||
+ (m_eWorkingMode == E_CLOSE && eMode == E_INIT)
+ )
+ {
+ m_eWorkingMode = eMode;
+ if (m_eWorkingMode == E_BEFORECLOSE || m_eWorkingMode == E_CLOSE)
+ {
+ bWaitFor = true;
+ }
+ }
+ }
+ // Wait for current existing transactions then!
+ // (Only necessary for changing to E_BEFORECLOSE or E_CLOSE!...
+ // otherwise; if you wait at setting E_WORK another thread could finish an acquire-call during our unlock() and wait() call
+ // ... and we will wait forever here!!!)
+ // Don't forget to release access mutex before.
+ if( bWaitFor )
+ {
+ m_aBarrier.wait();
+ }
+}
+
+/*-****************************************************************************************************
+ @short get current working mode
+ @descr If you stand in your close() or init() method ... but don't know
+ if you called more than ones(!) ... you can use this function to get
+ right information.
+ e.g: You have a method init() which is used to change working mode from
+ E_INIT to E_WORK and should be used to initialize some member too ...
+ What should you do:
+
+ void init( sal_Int32 nValue )
+ {
+ // Reject this call if our transaction manager say: "Object already initialized!"
+ // Otherwise initialize your member.
+ if( m_aTransactionManager.getWorkingMode() == E_INIT )
+ {
+ // Object is uninitialized ...
+ // Make member access threadsafe!
+ Guard aGuard( m_aMutex );
+
+ // Check working mode again .. because another instance could be faster.
+ // (It's possible to set this guard at first of this method too!)
+ if( m_aTransactionManager.getWorkingMode() == E_INIT )
+ {
+ m_aMember = nValue;
+
+ // Object is initialized now ... set working mode to E_WORK!
+ m_aTransactionManager.setWorkingMode( E_WORK );
+ }
+ }
+ }
+
+ @seealso method setWorkingMode()
+ @return Current set mode.
+
+ @onerror No error should occur.
+*//*-*****************************************************************************************************/
+EWorkingMode TransactionManager::getWorkingMode() const
+{
+ // Synchronize access to internal member!
+ std::unique_lock aAccessLock( m_aAccessLock );
+ return m_eWorkingMode;
+}
+
+/*-****************************************************************************************************
+ @short start new transaction
+ @descr A guard should use this method to start a new transaction. He should look for rejected
+ calls to by using parameter eMode and eReason.
+ If call was not rejected your transaction will be non breakable during releasing your transaction
+ guard! BUT ... your code isn't threadsafe then! It's a transaction manager only...
+
+ @seealso method unregisterTransaction()
+
+ @param "eMode" ,used to enable/disable throwing exceptions automatically for rejected calls
+*//*-*****************************************************************************************************/
+void TransactionManager::registerTransaction( EExceptionMode eMode )
+{
+ std::unique_lock aAccessGuard( m_aAccessLock );
+ switch( m_eWorkingMode )
+ {
+ case E_INIT:
+ if( eMode == E_HARDEXCEPTIONS )
+ {
+ // Help programmer to find out, why this exception is thrown!
+ SAL_WARN( "fwk", "TransactionManager...: Owner instance not correctly initialized yet. Call was rejected! Normally it's an algorithm error ... wrong use of class!" );
+ //ATTENTION: temp. disabled - till all bad code positions are detected and changed! */
+ // throw css::uno::RuntimeException( "TransactionManager.: Owner instance not right initialized yet. Call was rejected! Normally it's an algorithm error... wrong using of class!\n", css::uno::Reference< css::uno::XInterface >() );
+ }
+ break;
+ case E_WORK:
+ break;
+ case E_BEFORECLOSE:
+ if( eMode == E_HARDEXCEPTIONS )
+ {
+ // Help programmer to find out, why this exception is thrown!
+ SAL_WARN( "fwk", "TransactionManager...: Owner instance stand in close method. Call was rejected!" );
+ throw css::lang::DisposedException( "TransactionManager: Owner instance stand in close method. Call was rejected!" );
+ }
+ break;
+ case E_CLOSE:
+ // Help programmer to find out, why this exception is thrown!
+ SAL_WARN( "fwk", "TransactionManager...: Owner instance already closed. Call was rejected!" );
+ throw css::lang::DisposedException( "TransactionManager: Owner instance already closed. Call was rejected!" );
+ }
+
+ // Register this new transaction.
+ // If it is the first one .. close gate to disable changing of working mode.
+ ++m_nTransactionCount;
+ if( m_nTransactionCount == 1 )
+ {
+ m_aBarrier.close();
+ }
+}
+
+/*-****************************************************************************************************
+ @short finish transaction
+ @descr A guard should call this method to release current transaction.
+
+ @seealso method registerTransaction()
+*//*-*****************************************************************************************************/
+void TransactionManager::unregisterTransaction()
+{
+ // This call could not rejected!
+ // Safe access to internal member.
+ std::unique_lock aAccessGuard( m_aAccessLock );
+
+ // Deregister this transaction.
+ // If it was the last one ... open gate to enable changing of working mode!
+ // (see setWorkingMode())
+
+ --m_nTransactionCount;
+ if( m_nTransactionCount == 0 )
+ {
+ m_aBarrier.open();
+ }
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwi/uielement/constitemcontainer.cxx b/framework/source/fwi/uielement/constitemcontainer.cxx
new file mode 100644
index 000000000..7e43a5009
--- /dev/null
+++ b/framework/source/fwi/uielement/constitemcontainer.cxx
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/constitemcontainer.hxx>
+#include <uielement/itemcontainer.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <comphelper/propertysetinfo.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <rtl/ref.hxx>
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::container;
+
+const int PROPHANDLE_UINAME = 1;
+constexpr OUStringLiteral PROPNAME_UINAME = u"UIName";
+
+namespace framework
+{
+
+ConstItemContainer::ConstItemContainer()
+{
+}
+
+ConstItemContainer::ConstItemContainer( const ItemContainer& rItemContainer )
+{
+ ShareGuard aLock( rItemContainer.m_aShareMutex );
+ copyItemContainer( rItemContainer.m_aItemVector );
+}
+
+ConstItemContainer::ConstItemContainer( const Reference< XIndexAccess >& rSourceContainer, bool bFastCopy )
+{
+ // We also have to copy the UIName property
+ try
+ {
+ Reference< XPropertySet > xPropSet( rSourceContainer, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ xPropSet->getPropertyValue("UIName") >>= m_aUIName;
+ }
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ if ( !rSourceContainer.is() )
+ return;
+
+ try
+ {
+ sal_Int32 nCount = rSourceContainer->getCount();
+ m_aItemVector.reserve(nCount);
+ if ( bFastCopy )
+ {
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ Sequence< PropertyValue > aPropSeq;
+ if ( rSourceContainer->getByIndex( i ) >>= aPropSeq )
+ m_aItemVector.push_back( aPropSeq );
+ }
+ }
+ else
+ {
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ Sequence< PropertyValue > aPropSeq;
+ if ( rSourceContainer->getByIndex( i ) >>= aPropSeq )
+ {
+ sal_Int32 nContainerIndex = -1;
+ Reference< XIndexAccess > xIndexAccess;
+ for ( sal_Int32 j = 0; j < aPropSeq.getLength(); j++ )
+ {
+ if ( aPropSeq[j].Name == "ItemDescriptorContainer" )
+ {
+ aPropSeq[j].Value >>= xIndexAccess;
+ nContainerIndex = j;
+ break;
+ }
+ }
+
+ if ( xIndexAccess.is() && nContainerIndex >= 0 )
+ aPropSeq.getArray()[nContainerIndex].Value <<= deepCopyContainer( xIndexAccess );
+
+ m_aItemVector.push_back( aPropSeq );
+ }
+ }
+ }
+ }
+ catch ( const IndexOutOfBoundsException& )
+ {
+ }
+}
+
+ConstItemContainer::~ConstItemContainer()
+{
+}
+
+// private
+void ConstItemContainer::copyItemContainer( const std::vector< Sequence< PropertyValue > >& rSourceVector )
+{
+ const sal_uInt32 nCount = rSourceVector.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ sal_Int32 nContainerIndex = -1;
+ Sequence< PropertyValue > aPropSeq( rSourceVector[i] );
+ Reference< XIndexAccess > xIndexAccess;
+ for ( sal_Int32 j = 0; j < aPropSeq.getLength(); j++ )
+ {
+ if ( aPropSeq[j].Name == "ItemDescriptorContainer" )
+ {
+ aPropSeq[j].Value >>= xIndexAccess;
+ nContainerIndex = j;
+ break;
+ }
+ }
+
+ if ( xIndexAccess.is() && nContainerIndex >= 0 )
+ aPropSeq.getArray()[nContainerIndex].Value <<= deepCopyContainer( xIndexAccess );
+
+ m_aItemVector.push_back( aPropSeq );
+ }
+}
+
+Reference< XIndexAccess > ConstItemContainer::deepCopyContainer( const Reference< XIndexAccess >& rSubContainer )
+{
+ Reference< XIndexAccess > xReturn;
+ if ( rSubContainer.is() )
+ {
+ ItemContainer* pSource = comphelper::getFromUnoTunnel<ItemContainer>( rSubContainer );
+ rtl::Reference<ConstItemContainer> pSubContainer;
+ if ( pSource )
+ pSubContainer = new ConstItemContainer( *pSource );
+ else
+ pSubContainer = new ConstItemContainer( rSubContainer );
+ xReturn = pSubContainer;
+ }
+
+ return xReturn;
+}
+
+// XUnoTunnel
+sal_Int64 ConstItemContainer::getSomething( const css::uno::Sequence< sal_Int8 >& rIdentifier )
+{
+ return comphelper::getSomethingImpl(rIdentifier, this);
+}
+
+const Sequence< sal_Int8 >& ConstItemContainer::getUnoTunnelId() noexcept
+{
+ static const comphelper::UnoIdInit theConstItemContainerUnoTunnelId;
+ return theConstItemContainerUnoTunnelId.getSeq();
+}
+
+// XElementAccess
+sal_Bool SAL_CALL ConstItemContainer::hasElements()
+{
+ return ( !m_aItemVector.empty() );
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL ConstItemContainer::getCount()
+{
+ return m_aItemVector.size();
+}
+
+Any SAL_CALL ConstItemContainer::getByIndex( sal_Int32 Index )
+{
+ if ( sal_Int32( m_aItemVector.size()) <= Index )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+ return Any( m_aItemVector[Index] );
+}
+
+namespace
+{
+ std::vector<comphelper::PropertyMapEntry> makePropertyMap(const css::uno::Sequence<css::beans::Property>& rProps)
+ {
+ std::vector<comphelper::PropertyMapEntry> aEntries;
+ for (auto const& it : rProps)
+ aEntries.emplace_back(it.Name, it.Handle, it.Type, it.Attributes, 0);
+ return aEntries;
+ }
+}
+
+// XPropertySet
+Reference< XPropertySetInfo > SAL_CALL ConstItemContainer::getPropertySetInfo()
+{
+ // Create structure of propertysetinfo for baseclass "OPropertySetHelper".
+ // (Use method "getInfoHelper()".)
+ static std::vector<comphelper::PropertyMapEntry> aPropertyInfos(makePropertyMap(getInfoHelper().getProperties()));
+ static Reference< XPropertySetInfo > xInfo(new comphelper::PropertySetInfo(aPropertyInfos));
+
+ return xInfo;
+}
+
+void SAL_CALL ConstItemContainer::setPropertyValue( const OUString&, const Any& )
+{
+}
+
+Any SAL_CALL ConstItemContainer::getPropertyValue( const OUString& PropertyName )
+{
+ if ( PropertyName == PROPNAME_UINAME )
+ return Any( m_aUIName );
+
+ throw UnknownPropertyException(PropertyName);
+}
+
+void SAL_CALL ConstItemContainer::addPropertyChangeListener( const OUString&, const css::uno::Reference< css::beans::XPropertyChangeListener >& )
+{
+}
+
+void SAL_CALL ConstItemContainer::removePropertyChangeListener( const OUString&, const css::uno::Reference< css::beans::XPropertyChangeListener >& )
+{
+ // Only read-only properties - do nothing
+}
+
+void SAL_CALL ConstItemContainer::addVetoableChangeListener( const OUString&, const css::uno::Reference< css::beans::XVetoableChangeListener >& )
+{
+ // Only read-only properties - do nothing
+}
+
+void SAL_CALL ConstItemContainer::removeVetoableChangeListener( const OUString&, const css::uno::Reference< css::beans::XVetoableChangeListener >& )
+{
+ // Only read-only properties - do nothing
+}
+
+// XFastPropertySet
+void SAL_CALL ConstItemContainer::setFastPropertyValue( sal_Int32, const css::uno::Any& )
+{
+}
+
+Any SAL_CALL ConstItemContainer::getFastPropertyValue( sal_Int32 nHandle )
+{
+ if ( nHandle == PROPHANDLE_UINAME )
+ return Any( m_aUIName );
+
+ throw UnknownPropertyException(OUString::number(nHandle));
+}
+
+::cppu::IPropertyArrayHelper& ConstItemContainer::getInfoHelper()
+{
+ // Define static member to give structure of properties to baseclass "OPropertySetHelper".
+ // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable.
+ // "true" say: Table is sorted by name.
+ static ::cppu::OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true );
+
+ return ourInfoHelper;
+}
+
+css::uno::Sequence< css::beans::Property > ConstItemContainer::impl_getStaticPropertyDescriptor()
+{
+ // Create a property array to initialize sequence!
+ // Table of all predefined properties of this class. It's used from OPropertySetHelper-class!
+ // Don't forget to change the defines (see begin of this file), if you add, change or delete a property in this list!!!
+ // It's necessary for methods of OPropertySetHelper.
+ // ATTENTION:
+ // YOU MUST SORT FOLLOW TABLE BY NAME ALPHABETICAL !!!
+
+ return
+ {
+ css::beans::Property( PROPNAME_UINAME, PROPHANDLE_UINAME ,
+ cppu::UnoType<OUString>::get(),
+ css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY )
+ };
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwi/uielement/itemcontainer.cxx b/framework/source/fwi/uielement/itemcontainer.cxx
new file mode 100644
index 000000000..389bbaeaa
--- /dev/null
+++ b/framework/source/fwi/uielement/itemcontainer.cxx
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <uielement/itemcontainer.hxx>
+#include <uielement/constitemcontainer.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <rtl/ref.hxx>
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::container;
+
+constexpr OUStringLiteral WRONG_TYPE_EXCEPTION
+ = u"Type must be css::uno::Sequence< css::beans::PropertyValue >";
+
+namespace framework
+{
+
+// XInterface, XTypeProvider
+
+ItemContainer::ItemContainer( const ShareableMutex& rMutex ) :
+ m_aShareMutex( rMutex )
+{
+}
+
+ItemContainer::ItemContainer( const ConstItemContainer& rConstItemContainer, const ShareableMutex& rMutex ) : m_aShareMutex( rMutex )
+{
+ copyItemContainer( rConstItemContainer.m_aItemVector, rMutex );
+}
+
+ItemContainer::ItemContainer( const Reference< XIndexAccess >& rSourceContainer, const ShareableMutex& rMutex ) :
+ m_aShareMutex( rMutex )
+{
+ if ( !rSourceContainer.is() )
+ return;
+
+ sal_Int32 nCount = rSourceContainer->getCount();
+ try
+ {
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ Sequence< PropertyValue > aPropSeq;
+ if ( rSourceContainer->getByIndex( i ) >>= aPropSeq )
+ {
+ sal_Int32 nContainerIndex = -1;
+ Reference< XIndexAccess > xIndexAccess;
+ for ( sal_Int32 j = 0; j < aPropSeq.getLength(); j++ )
+ {
+ if ( aPropSeq[j].Name == "ItemDescriptorContainer" )
+ {
+ aPropSeq[j].Value >>= xIndexAccess;
+ nContainerIndex = j;
+ break;
+ }
+ }
+
+ if ( xIndexAccess.is() && nContainerIndex >= 0 )
+ aPropSeq.getArray()[nContainerIndex].Value <<= deepCopyContainer( xIndexAccess, rMutex );
+
+ m_aItemVector.push_back( aPropSeq );
+ }
+ }
+ }
+ catch ( const IndexOutOfBoundsException& )
+ {
+ }
+}
+
+ItemContainer::~ItemContainer()
+{
+}
+
+// private
+void ItemContainer::copyItemContainer( const std::vector< Sequence< PropertyValue > >& rSourceVector, const ShareableMutex& rMutex )
+{
+ const sal_uInt32 nCount = rSourceVector.size();
+ for ( sal_uInt32 i = 0; i < nCount; ++i )
+ {
+ sal_Int32 nContainerIndex = -1;
+ Sequence< PropertyValue > aPropSeq( rSourceVector[i] );
+ Reference< XIndexAccess > xIndexAccess;
+ for ( sal_Int32 j = 0; j < aPropSeq.getLength(); j++ )
+ {
+ if ( aPropSeq[j].Name == "ItemDescriptorContainer" )
+ {
+ aPropSeq[j].Value >>= xIndexAccess;
+ nContainerIndex = j;
+ break;
+ }
+ }
+
+ if ( xIndexAccess.is() && nContainerIndex >= 0 )
+ aPropSeq.getArray()[nContainerIndex].Value <<= deepCopyContainer( xIndexAccess, rMutex );
+
+ m_aItemVector.push_back( aPropSeq );
+ }
+}
+
+Reference< XIndexAccess > ItemContainer::deepCopyContainer( const Reference< XIndexAccess >& rSubContainer, const ShareableMutex& rMutex )
+{
+ Reference< XIndexAccess > xReturn;
+ if ( rSubContainer.is() )
+ {
+ ConstItemContainer* pSource = comphelper::getFromUnoTunnel<ConstItemContainer>( rSubContainer );
+ rtl::Reference<ItemContainer> pSubContainer;
+ if ( pSource )
+ pSubContainer = new ItemContainer( *pSource, rMutex );
+ else
+ pSubContainer = new ItemContainer( rSubContainer, rMutex );
+ xReturn = pSubContainer;
+ }
+
+ return xReturn;
+}
+
+const Sequence< sal_Int8 >& ItemContainer::getUnoTunnelId() noexcept
+{
+ static const comphelper::UnoIdInit theItemContainerUnoTunnelId;
+ return theItemContainerUnoTunnelId.getSeq();
+}
+
+// XElementAccess
+sal_Bool SAL_CALL ItemContainer::hasElements()
+{
+ ShareGuard aLock( m_aShareMutex );
+ return ( !m_aItemVector.empty() );
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL ItemContainer::getCount()
+{
+ ShareGuard aLock( m_aShareMutex );
+ return m_aItemVector.size();
+}
+
+Any SAL_CALL ItemContainer::getByIndex( sal_Int32 Index )
+{
+ ShareGuard aLock( m_aShareMutex );
+ if ( sal_Int32( m_aItemVector.size()) <= Index )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ return Any( m_aItemVector[Index] );
+}
+
+// XIndexContainer
+void SAL_CALL ItemContainer::insertByIndex( sal_Int32 Index, const Any& aItem )
+{
+ Sequence< PropertyValue > aSeq;
+ if ( !(aItem >>= aSeq) )
+ throw IllegalArgumentException( WRONG_TYPE_EXCEPTION,
+ static_cast<OWeakObject *>(this), 2 );
+
+ ShareGuard aLock( m_aShareMutex );
+ if ( sal_Int32( m_aItemVector.size()) == Index )
+ m_aItemVector.push_back( aSeq );
+ else if ( sal_Int32( m_aItemVector.size()) >Index )
+ {
+ std::vector< Sequence< PropertyValue > >::iterator aIter = m_aItemVector.begin();
+ aIter += Index;
+ m_aItemVector.insert( aIter, aSeq );
+ }
+ else
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+}
+
+void SAL_CALL ItemContainer::removeByIndex( sal_Int32 nIndex )
+{
+ ShareGuard aLock( m_aShareMutex );
+ if ( static_cast<sal_Int32>(m_aItemVector.size()) <= nIndex )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ m_aItemVector.erase(m_aItemVector.begin() + nIndex);
+}
+
+void SAL_CALL ItemContainer::replaceByIndex( sal_Int32 Index, const Any& aItem )
+{
+ Sequence< PropertyValue > aSeq;
+ if ( !(aItem >>= aSeq) )
+ throw IllegalArgumentException( WRONG_TYPE_EXCEPTION,
+ static_cast<OWeakObject *>(this), 2 );
+
+ ShareGuard aLock( m_aShareMutex );
+ if ( sal_Int32( m_aItemVector.size()) <= Index )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ m_aItemVector[Index] = aSeq;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwi/uielement/rootitemcontainer.cxx b/framework/source/fwi/uielement/rootitemcontainer.cxx
new file mode 100644
index 000000000..50ecd0d53
--- /dev/null
+++ b/framework/source/fwi/uielement/rootitemcontainer.cxx
@@ -0,0 +1,313 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <uielement/rootitemcontainer.hxx>
+#include <uielement/itemcontainer.hxx>
+#include <uielement/constitemcontainer.hxx>
+#include <properties.h>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <rtl/ref.hxx>
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::container;
+
+constexpr OUStringLiteral WRONG_TYPE_EXCEPTION
+ = u"Type must be css::uno::Sequence< css::beans::PropertyValue >";
+
+const int PROPHANDLE_UINAME = 1;
+constexpr OUStringLiteral PROPNAME_UINAME = u"UIName";
+
+namespace framework
+{
+
+RootItemContainer::RootItemContainer()
+ : ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType >( m_aMutex )
+ , ::cppu::OPropertySetHelper ( *static_cast< ::cppu::OBroadcastHelper* >(this) )
+{
+}
+
+RootItemContainer::RootItemContainer( const Reference< XIndexAccess >& rSourceContainer )
+ : ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType >( m_aMutex )
+ , ::cppu::OPropertySetHelper ( *static_cast< ::cppu::OBroadcastHelper* >(this) )
+{
+ // We also have to copy the UIName property
+ try
+ {
+ Reference< XPropertySet > xPropSet( rSourceContainer, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ xPropSet->getPropertyValue("UIName") >>= m_aUIName;
+ }
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ if ( !rSourceContainer.is() )
+ return;
+
+ sal_Int32 nCount = rSourceContainer->getCount();
+ try
+ {
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ Sequence< PropertyValue > aPropSeq;
+ if ( rSourceContainer->getByIndex( i ) >>= aPropSeq )
+ {
+ sal_Int32 nContainerIndex = -1;
+ Reference< XIndexAccess > xIndexAccess;
+ for ( sal_Int32 j = 0; j < aPropSeq.getLength(); j++ )
+ {
+ if ( aPropSeq[j].Name == "ItemDescriptorContainer" )
+ {
+ aPropSeq[j].Value >>= xIndexAccess;
+ nContainerIndex = j;
+ break;
+ }
+ }
+
+ if ( xIndexAccess.is() && nContainerIndex >= 0 )
+ aPropSeq.getArray()[nContainerIndex].Value <<= deepCopyContainer( xIndexAccess );
+
+ m_aItemVector.push_back( aPropSeq );
+ }
+ }
+ }
+ catch ( const IndexOutOfBoundsException& )
+ {
+ }
+}
+
+RootItemContainer::~RootItemContainer()
+{
+}
+
+Any SAL_CALL RootItemContainer::queryInterface( const Type& _rType )
+{
+ Any aRet = RootItemContainer_BASE::queryInterface( _rType );
+ if ( !aRet.hasValue() )
+ aRet = OPropertySetHelper::queryInterface( _rType );
+ return aRet;
+}
+
+Sequence< Type > SAL_CALL RootItemContainer::getTypes( )
+{
+ return comphelper::concatSequences(
+ RootItemContainer_BASE::getTypes(),
+ ::cppu::OPropertySetHelper::getTypes()
+ );
+}
+
+Reference< XIndexAccess > RootItemContainer::deepCopyContainer( const Reference< XIndexAccess >& rSubContainer )
+{
+ Reference< XIndexAccess > xReturn;
+ if ( rSubContainer.is() )
+ {
+ ConstItemContainer* pSource = comphelper::getFromUnoTunnel<ConstItemContainer>( rSubContainer );
+ rtl::Reference<ItemContainer> pSubContainer;
+ if ( pSource )
+ pSubContainer = new ItemContainer( *pSource, m_aShareMutex );
+ else
+ pSubContainer = new ItemContainer( rSubContainer, m_aShareMutex );
+ xReturn = pSubContainer;
+ }
+
+ return xReturn;
+}
+
+// XUnoTunnel
+sal_Int64 RootItemContainer::getSomething( const css::uno::Sequence< sal_Int8 >& rIdentifier )
+{
+ return comphelper::getSomethingImpl(rIdentifier, this);
+}
+
+const Sequence< sal_Int8 >& RootItemContainer::getUnoTunnelId() noexcept
+{
+ static const comphelper::UnoIdInit theRootItemContainerUnoTunnelId;
+ return theRootItemContainerUnoTunnelId.getSeq();
+}
+
+// XElementAccess
+sal_Bool SAL_CALL RootItemContainer::hasElements()
+{
+ ShareGuard aLock( m_aShareMutex );
+ return ( !m_aItemVector.empty() );
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL RootItemContainer::getCount()
+{
+ ShareGuard aLock( m_aShareMutex );
+ return m_aItemVector.size();
+}
+
+Any SAL_CALL RootItemContainer::getByIndex( sal_Int32 Index )
+{
+ ShareGuard aLock( m_aShareMutex );
+ if ( sal_Int32( m_aItemVector.size()) <= Index )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ return Any( m_aItemVector[Index] );
+}
+
+// XIndexContainer
+void SAL_CALL RootItemContainer::insertByIndex( sal_Int32 Index, const Any& aItem )
+{
+ Sequence< PropertyValue > aSeq;
+ if ( !(aItem >>= aSeq) )
+ throw IllegalArgumentException( WRONG_TYPE_EXCEPTION, static_cast<OWeakObject *>(this), 2 );
+
+ ShareGuard aLock( m_aShareMutex );
+ if ( sal_Int32( m_aItemVector.size()) == Index )
+ m_aItemVector.push_back( aSeq );
+ else if ( sal_Int32( m_aItemVector.size()) >Index )
+ {
+ std::vector< Sequence< PropertyValue > >::iterator aIter = m_aItemVector.begin();
+ aIter += Index;
+ m_aItemVector.insert( aIter, aSeq );
+ }
+ else
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+}
+
+void SAL_CALL RootItemContainer::removeByIndex( sal_Int32 nIndex )
+{
+ ShareGuard aLock( m_aShareMutex );
+ if ( static_cast<sal_Int32>(m_aItemVector.size()) <= nIndex )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ m_aItemVector.erase(m_aItemVector.begin() + nIndex);
+}
+
+void SAL_CALL RootItemContainer::replaceByIndex( sal_Int32 Index, const Any& aItem )
+{
+ Sequence< PropertyValue > aSeq;
+ if ( !(aItem >>= aSeq) )
+ throw IllegalArgumentException( WRONG_TYPE_EXCEPTION, static_cast<OWeakObject *>(this), 2 );
+
+ ShareGuard aLock( m_aShareMutex );
+ if ( sal_Int32( m_aItemVector.size()) <= Index )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ m_aItemVector[Index] = aSeq;
+}
+
+Reference< XInterface > SAL_CALL RootItemContainer::createInstanceWithContext( const Reference< XComponentContext >& )
+{
+ return static_cast<OWeakObject *>(new ItemContainer( m_aShareMutex ));
+}
+
+Reference< XInterface > SAL_CALL RootItemContainer::createInstanceWithArgumentsAndContext( const Sequence< Any >&, const Reference< XComponentContext >& )
+{
+ return static_cast<OWeakObject *>(new ItemContainer( m_aShareMutex ));
+}
+
+// XPropertySet helper
+sal_Bool SAL_CALL RootItemContainer::convertFastPropertyValue( Any& aConvertedValue ,
+ Any& aOldValue ,
+ sal_Int32 nHandle ,
+ const Any& aValue )
+{
+ // Initialize state with sal_False !!!
+ // (Handle can be invalid)
+ bool bReturn = false;
+
+ switch( nHandle )
+ {
+ case PROPHANDLE_UINAME:
+ bReturn = PropHelper::willPropertyBeChanged(
+ css::uno::Any(m_aUIName),
+ aValue,
+ aOldValue,
+ aConvertedValue);
+ break;
+ }
+
+ // Return state of operation.
+ return bReturn;
+}
+
+void SAL_CALL RootItemContainer::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle ,
+ const css::uno::Any& aValue )
+{
+ switch( nHandle )
+ {
+ case PROPHANDLE_UINAME:
+ aValue >>= m_aUIName;
+ break;
+ }
+}
+
+void SAL_CALL RootItemContainer::getFastPropertyValue( css::uno::Any& aValue ,
+ sal_Int32 nHandle ) const
+{
+ switch( nHandle )
+ {
+ case PROPHANDLE_UINAME:
+ aValue <<= m_aUIName;
+ break;
+ }
+}
+
+::cppu::IPropertyArrayHelper& SAL_CALL RootItemContainer::getInfoHelper()
+{
+ // Define static member to give structure of properties to baseclass "OPropertySetHelper".
+ // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable.
+ // "true" say: Table is sorted by name.
+ static ::cppu::OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true );
+
+ return ourInfoHelper;
+}
+
+css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL RootItemContainer::getPropertySetInfo()
+{
+ // Create structure of propertysetinfo for baseclass "OPropertySetHelper".
+ // (Use method "getInfoHelper()".)
+ static css::uno::Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+
+ return xInfo;
+}
+
+css::uno::Sequence< css::beans::Property > RootItemContainer::impl_getStaticPropertyDescriptor()
+{
+ // Create a property array to initialize sequence!
+ // Table of all predefined properties of this class. It's used from OPropertySetHelper-class!
+ // Don't forget to change the defines (see begin of this file), if you add, change or delete a property in this list!!!
+ // It's necessary for methods of OPropertySetHelper.
+ // ATTENTION:
+ // YOU MUST SORT FOLLOW TABLE BY NAME ALPHABETICAL !!!
+
+ return
+ {
+ css::beans::Property( PROPNAME_UINAME, PROPHANDLE_UINAME ,
+ cppu::UnoType<OUString>::get(),
+ css::beans::PropertyAttribute::TRANSIENT )
+ };
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/helper/dockingareadefaultacceptor.cxx b/framework/source/helper/dockingareadefaultacceptor.cxx
new file mode 100644
index 000000000..59a43a1e3
--- /dev/null
+++ b/framework/source/helper/dockingareadefaultacceptor.cxx
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/dockingareadefaultacceptor.hxx>
+
+#include <com/sun/star/awt/XDevice.hpp>
+#include <com/sun/star/awt/PosSize.hpp>
+
+#include <vcl/svapp.hxx>
+
+namespace framework{
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::cppu;
+using namespace ::osl;
+
+// constructor
+
+DockingAreaDefaultAcceptor::DockingAreaDefaultAcceptor( const css::uno::Reference< XFrame >& xOwner )
+ : m_xOwner ( xOwner )
+{
+}
+
+// destructor
+
+DockingAreaDefaultAcceptor::~DockingAreaDefaultAcceptor()
+{
+}
+
+// XDockingAreaAcceptor
+css::uno::Reference< css::awt::XWindow > SAL_CALL DockingAreaDefaultAcceptor::getContainerWindow()
+{
+ SolarMutexGuard g;
+
+ // Try to "lock" the frame for access to taskscontainer.
+ css::uno::Reference< XFrame > xFrame( m_xOwner );
+ //TODO: check xFrame for null?
+ css::uno::Reference< css::awt::XWindow > xContainerWindow( xFrame->getContainerWindow() );
+
+ return xContainerWindow;
+}
+
+sal_Bool SAL_CALL DockingAreaDefaultAcceptor::requestDockingAreaSpace( const css::awt::Rectangle& RequestedSpace )
+{
+ // Try to "lock" the frame for access to taskscontainer.
+ css::uno::Reference< XFrame > xFrame( m_xOwner );
+
+ if ( !xFrame.is() )
+ return false;
+
+ css::uno::Reference< css::awt::XWindow > xContainerWindow( xFrame->getContainerWindow() );
+ css::uno::Reference< css::awt::XWindow > xComponentWindow( xFrame->getComponentWindow() );
+
+ if ( !xContainerWindow.is() || !xComponentWindow.is() )
+ return false;
+
+ css::uno::Reference< css::awt::XDevice > xDevice( xContainerWindow, css::uno::UNO_QUERY );
+ // Convert relative size to output size.
+ css::awt::Rectangle aRectangle = xContainerWindow->getPosSize();
+ css::awt::DeviceInfo aInfo = xDevice->getInfo();
+ css::awt::Size aSize ( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset ,
+ aRectangle.Height - aInfo.TopInset - aInfo.BottomInset );
+
+ css::awt::Size aMinSize( 0, 0 ); // = xLayoutConstraints->getMinimumSize();
+
+ // Check if request border space would decrease component window size below minimum size
+ if ((( aSize.Width - RequestedSpace.X - RequestedSpace.Width ) < aMinSize.Width ) ||
+ (( aSize.Height - RequestedSpace.Y - RequestedSpace.Height ) < aMinSize.Height ) )
+ return false;
+
+ return true;
+}
+
+void SAL_CALL DockingAreaDefaultAcceptor::setDockingAreaSpace( const css::awt::Rectangle& BorderSpace )
+{
+ SolarMutexGuard g;
+
+ // Try to "lock" the frame for access to taskscontainer.
+ css::uno::Reference< XFrame > xFrame( m_xOwner );
+ if ( !xFrame.is() )
+ return;
+
+ css::uno::Reference< css::awt::XWindow > xContainerWindow( xFrame->getContainerWindow() );
+ css::uno::Reference< css::awt::XWindow > xComponentWindow( xFrame->getComponentWindow() );
+
+ if ( !(xContainerWindow.is() && xComponentWindow.is()) )
+ return;
+
+ css::uno::Reference< css::awt::XDevice > xDevice( xContainerWindow, css::uno::UNO_QUERY );
+ // Convert relative size to output size.
+ css::awt::Rectangle aRectangle = xContainerWindow->getPosSize();
+ css::awt::DeviceInfo aInfo = xDevice->getInfo();
+ css::awt::Size aSize ( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset ,
+ aRectangle.Height - aInfo.TopInset - aInfo.BottomInset );
+ css::awt::Size aMinSize( 0, 0 );// = xLayoutConstraints->getMinimumSize();
+
+ // Check if request border space would decrease component window size below minimum size
+ sal_Int32 nWidth = aSize.Width - BorderSpace.X - BorderSpace.Width;
+ sal_Int32 nHeight = aSize.Height - BorderSpace.Y - BorderSpace.Height;
+
+ if (( nWidth > aMinSize.Width ) && ( nHeight > aMinSize.Height ))
+ {
+ // Resize our component window.
+ xComponentWindow->setPosSize( BorderSpace.X, BorderSpace.Y, nWidth, nHeight, css::awt::PosSize::POSSIZE );
+ }
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/helper/ocomponentaccess.cxx b/framework/source/helper/ocomponentaccess.cxx
new file mode 100644
index 000000000..ad7409990
--- /dev/null
+++ b/framework/source/helper/ocomponentaccess.cxx
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/ocomponentaccess.hxx>
+#include <helper/ocomponentenumeration.hxx>
+
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+
+namespace framework{
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::cppu;
+using namespace ::osl;
+
+// constructor
+
+OComponentAccess::OComponentAccess( const css::uno::Reference< XDesktop >& xOwner )
+ : m_xOwner ( xOwner )
+{
+ // Safe impossible cases
+ SAL_WARN_IF( !xOwner.is(), "fwk", "OComponentAccess::OComponentAccess(): Invalid parameter detected!" );
+}
+
+// destructor
+
+OComponentAccess::~OComponentAccess()
+{
+}
+
+// XEnumerationAccess
+css::uno::Reference< XEnumeration > SAL_CALL OComponentAccess::createEnumeration()
+{
+ SolarMutexGuard g;
+
+ // Set default return value, if method failed.
+ // If no desktop exist and there is no task container - return an empty enumeration!
+ css::uno::Reference< XEnumeration > xReturn;
+
+ // Try to "lock" the desktop for access to task container.
+ css::uno::Reference< XInterface > xLock = m_xOwner.get();
+ if ( xLock.is() )
+ {
+ // Desktop exist => pointer to task container must be valid.
+ // Initialize a new enumeration ... if some tasks and his components exist!
+ // (OTasksEnumeration will make an assert, if we initialize the new instance without valid values!)
+
+ std::vector< css::uno::Reference< XComponent > > seqComponents;
+ impl_collectAllChildComponents( css::uno::Reference< XFramesSupplier >( xLock, UNO_QUERY ), seqComponents );
+ xReturn = new OComponentEnumeration( std::move(seqComponents) );
+ }
+
+ // Return result of this operation.
+ return xReturn;
+}
+
+// XElementAccess
+Type SAL_CALL OComponentAccess::getElementType()
+{
+ // Elements in list an enumeration are components!
+ // Return the uno-type of XComponent.
+ return cppu::UnoType<XComponent>::get();
+}
+
+// XElementAccess
+sal_Bool SAL_CALL OComponentAccess::hasElements()
+{
+ SolarMutexGuard g;
+
+ // Set default return value, if method failed.
+ bool bReturn = false;
+
+ // Try to "lock" the desktop for access to task container.
+ css::uno::Reference< XFramesSupplier > xLock( m_xOwner.get(), UNO_QUERY );
+ if ( xLock.is() )
+ {
+ // Ask container of owner for existing elements.
+ bReturn = xLock->getFrames()->hasElements();
+ }
+
+ // Return result of this operation.
+ return bReturn;
+}
+
+
+void OComponentAccess::impl_collectAllChildComponents( const css::uno::Reference< XFramesSupplier >& xNode ,
+ std::vector< css::uno::Reference< XComponent > >& seqComponents )
+{
+ // If valid node was given ...
+ if( !xNode.is() )
+ return;
+
+ // ... continue collection at these.
+
+ // Get the container of current node, collect the components of existing child frames
+ // and go down to next level in tree (recursive!).
+
+ const css::uno::Reference< XFrames > xContainer = xNode->getFrames();
+ const Sequence< css::uno::Reference< XFrame > > seqFrames = xContainer->queryFrames( FrameSearchFlag::CHILDREN );
+
+ const sal_Int32 nFrameCount = seqFrames.getLength();
+ for( sal_Int32 nFrame=0; nFrame<nFrameCount; ++nFrame )
+ {
+ css::uno::Reference< XComponent > xComponent = impl_getFrameComponent( seqFrames[nFrame] );
+ if( xComponent.is() )
+ {
+ seqComponents.push_back( xComponent );
+ }
+ }
+ // ... otherwise break a recursive path and go back at current stack!
+}
+
+css::uno::Reference< XComponent > OComponentAccess::impl_getFrameComponent( const css::uno::Reference< XFrame >& xFrame ) const
+{
+ // Set default return value, if method failed.
+ css::uno::Reference< XComponent > xComponent;
+ // Does no controller exists?
+ css::uno::Reference< XController > xController = xFrame->getController();
+ if ( !xController.is() )
+ {
+ // Controller not exist - use the VCL-component.
+ xComponent = xFrame->getComponentWindow();
+ }
+ else
+ {
+ // Does no model exists?
+ css::uno::Reference< XModel > xModel = xController->getModel();
+ if ( xModel.is() )
+ {
+ // Model exist - use the model as component.
+ xComponent = xModel;
+ }
+ else
+ {
+ // Model not exist - use the controller as component.
+ xComponent = xController;
+ }
+ }
+
+ return xComponent;
+}
+
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/helper/ocomponentenumeration.cxx b/framework/source/helper/ocomponentenumeration.cxx
new file mode 100644
index 000000000..3d00f75e5
--- /dev/null
+++ b/framework/source/helper/ocomponentenumeration.cxx
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/ocomponentenumeration.hxx>
+
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+
+namespace framework{
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::cppu;
+using namespace ::osl;
+
+// constructor
+
+OComponentEnumeration::OComponentEnumeration( std::vector< css::uno::Reference< XComponent > >&& seqComponents )
+ : m_nPosition ( 0 ) // 0 is the first position for a valid list and the right value for an invalid list to!
+ , m_seqComponents ( std::move(seqComponents) )
+{}
+
+// destructor
+
+OComponentEnumeration::~OComponentEnumeration()
+{
+ // Reset instance, free memory...
+ impl_resetObject();
+}
+
+// XEventListener
+void SAL_CALL OComponentEnumeration::disposing( const EventObject& aEvent )
+{
+ SolarMutexGuard g;
+
+ // Safe impossible cases
+ // This method is not specified for all incoming parameters.
+ SAL_WARN_IF( !aEvent.Source.is(), "fwk", "OComponentEnumeration::disposing(): Invalid parameter detected!" );
+
+ // Reset instance to defaults, release references and free memory.
+ impl_resetObject();
+}
+
+// XEnumeration
+sal_Bool SAL_CALL OComponentEnumeration::hasMoreElements()
+{
+ SolarMutexGuard g;
+
+ // First position in a valid list is 0.
+ // => The last one is getLength() - 1!
+ // m_nPosition's current value is the position for the next element, which will be return, if user call "nextElement()"
+ // => We have more elements if current position less than the length of the list!
+ return ( m_nPosition < static_cast<sal_uInt32>(m_seqComponents.size()) );
+}
+
+// XEnumeration
+
+Any SAL_CALL OComponentEnumeration::nextElement()
+{
+ SolarMutexGuard g;
+
+ // If we have no elements or end of enumeration is arrived ...
+ if ( !hasMoreElements() )
+ {
+ // .. throw an exception!
+ throw NoSuchElementException();
+ }
+
+ // Else; Get next element from list ...
+ Any aComponent;
+ aComponent <<= m_seqComponents[m_nPosition];
+ // ... and step to next element!
+ ++m_nPosition;
+
+ // Return listitem.
+ return aComponent;
+}
+
+// protected method
+
+void OComponentEnumeration::impl_resetObject()
+{
+ // Attention:
+ // Write this for multiple calls - NOT AT THE SAME TIME - but for more than one call again)!
+ // It exist two ways to call this method. From destructor and from disposing().
+ // I can't say, which one is the first. Normally the disposing-call - but other way...
+
+ // Delete list of components.
+ m_seqComponents.clear();
+ // Reset position in list.
+ // The list has no elements anymore. m_nPosition is normally the current position in list for nextElement!
+ // But a position of 0 in a list of 0 items is an invalid state. This constellation can't work in future.
+ // End of enumeration is arrived!
+ // (see hasMoreElements() for more details...)
+ m_nPosition = 0;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/helper/oframes.cxx b/framework/source/helper/oframes.cxx
new file mode 100644
index 000000000..a58949852
--- /dev/null
+++ b/framework/source/helper/oframes.cxx
@@ -0,0 +1,370 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/oframes.hxx>
+
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+
+namespace framework{
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::cppu;
+using namespace ::osl;
+using namespace ::std;
+
+// constructor
+
+OFrames::OFrames( const css::uno::Reference< XFrame >& xOwner ,
+ FrameContainer* pFrameContainer )
+ : m_xOwner ( xOwner )
+ , m_pFrameContainer ( pFrameContainer )
+ , m_bRecursiveSearchProtection( false )
+{
+ // An instance of this class can only work with valid initialization.
+ // We share the mutex with our owner class, need a valid factory to instantiate new services and
+ // use the access to our owner for some operations.
+ SAL_WARN_IF( !xOwner.is() || !pFrameContainer, "fwk", "OFrames::OFrames(): Invalid parameter detected!" );
+}
+
+// (protected!) destructor
+
+OFrames::~OFrames()
+{
+ // Reset instance, free memory...
+ impl_resetObject();
+}
+
+// XFrames
+void SAL_CALL OFrames::append( const css::uno::Reference< XFrame >& xFrame )
+{
+ SolarMutexGuard g;
+
+ // Safe impossible cases
+ // Method is not defined for ALL incoming parameters!
+ SAL_WARN_IF( !xFrame.is(), "fwk", "OFrames::append(): Invalid parameter detected!" );
+
+ // Do the follow only, if owner instance valid!
+ // Lock owner for follow operations - make a "hard reference"!
+ css::uno::Reference< XFramesSupplier > xOwner( m_xOwner.get(), UNO_QUERY );
+ if ( xOwner.is() )
+ {
+ // Append frame to the end of the container ...
+ m_pFrameContainer->append( xFrame );
+ // Set owner of this instance as parent of the new frame in container!
+ xFrame->setCreator( xOwner );
+ }
+ // Else; Do nothing! Our owner is dead.
+ SAL_WARN_IF( !xOwner.is(), "fwk", "OFrames::append():Our owner is dead - you can't append any frames ...!" );
+}
+
+// XFrames
+void SAL_CALL OFrames::remove( const css::uno::Reference< XFrame >& xFrame )
+{
+ SolarMutexGuard g;
+
+ // Safe impossible cases
+ // Method is not defined for ALL incoming parameters!
+ SAL_WARN_IF( !xFrame.is(), "fwk", "OFrames::remove(): Invalid parameter detected!" );
+
+ // Do the follow only, if owner instance valid!
+ // Lock owner for follow operations - make a "hard reference"!
+ css::uno::Reference< XFramesSupplier > xOwner( m_xOwner.get(), UNO_QUERY );
+ if ( xOwner.is() )
+ {
+ // Search frame and remove it from container ...
+ m_pFrameContainer->remove( xFrame );
+ // Don't reset owner-property of removed frame!
+ // This must do the caller of this method himself.
+ // See documentation of interface XFrames for further information.
+ }
+ // Else; Do nothing! Our owner is dead.
+ SAL_WARN_IF( !xOwner.is(), "fwk", "OFrames::remove(): Our owner is dead - you can't remove any frames ...!" );
+}
+
+// XFrames
+Sequence< css::uno::Reference< XFrame > > SAL_CALL OFrames::queryFrames( sal_Int32 nSearchFlags )
+{
+ SolarMutexGuard g;
+
+ // Safe impossible cases
+ // Method is not defined for ALL incoming parameters!
+ SAL_WARN_IF( !impldbg_checkParameter_queryFrames( nSearchFlags ), "fwk", "OFrames::queryFrames(): Invalid parameter detected!" );
+
+ // Set default return value. (empty sequence)
+ Sequence< css::uno::Reference< XFrame > > seqFrames;
+
+ // Do the follow only, if owner instance valid.
+ // Lock owner for follow operations - make a "hard reference"!
+ css::uno::Reference< XFrame > xOwner( m_xOwner.get(), UNO_QUERY );
+ if ( xOwner.is() )
+ {
+ // Work only, if search was not started here ...!
+ if( !m_bRecursiveSearchProtection )
+ {
+ // This class is a helper for services, which must implement XFrames.
+ // His parent and children are MY parent and children to.
+ // All searchflags are supported by this implementation!
+ // If some flags should not be supported - don't call me with this flags!!!
+
+ // Search with AUTO-flag is not supported yet!
+ // We think about right implementation.
+ SAL_WARN_IF( (nSearchFlags & FrameSearchFlag::AUTO), "fwk", "OFrames::queryFrames(): Search with AUTO-flag is not supported yet!" );
+
+ // Search for ALL and GLOBAL is superfluous!
+ // We support all necessary flags, from which these two flags are derived.
+ // ALL = PARENT + SELF + CHILDREN + SIBLINGS
+ // GLOBAL = ALL + TASKS
+
+ // Add parent to list ... if any exist!
+ if( nSearchFlags & FrameSearchFlag::PARENT )
+ {
+ css::uno::Reference< XFrame > xParent = xOwner->getCreator();
+ if( xParent.is() )
+ {
+ impl_appendSequence( seqFrames, { xParent } );
+ }
+ }
+
+ // Add owner to list if SELF is searched.
+ if( nSearchFlags & FrameSearchFlag::SELF )
+ {
+ impl_appendSequence( seqFrames, { xOwner } );
+ }
+
+ // Add SIBLINGS to list.
+ if( nSearchFlags & FrameSearchFlag::SIBLINGS )
+ {
+ // Else; start a new search.
+ // Protect this instance against recursive calls from parents.
+ m_bRecursiveSearchProtection = true;
+ // Ask parent of my owner for frames and append results to return list.
+ css::uno::Reference< XFramesSupplier > xParent = xOwner->getCreator();
+ // If a parent exist ...
+ if ( xParent.is() )
+ {
+ // ... ask him for right frames.
+ impl_appendSequence( seqFrames, xParent->getFrames()->queryFrames( nSearchFlags ) );
+ }
+ // We have all searched information.
+ // Reset protection-mode.
+ m_bRecursiveSearchProtection = false;
+ }
+
+ // If searched for children, step over all elements in container and collect the information.
+ if ( nSearchFlags & FrameSearchFlag::CHILDREN )
+ {
+ // Don't search for parents, siblings and self at children!
+ // These things are supported by this instance himself.
+ sal_Int32 const nChildSearchFlags = FrameSearchFlag::SELF | FrameSearchFlag::CHILDREN;
+ // Step over all items of container and ask children for frames.
+ sal_uInt32 nCount = m_pFrameContainer->getCount();
+ for ( sal_uInt32 nIndex=0; nIndex<nCount; ++nIndex )
+ {
+ // We don't must control this conversion.
+ // We have done this at append()!
+ css::uno::Reference< XFramesSupplier > xItem( (*m_pFrameContainer)[nIndex], UNO_QUERY );
+ impl_appendSequence( seqFrames, xItem->getFrames()->queryFrames( nChildSearchFlags ) );
+ }
+ }
+ }
+ }
+ // Else; Do nothing! Our owner is dead.
+ SAL_WARN_IF( !xOwner.is(), "fwk", "OFrames::queryFrames(): Our owner is dead - you can't query for frames ...!" );
+
+ // Return result of this operation.
+ return seqFrames;
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL OFrames::getCount()
+{
+ SolarMutexGuard g;
+
+ // Set default return value.
+ sal_Int32 nCount = 0;
+
+ // Do the follow only, if owner instance valid.
+ // Lock owner for follow operations - make a "hard reference"!
+ css::uno::Reference< XFrame > xOwner( m_xOwner.get(), UNO_QUERY );
+ if ( xOwner.is() )
+ {
+ // Set CURRENT size of container for return.
+ nCount = m_pFrameContainer->getCount();
+ }
+
+ // Return result.
+ return nCount;
+}
+
+// XIndexAccess
+
+Any SAL_CALL OFrames::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ sal_uInt32 nCount = m_pFrameContainer->getCount();
+ if ( nIndex < 0 || ( sal::static_int_cast< sal_uInt32 >( nIndex ) >= nCount ))
+ throw IndexOutOfBoundsException("OFrames::getByIndex - Index out of bounds",
+ static_cast<OWeakObject *>(this) );
+
+ // Set default return value.
+ Any aReturnValue;
+
+ // Do the follow only, if owner instance valid.
+ // Lock owner for follow operations - make a "hard reference"!
+ css::uno::Reference< XFrame > xOwner( m_xOwner.get(), UNO_QUERY );
+ if ( xOwner.is() )
+ {
+ // Get element form container.
+ // (If index not valid, FrameContainer return NULL!)
+ aReturnValue <<= (*m_pFrameContainer)[nIndex];
+ }
+
+ // Return result of this operation.
+ return aReturnValue;
+}
+
+// XElementAccess
+Type SAL_CALL OFrames::getElementType()
+{
+ // This "container" support XFrame-interfaces only!
+ return cppu::UnoType<XFrame>::get();
+}
+
+// XElementAccess
+sal_Bool SAL_CALL OFrames::hasElements()
+{
+ SolarMutexGuard g;
+
+ // Set default return value.
+ bool bHasElements = false;
+ // Do the follow only, if owner instance valid.
+ // Lock owner for follow operations - make a "hard reference"!
+ css::uno::Reference< XFrame > xOwner( m_xOwner.get(), UNO_QUERY );
+ if ( xOwner.is() )
+ {
+ // If some elements exist ...
+ if ( m_pFrameContainer->getCount() > 0 )
+ {
+ // ... change this state value!
+ bHasElements = true;
+ }
+ }
+ // Return result of this operation.
+ return bHasElements;
+}
+
+// protected method
+
+void OFrames::impl_resetObject()
+{
+ // Attention:
+ // Write this for multiple calls - NOT AT THE SAME TIME - but for more than one call again)!
+ // It exist two ways to call this method. From destructor and from disposing().
+ // I can't say, which one is the first. Normally the disposing-call - but other way...
+
+ // This instance can't work if the weakreference to owner is invalid!
+ // Destroy this to reset this object.
+ m_xOwner.clear();
+ // Reset pointer to shared container to!
+ m_pFrameContainer = nullptr;
+}
+
+void OFrames::impl_appendSequence( Sequence< css::uno::Reference< XFrame > >& seqDestination ,
+ const Sequence< css::uno::Reference< XFrame > >& seqSource )
+{
+ // Get some information about the sequences.
+ sal_Int32 nSourceCount = seqSource.getLength();
+ sal_Int32 nDestinationCount = seqDestination.getLength();
+ const css::uno::Reference< XFrame >* pSourceAccess = seqSource.getConstArray();
+ css::uno::Reference< XFrame >* pDestinationAccess = seqDestination.getArray();
+
+ // Get memory for result list.
+ Sequence< css::uno::Reference< XFrame > > seqResult ( nSourceCount + nDestinationCount );
+ css::uno::Reference< XFrame >* pResultAccess = seqResult.getArray();
+ sal_Int32 nResultPosition = 0;
+
+ // Copy all items from first sequence.
+ for ( sal_Int32 nSourcePosition=0; nSourcePosition<nSourceCount; ++nSourcePosition )
+ {
+ pResultAccess[nResultPosition] = pSourceAccess[nSourcePosition];
+ ++nResultPosition;
+ }
+
+ // Don't manipulate nResultPosition between these two loops!
+ // It's the current position in the result list.
+
+ // Copy all items from second sequence.
+ for ( sal_Int32 nDestinationPosition=0; nDestinationPosition<nDestinationCount; ++nDestinationPosition )
+ {
+ pResultAccess[nResultPosition] = pDestinationAccess[nDestinationPosition];
+ ++nResultPosition;
+ }
+
+ // Return result of this operation.
+ seqDestination.realloc( 0 );
+ seqDestination = seqResult;
+}
+
+// debug methods
+
+/*-----------------------------------------------------------------------------------------------------------------
+ The follow methods checks the parameter for other functions. If a parameter or his value is non valid,
+ we return "sal_False". (else sal_True) This mechanism is used to throw an ASSERT!
+
+ ATTENTION
+
+ If you miss a test for one of this parameters, contact the author or add it himself !(?)
+ But ... look for right testing! See using of this methods!
+-----------------------------------------------------------------------------------------------------------------*/
+
+// A search for frames must initiate with right flags.
+// Some one are superfluous and not supported yet. But here we control only the range of incoming parameter!
+bool OFrames::impldbg_checkParameter_queryFrames( sal_Int32 nSearchFlags )
+{
+ // Set default return value.
+ bool bOK = true;
+ // Check parameter.
+ if (
+ ( nSearchFlags != FrameSearchFlag::AUTO ) &&
+ ( !( nSearchFlags & FrameSearchFlag::PARENT ) ) &&
+ ( !( nSearchFlags & FrameSearchFlag::SELF ) ) &&
+ ( !( nSearchFlags & FrameSearchFlag::CHILDREN ) ) &&
+ ( !( nSearchFlags & FrameSearchFlag::CREATE ) ) &&
+ ( !( nSearchFlags & FrameSearchFlag::SIBLINGS ) ) &&
+ ( !( nSearchFlags & FrameSearchFlag::TASKS ) ) &&
+ ( !( nSearchFlags & FrameSearchFlag::ALL ) ) &&
+ ( !( nSearchFlags & FrameSearchFlag::GLOBAL ) )
+ )
+ {
+ bOK = false;
+ }
+ // Return result of check.
+ return bOK;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/helper/persistentwindowstate.cxx b/framework/source/helper/persistentwindowstate.cxx
new file mode 100644
index 000000000..2f7c99719
--- /dev/null
+++ b/framework/source/helper/persistentwindowstate.cxx
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/persistentwindowstate.hxx>
+
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/configurationhelper.hxx>
+#include <utility>
+#include <vcl/window.hxx>
+#include <vcl/syswin.hxx>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/wrkwin.hxx>
+
+namespace framework{
+
+PersistentWindowState::PersistentWindowState(css::uno::Reference< css::uno::XComponentContext > xContext)
+ : m_xContext (std::move(xContext ))
+ , m_bWindowStateAlreadySet(false )
+{
+}
+
+PersistentWindowState::~PersistentWindowState()
+{
+}
+
+void SAL_CALL PersistentWindowState::initialize(const css::uno::Sequence< css::uno::Any >& lArguments)
+{
+ // check arguments
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ if (!lArguments.hasElements())
+ throw css::lang::IllegalArgumentException(
+ "Empty argument list!",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 1);
+
+ lArguments[0] >>= xFrame;
+ if (!xFrame.is())
+ throw css::lang::IllegalArgumentException(
+ "No valid frame specified!",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 1);
+
+ {
+ SolarMutexGuard g;
+ m_xFrame = xFrame;
+ }
+
+ // start listening
+ xFrame->addFrameActionListener(this);
+}
+
+void SAL_CALL PersistentWindowState::frameAction(const css::frame::FrameActionEvent& aEvent)
+{
+ // We don't want to do this stuff when being used through LibreOfficeKit
+ if( comphelper::LibreOfficeKit::isActive() )
+ return;
+
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ bool bRestoreWindowState;
+ {
+ SolarMutexGuard g;
+ xContext = m_xContext;
+ xFrame.set(m_xFrame.get(), css::uno::UNO_QUERY);
+ bRestoreWindowState = !m_bWindowStateAlreadySet;
+ }
+
+ // frame already gone ? We hold it weak only ...
+ if (!xFrame.is())
+ return;
+
+ // no window -> no position and size available
+ css::uno::Reference< css::awt::XWindow > xWindow = xFrame->getContainerWindow();
+ if (!xWindow.is())
+ return;
+
+ // unknown module -> no configuration available!
+ OUString sModuleName = PersistentWindowState::implst_identifyModule(xContext, xFrame);
+ if (sModuleName.isEmpty())
+ return;
+
+ switch(aEvent.Action)
+ {
+ case css::frame::FrameAction_COMPONENT_ATTACHED :
+ {
+ if (bRestoreWindowState)
+ {
+ OUString sWindowState = PersistentWindowState::implst_getWindowStateFromConfig(xContext, sModuleName);
+ PersistentWindowState::implst_setWindowStateOnWindow(xWindow,sWindowState);
+ SolarMutexGuard g;
+ m_bWindowStateAlreadySet = true;
+ }
+ }
+ break;
+
+ case css::frame::FrameAction_COMPONENT_REATTACHED :
+ {
+ // nothing todo here, because it's not allowed to change position and size
+ // of an already existing frame!
+ }
+ break;
+
+ case css::frame::FrameAction_COMPONENT_DETACHING :
+ {
+ OUString sWindowState = PersistentWindowState::implst_getWindowStateFromWindow(xWindow);
+ PersistentWindowState::implst_setWindowStateOnConfig(xContext, sModuleName, sWindowState);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void SAL_CALL PersistentWindowState::disposing(const css::lang::EventObject&)
+{
+ css::uno::Reference< css::frame::XFrame > xFrame(m_xFrame.get(), css::uno::UNO_QUERY);
+ if (xFrame.is())
+ xFrame->removeFrameActionListener(this);
+
+ // nothing todo here - because we hold the frame as weak reference only
+}
+
+OUString PersistentWindowState::implst_identifyModule(const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::frame::XFrame >& xFrame)
+{
+ OUString sModuleName;
+
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager =
+ css::frame::ModuleManager::create( rxContext );
+
+ try
+ {
+ sModuleName = xModuleManager->identify(xFrame);
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { sModuleName.clear(); }
+
+ return sModuleName;
+}
+
+OUString PersistentWindowState::implst_getWindowStateFromConfig(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ std::u16string_view sModuleName)
+{
+ OUString sWindowState;
+ try
+ {
+ ::comphelper::ConfigurationHelper::readDirectKey(rxContext,
+ "org.openoffice.Setup/",
+ OUString::Concat("Office/Factories/*[\"") + sModuleName + "\"]",
+ "ooSetupFactoryWindowAttributes",
+ ::comphelper::EConfigurationModes::ReadOnly) >>= sWindowState;
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { sWindowState.clear(); }
+
+ return sWindowState;
+}
+
+void PersistentWindowState::implst_setWindowStateOnConfig(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ std::u16string_view sModuleName, const OUString& sWindowState)
+{
+ try
+ {
+ ::comphelper::ConfigurationHelper::writeDirectKey(rxContext,
+ "org.openoffice.Setup/",
+ OUString::Concat("Office/Factories/*[\"") + sModuleName + "\"]",
+ "ooSetupFactoryWindowAttributes",
+ css::uno::Any(sWindowState),
+ ::comphelper::EConfigurationModes::Standard);
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ {}
+}
+
+OUString PersistentWindowState::implst_getWindowStateFromWindow(const css::uno::Reference< css::awt::XWindow >& xWindow)
+{
+ OUString sWindowState;
+
+ if (xWindow.is())
+ {
+ // SOLAR SAFE -> ------------------------
+ SolarMutexGuard aSolarGuard;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ // check for system window is necessary to guarantee correct pointer cast!
+ if ( pWindow && pWindow->IsSystemWindow() )
+ {
+ WindowStateMask const nMask = WindowStateMask::All & ~WindowStateMask::Minimized;
+ sWindowState = OStringToOUString(
+ static_cast<SystemWindow*>(pWindow.get())->GetWindowState(nMask),
+ RTL_TEXTENCODING_UTF8);
+ }
+ // <- SOLAR SAFE ------------------------
+ }
+
+ return sWindowState;
+}
+
+void PersistentWindowState::implst_setWindowStateOnWindow(const css::uno::Reference< css::awt::XWindow >& xWindow ,
+ std::u16string_view sWindowState)
+{
+ if (
+ (!xWindow.is() ) ||
+ ( sWindowState.empty() )
+ )
+ return;
+
+ // SOLAR SAFE -> ------------------------
+ SolarMutexGuard aSolarGuard;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ if (!pWindow)
+ return;
+
+ // check for system and work window - it's necessary to guarantee correct pointer cast!
+ bool bSystemWindow = pWindow->IsSystemWindow();
+ bool bWorkWindow = (pWindow->GetType() == WindowType::WORKWINDOW);
+
+ if (!bSystemWindow && !bWorkWindow)
+ return;
+
+ SystemWindow* pSystemWindow = static_cast<SystemWindow*>(pWindow.get());
+ WorkWindow* pWorkWindow = static_cast<WorkWindow* >(pWindow.get());
+
+ // don't save this special state!
+ if (pWorkWindow->IsMinimized())
+ return;
+
+ OUString sOldWindowState = OStringToOUString( pSystemWindow->GetWindowState(), RTL_TEXTENCODING_ASCII_US );
+ if ( sOldWindowState != sWindowState )
+ pSystemWindow->SetWindowState(OUStringToOString(sWindowState,RTL_TEXTENCODING_UTF8));
+ // <- SOLAR SAFE ------------------------
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/helper/statusindicator.cxx b/framework/source/helper/statusindicator.cxx
new file mode 100644
index 000000000..2deb4a0ae
--- /dev/null
+++ b/framework/source/helper/statusindicator.cxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/lok.hxx>
+#include <helper/statusindicator.hxx>
+
+namespace framework
+{
+StatusIndicator::StatusIndicator(StatusIndicatorFactory* pFactory)
+ : m_xFactory(pFactory)
+ , m_nRange(100)
+ , m_nLastCallbackPercent(-1)
+{
+}
+
+StatusIndicator::~StatusIndicator() {}
+
+void SAL_CALL StatusIndicator::start(const OUString& sText, sal_Int32 nRange)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ m_nRange = nRange;
+ m_nLastCallbackPercent = -1;
+
+ comphelper::LibreOfficeKit::statusIndicatorStart(sText);
+ }
+#if !defined(IOS) && !defined(ANDROID)
+ css::uno::Reference<css::task::XStatusIndicatorFactory> xFactory(m_xFactory);
+ if (xFactory.is())
+ {
+ StatusIndicatorFactory* pFactory = static_cast<StatusIndicatorFactory*>(xFactory.get());
+ pFactory->start(this, sText, nRange);
+ }
+#else
+ (void)sText;
+#endif
+}
+
+void SAL_CALL StatusIndicator::end()
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ comphelper::LibreOfficeKit::statusIndicatorFinish();
+ }
+#if !defined(IOS) && !defined(ANDROID)
+ css::uno::Reference<css::task::XStatusIndicatorFactory> xFactory(m_xFactory);
+ if (xFactory.is())
+ {
+ StatusIndicatorFactory* pFactory = static_cast<StatusIndicatorFactory*>(xFactory.get());
+ pFactory->end(this);
+ }
+#endif
+}
+
+void SAL_CALL StatusIndicator::reset()
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+#if !defined(IOS) && !defined(ANDROID)
+ css::uno::Reference<css::task::XStatusIndicatorFactory> xFactory(m_xFactory);
+ if (xFactory.is())
+ {
+ StatusIndicatorFactory* pFactory = static_cast<StatusIndicatorFactory*>(xFactory.get());
+ pFactory->reset(this);
+ }
+#endif
+}
+
+void SAL_CALL StatusIndicator::setText(const OUString& sText)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+#if !defined(IOS) && !defined(ANDROID)
+ css::uno::Reference<css::task::XStatusIndicatorFactory> xFactory(m_xFactory);
+ if (xFactory.is())
+ {
+ StatusIndicatorFactory* pFactory = static_cast<StatusIndicatorFactory*>(xFactory.get());
+ pFactory->setText(this, sText);
+ }
+#else
+ (void)sText;
+#endif
+}
+
+void SAL_CALL StatusIndicator::setValue(sal_Int32 nValue)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (m_nRange > 0)
+ {
+ int nPercent = (100 * nValue) / m_nRange;
+ if (nPercent >= m_nLastCallbackPercent)
+ {
+ comphelper::LibreOfficeKit::statusIndicatorSetValue(nPercent);
+ m_nLastCallbackPercent = nPercent;
+ }
+ }
+ return;
+ }
+#if !defined(IOS) && !defined(ANDROID)
+ css::uno::Reference<css::task::XStatusIndicatorFactory> xFactory(m_xFactory);
+ if (xFactory.is())
+ {
+ StatusIndicatorFactory* pFactory = static_cast<StatusIndicatorFactory*>(xFactory.get());
+ pFactory->setValue(this, nValue);
+ }
+#endif
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/helper/statusindicatorfactory.cxx b/framework/source/helper/statusindicatorfactory.cxx
new file mode 100644
index 000000000..e9e54df6b
--- /dev/null
+++ b/framework/source/helper/statusindicatorfactory.cxx
@@ -0,0 +1,577 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <algorithm>
+#include <utility>
+#include <helper/statusindicatorfactory.hxx>
+#include <helper/statusindicator.hxx>
+#include <helper/vclstatusindicator.hxx>
+#include <properties.h>
+
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XLayoutManager2.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <comphelper/sequenceashashmap.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <vcl/svapp.hxx>
+#include <mutex>
+#include <rtl/ref.hxx>
+
+#include <officecfg/Office/Common.hxx>
+
+namespace framework{
+
+sal_Int32 StatusIndicatorFactory::m_nInReschedule = 0; ///< static counter for rescheduling
+
+constexpr OUStringLiteral PROGRESS_RESOURCE = u"private:resource/progressbar/progressbar";
+
+StatusIndicatorFactory::StatusIndicatorFactory(css::uno::Reference< css::uno::XComponentContext > xContext)
+ : m_xContext (std::move(xContext ))
+ , m_bAllowReschedule (false)
+ , m_bAllowParentShow (false)
+ , m_bDisableReschedule(false)
+{
+}
+
+StatusIndicatorFactory::~StatusIndicatorFactory()
+{
+ impl_stopWakeUpThread();
+}
+
+void SAL_CALL StatusIndicatorFactory::initialize(const css::uno::Sequence< css::uno::Any >& lArguments)
+{
+ if (lArguments.hasElements()) {
+ std::scoped_lock g(m_mutex);
+
+ css::uno::Reference< css::frame::XFrame > xTmpFrame;
+ css::uno::Reference< css::awt::XWindow > xTmpWindow;
+ bool b1 = lArguments[0] >>= xTmpFrame;
+ bool b2 = lArguments[0] >>= xTmpWindow;
+ if (lArguments.getLength() == 3 && b1) {
+ // it's the first service constructor "createWithFrame"
+ m_xFrame = xTmpFrame;
+ lArguments[1] >>= m_bDisableReschedule;
+ lArguments[2] >>= m_bAllowParentShow;
+ } else if (lArguments.getLength() == 3 && b2) {
+ // it's the second service constructor "createWithWindow"
+ m_xPluggWindow = xTmpWindow;
+ lArguments[1] >>= m_bDisableReschedule;
+ lArguments[2] >>= m_bAllowParentShow;
+ } else {
+ // it's an old-style initialisation using properties
+ ::comphelper::SequenceAsHashMap lArgs(lArguments);
+
+ m_xFrame = lArgs.getUnpackedValueOrDefault("Frame" , css::uno::Reference< css::frame::XFrame >());
+ m_xPluggWindow = lArgs.getUnpackedValueOrDefault("Window" , css::uno::Reference< css::awt::XWindow >() );
+ m_bAllowParentShow = lArgs.getUnpackedValueOrDefault("AllowParentShow" , false );
+ m_bDisableReschedule = lArgs.getUnpackedValueOrDefault("DisableReschedule", false );
+ }
+ }
+
+#ifdef EMSCRIPTEN
+ m_bDisableReschedule = true;
+#endif
+ impl_createProgress();
+}
+
+css::uno::Reference< css::task::XStatusIndicator > SAL_CALL StatusIndicatorFactory::createStatusIndicator()
+{
+ return new StatusIndicator(this);
+}
+
+void SAL_CALL StatusIndicatorFactory::update()
+{
+ std::scoped_lock g(m_mutex);
+ m_bAllowReschedule = true;
+}
+
+void StatusIndicatorFactory::start(const css::uno::Reference< css::task::XStatusIndicator >& xChild,
+ const OUString& sText ,
+ sal_Int32 nRange)
+{
+ css::uno::Reference< css::task::XStatusIndicator > xProgress;
+ // SAFE -> ----------------------------------
+ {
+ std::scoped_lock aWriteLock(m_mutex);
+
+ // create new info structure for this child or move it to the front of our stack
+ IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild);
+ if (pItem != m_aStack.end())
+ m_aStack.erase(pItem);
+ IndicatorInfo aInfo(xChild, sText);
+ m_aStack.push_back (aInfo );
+
+ m_xActiveChild = xChild;
+ xProgress = m_xProgress;
+ }
+ // <- SAFE ----------------------------------
+
+ implts_makeParentVisibleIfAllowed();
+
+ if (xProgress.is())
+ xProgress->start(sText, nRange);
+
+ impl_startWakeUpThread();
+ impl_reschedule(true);
+}
+
+void StatusIndicatorFactory::reset(const css::uno::Reference< css::task::XStatusIndicator >& xChild)
+{
+ css::uno::Reference< css::task::XStatusIndicator > xActive;
+ css::uno::Reference< css::task::XStatusIndicator > xProgress;
+ // SAFE -> ----------------------------------
+ {
+ std::scoped_lock aReadLock(m_mutex);
+
+ // reset the internal info structure related to this child
+ IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild);
+ if (pItem != m_aStack.end())
+ {
+ pItem->m_nValue = 0;
+ pItem->m_sText.clear();
+ }
+
+ xActive = m_xActiveChild;
+ xProgress = m_xProgress;
+ }
+ // <- SAFE ----------------------------------
+
+ // not the top most child => don't change UI
+ // But don't forget Reschedule!
+ if (
+ (xChild == xActive) &&
+ (xProgress.is() )
+ )
+ xProgress->reset();
+
+ impl_reschedule(true);
+}
+
+void StatusIndicatorFactory::end(const css::uno::Reference< css::task::XStatusIndicator >& xChild)
+{
+ css::uno::Reference< css::task::XStatusIndicator > xActive;
+ css::uno::Reference< css::task::XStatusIndicator > xProgress;
+ OUString sText;
+ sal_Int32 nValue = 0;
+ // SAFE -> ----------------------------------
+ {
+ std::scoped_lock aWriteLock(m_mutex);
+
+ // remove this child from our stack
+ IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild);
+ if (pItem != m_aStack.end())
+ m_aStack.erase(pItem);
+
+ // activate next child ... or finish the progress if there is no further one.
+ m_xActiveChild.clear();
+ IndicatorStack::reverse_iterator pNext = m_aStack.rbegin();
+ if (pNext != m_aStack.rend())
+ {
+ m_xActiveChild = pNext->m_xIndicator;
+ sText = pNext->m_sText;
+ nValue = pNext->m_nValue;
+ }
+
+ xActive = m_xActiveChild;
+ xProgress = m_xProgress;
+ }
+ // <- SAFE ----------------------------------
+
+ if (xActive.is())
+ {
+ // There is at least one further child indicator.
+ // Actualize our progress, so it shows these values from now.
+ if (xProgress.is())
+ {
+ xProgress->setText (sText );
+ xProgress->setValue(nValue);
+ }
+ }
+ else
+ {
+ // Our stack is empty. No further child exists.
+ // Se we must "end" our progress really
+ if (xProgress.is())
+ xProgress->end();
+ // Now hide the progress bar again.
+ impl_hideProgress();
+
+ impl_stopWakeUpThread();
+ }
+
+ impl_reschedule(true);
+}
+
+void StatusIndicatorFactory::setText(const css::uno::Reference< css::task::XStatusIndicator >& xChild,
+ const OUString& sText )
+{
+ css::uno::Reference< css::task::XStatusIndicator > xActive;
+ css::uno::Reference< css::task::XStatusIndicator > xProgress;
+ // SAFE -> ----------------------------------
+ {
+ std::scoped_lock aWriteLock(m_mutex);
+
+ IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild);
+ if (pItem != m_aStack.end())
+ pItem->m_sText = sText;
+
+ xActive = m_xActiveChild;
+ xProgress = m_xProgress;
+ }
+ // SAFE -> ----------------------------------
+
+ // paint only the top most indicator
+ // but don't forget to Reschedule!
+ if (
+ (xChild == xActive) &&
+ (xProgress.is() )
+ )
+ {
+ xProgress->setText(sText);
+ }
+
+ impl_reschedule(true);
+}
+
+void StatusIndicatorFactory::setValue( const css::uno::Reference< css::task::XStatusIndicator >& xChild ,
+ sal_Int32 nValue )
+{
+ sal_Int32 nOldValue = 0;
+ css::uno::Reference< css::task::XStatusIndicator > xActive;
+ css::uno::Reference< css::task::XStatusIndicator > xProgress;
+ // SAFE -> ----------------------------------
+ {
+ std::scoped_lock aWriteLock(m_mutex);
+
+ IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild);
+ if (pItem != m_aStack.end())
+ {
+ nOldValue = pItem->m_nValue;
+ pItem->m_nValue = nValue;
+ }
+
+ xActive = m_xActiveChild;
+ xProgress = m_xProgress;
+ }
+ // SAFE -> ----------------------------------
+
+ if (
+ (xChild == xActive) &&
+ (nOldValue != nValue ) &&
+ (xProgress.is() )
+ )
+ {
+ xProgress->setValue(nValue);
+ }
+
+ impl_reschedule(false);
+}
+
+void StatusIndicatorFactory::implts_makeParentVisibleIfAllowed()
+{
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ css::uno::Reference< css::awt::XWindow > xPluggWindow;
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ // SAFE -> ----------------------------------
+ {
+ std::scoped_lock aReadLock(m_mutex);
+
+ if (!m_bAllowParentShow)
+ return;
+
+ xFrame = m_xFrame;
+ xPluggWindow = m_xPluggWindow;
+ xContext = m_xContext;
+ }
+ // <- SAFE ----------------------------------
+
+ css::uno::Reference< css::awt::XWindow > xParentWindow;
+ if (xFrame.is())
+ xParentWindow = xFrame->getContainerWindow();
+ else
+ xParentWindow = xPluggWindow;
+
+ // don't disturb user in case he put the loading document into the background!
+ // Suppress any setVisible() or toFront() call in case the initial show was
+ // already made.
+ css::uno::Reference< css::awt::XWindow2 > xVisibleCheck(xParentWindow, css::uno::UNO_QUERY);
+ bool bIsVisible = false;
+ if (xVisibleCheck.is())
+ bIsVisible = xVisibleCheck->isVisible();
+
+ if (bIsVisible)
+ {
+ impl_showProgress();
+ return;
+ }
+
+ // Check if the layout manager has been set to invisible state. It this case we are also
+ // not allowed to set the frame visible!
+ css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
+ if (xPropSet.is())
+ {
+ css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
+ xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
+ if (xLayoutManager.is())
+ {
+ if ( !xLayoutManager->isVisible() )
+ return;
+ }
+ }
+
+ // Ok the window should be made visible... because it is not currently visible.
+ // BUT..!
+ // We need a Hack for our applications: They get her progress from the frame directly
+ // on saving documents. Because there is no progress set on the MediaDescriptor.
+ // But that's wrong. In case the document was opened hidden, they should not use any progress .-(
+ // They only possible workaround: don't show the parent window here, if the document was opened hidden.
+ bool bHiddenDoc = false;
+ if (xFrame.is())
+ {
+ css::uno::Reference< css::frame::XController > xController;
+ css::uno::Reference< css::frame::XModel > xModel;
+ xController = xFrame->getController();
+ if (xController.is())
+ xModel = xController->getModel();
+ if (xModel.is())
+ {
+ utl::MediaDescriptor lDocArgs(xModel->getArgs());
+ bHiddenDoc = lDocArgs.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_HIDDEN,
+ false);
+ }
+ }
+
+ if (bHiddenDoc)
+ return;
+
+ // OK: The document was not opened in hidden mode ...
+ // and the window isn't already visible.
+ // Show it and bring it to front.
+ // But before we have to be sure, that our internal used helper progress
+ // is visible too.
+ impl_showProgress();
+
+ SolarMutexGuard aSolarGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xParentWindow);
+ if ( pWindow )
+ {
+ bool bForceFrontAndFocus(officecfg::Office::Common::View::NewDocumentHandling::ForceFocusAndToFront::get());
+ pWindow->Show(true, bForceFrontAndFocus ? ShowFlags::ForegroundTask : ShowFlags::NONE );
+ }
+
+}
+
+void StatusIndicatorFactory::impl_createProgress()
+{
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ css::uno::Reference< css::awt::XWindow > xWindow;
+ // SAFE -> ----------------------------------
+ {
+ std::scoped_lock aReadLock(m_mutex);
+
+ xFrame = m_xFrame;
+ xWindow = m_xPluggWindow;
+ }
+ // <- SAFE ----------------------------------
+
+ css::uno::Reference< css::task::XStatusIndicator > xProgress;
+
+ if (xWindow.is())
+ {
+ // use vcl based progress implementation in plugged mode
+ xProgress = new VCLStatusIndicator(xWindow);
+ }
+ else if (xFrame.is())
+ {
+ // use frame layouted progress implementation
+ css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
+ if (xPropSet.is())
+ {
+ css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
+ xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
+ if (xLayoutManager.is())
+ {
+ xLayoutManager->lock();
+ OUString sPROGRESS_RESOURCE(PROGRESS_RESOURCE);
+ xLayoutManager->createElement( sPROGRESS_RESOURCE );
+ xLayoutManager->hideElement( sPROGRESS_RESOURCE );
+
+ css::uno::Reference< css::ui::XUIElement > xProgressBar = xLayoutManager->getElement(sPROGRESS_RESOURCE);
+ if (xProgressBar.is())
+ xProgress.set(xProgressBar->getRealInterface(), css::uno::UNO_QUERY);
+ xLayoutManager->unlock();
+ }
+ }
+ }
+
+ std::scoped_lock g(m_mutex);
+ m_xProgress = xProgress;
+}
+
+void StatusIndicatorFactory::impl_showProgress()
+{
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ // SAFE -> ----------------------------------
+ {
+ std::scoped_lock aReadLock(m_mutex);
+
+ xFrame = m_xFrame;
+ }
+ // <- SAFE ----------------------------------
+
+ css::uno::Reference< css::task::XStatusIndicator > xProgress;
+
+ if (!xFrame.is())
+ return;
+
+ // use frame layouted progress implementation
+ css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
+ if (xPropSet.is())
+ {
+ css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
+ xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
+ if (xLayoutManager.is())
+ {
+ // Be sure that we have always a progress. It can be that our frame
+ // was recycled and therefore the progress was destroyed!
+ // CreateElement does nothing if there is already a valid progress.
+ OUString sPROGRESS_RESOURCE(PROGRESS_RESOURCE);
+ xLayoutManager->createElement( sPROGRESS_RESOURCE );
+ xLayoutManager->showElement( sPROGRESS_RESOURCE );
+
+ css::uno::Reference< css::ui::XUIElement > xProgressBar = xLayoutManager->getElement(sPROGRESS_RESOURCE);
+ if (xProgressBar.is())
+ xProgress.set(xProgressBar->getRealInterface(), css::uno::UNO_QUERY);
+ }
+ }
+
+ std::scoped_lock g(m_mutex);
+ m_xProgress = xProgress;
+}
+
+void StatusIndicatorFactory::impl_hideProgress()
+{
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ // SAFE -> ----------------------------------
+ {
+ std::scoped_lock aReadLock(m_mutex);
+
+ xFrame = m_xFrame;
+ }
+ // <- SAFE ----------------------------------
+
+ if (xFrame.is())
+ {
+ // use frame layouted progress implementation
+ css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
+ if (xPropSet.is())
+ {
+ css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
+ xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
+ if (xLayoutManager.is())
+ xLayoutManager->hideElement( PROGRESS_RESOURCE );
+ }
+ }
+}
+
+void StatusIndicatorFactory::impl_reschedule(bool bForce)
+{
+ // SAFE ->
+ {
+ std::scoped_lock aReadLock(m_mutex);
+ if (m_bDisableReschedule)
+ return;
+ }
+ // <- SAFE
+
+ bool bReschedule = bForce;
+ if (!bReschedule)
+ {
+ std::scoped_lock g(m_mutex);
+ bReschedule = m_bAllowReschedule;
+ m_bAllowReschedule = false;
+ }
+
+ if (!bReschedule)
+ return;
+
+ static std::mutex rescheduleLock;
+ // SAFE ->
+ std::unique_lock aRescheduleGuard(rescheduleLock);
+
+ if (m_nInReschedule != 0)
+ return;
+
+ // coverity[missing_lock: FALSE] - coverity fails to see the aRescheduleGuard ctor as taking a lock
+ ++m_nInReschedule;
+ aRescheduleGuard.unlock();
+ // <- SAFE
+
+ {
+ SolarMutexGuard g;
+ Application::Reschedule(true);
+ }
+
+ // SAFE ->
+ aRescheduleGuard.lock();
+ --m_nInReschedule;
+}
+
+void StatusIndicatorFactory::impl_startWakeUpThread()
+{
+ std::scoped_lock g(m_mutex);
+
+ if (m_bDisableReschedule)
+ return;
+
+ if (!m_pWakeUp.is())
+ {
+ m_pWakeUp = new WakeUpThread(this);
+ m_pWakeUp->launch();
+ }
+}
+
+void StatusIndicatorFactory::impl_stopWakeUpThread()
+{
+ rtl::Reference<WakeUpThread> wakeUp;
+ {
+ std::scoped_lock g(m_mutex);
+ std::swap(wakeUp, m_pWakeUp);
+ }
+ if (wakeUp.is())
+ {
+ wakeUp->stop();
+ }
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_StatusIndicatorFactory_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new framework::StatusIndicatorFactory(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/helper/tagwindowasmodified.cxx b/framework/source/helper/tagwindowasmodified.cxx
new file mode 100644
index 000000000..b113c4b96
--- /dev/null
+++ b/framework/source/helper/tagwindowasmodified.cxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/tagwindowasmodified.hxx>
+
+#include <com/sun/star/awt/XWindow.hpp>
+
+#include <com/sun/star/frame/FrameAction.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/wintypes.hxx>
+
+namespace framework{
+
+TagWindowAsModified::TagWindowAsModified()
+{
+}
+
+TagWindowAsModified::~TagWindowAsModified()
+{
+}
+
+void SAL_CALL TagWindowAsModified::initialize(const css::uno::Sequence< css::uno::Any >& lArguments)
+{
+ css::uno::Reference< css::frame::XFrame > xFrame;
+
+ if (lArguments.hasElements())
+ lArguments[0] >>= xFrame;
+
+ if (!xFrame)
+ return;
+
+ m_xFrame = xFrame;
+ xFrame->addFrameActionListener(this);
+ impl_update (xFrame);
+}
+
+void SAL_CALL TagWindowAsModified::modified(const css::lang::EventObject& aEvent)
+{
+ if (!m_xModel || !m_xWindow || aEvent.Source != m_xModel)
+ return;
+
+ bool bModified = m_xModel->isModified ();
+
+ // SYNCHRONIZED ->
+ SolarMutexGuard aSolarGuard;
+
+ if (m_xWindow->isDisposed())
+ return;
+
+ if (bModified)
+ m_xWindow->SetExtendedStyle(WindowExtendedStyle::DocModified);
+ else
+ m_xWindow->SetExtendedStyle(WindowExtendedStyle::NONE);
+ // <- SYNCHRONIZED
+}
+
+void SAL_CALL TagWindowAsModified::frameAction(const css::frame::FrameActionEvent& aEvent)
+{
+ if (
+ (aEvent.Action != css::frame::FrameAction_COMPONENT_REATTACHED) &&
+ (aEvent.Action != css::frame::FrameAction_COMPONENT_ATTACHED )
+ )
+ return;
+
+ if ( aEvent.Source != m_xFrame )
+ return;
+
+ impl_update (m_xFrame);
+}
+
+void SAL_CALL TagWindowAsModified::disposing(const css::lang::EventObject& aEvent)
+{
+ SolarMutexGuard g;
+
+ if (m_xFrame && aEvent.Source == m_xFrame)
+ {
+ m_xFrame->removeFrameActionListener(this);
+ m_xFrame.clear();
+ return;
+ }
+
+ if (m_xModel && aEvent.Source == m_xModel)
+ {
+ m_xModel->removeModifyListener(this);
+ m_xModel.clear();
+ return;
+ }
+}
+
+void TagWindowAsModified::impl_update (const css::uno::Reference< css::frame::XFrame >& xFrame)
+{
+ if (!xFrame)
+ return;
+
+ css::uno::Reference< css::awt::XWindow > xWindow = xFrame->getContainerWindow ();
+ css::uno::Reference< css::frame::XController > xController = xFrame->getController ();
+ css::uno::Reference< css::util::XModifiable > xModel;
+ if (xController.is ())
+ xModel = css::uno::Reference< css::util::XModifiable >(xController->getModel(), css::uno::UNO_QUERY);
+
+ if (!xWindow || !xModel)
+ return;
+
+ {
+ SolarMutexGuard g;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ bool bSystemWindow = pWindow->IsSystemWindow();
+ bool bWorkWindow = (pWindow->GetType() == WindowType::WORKWINDOW);
+ if (!bSystemWindow && !bWorkWindow)
+ return;
+
+ if (m_xModel)
+ m_xModel->removeModifyListener (this);
+
+ // Note: frame was set as member outside ! we have to refresh connections
+ // regarding window and model only here.
+ m_xWindow = pWindow;
+ m_xModel = xModel;
+ }
+
+ m_xModel->addModifyListener (this);
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/helper/titlebarupdate.cxx b/framework/source/helper/titlebarupdate.cxx
new file mode 100644
index 000000000..a9f27bd4a
--- /dev/null
+++ b/framework/source/helper/titlebarupdate.cxx
@@ -0,0 +1,316 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/titlebarupdate.hxx>
+
+#include <properties.h>
+
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XTitle.hpp>
+#include <com/sun/star/frame/XTitleChangeBroadcaster.hpp>
+
+#include <comphelper/sequenceashashmap.hxx>
+#include <unotools/configmgr.hxx>
+#include <utility>
+#include <vcl/window.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/wrkwin.hxx>
+#include <tools/diagnose_ex.h>
+
+namespace framework{
+
+const ::sal_Int32 INVALID_ICON_ID = -1;
+const ::sal_Int32 DEFAULT_ICON_ID = 0;
+
+TitleBarUpdate::TitleBarUpdate(css::uno::Reference< css::uno::XComponentContext > xContext)
+ : m_xContext (std::move(xContext ))
+{
+}
+
+TitleBarUpdate::~TitleBarUpdate()
+{
+}
+
+void SAL_CALL TitleBarUpdate::initialize(const css::uno::Sequence< css::uno::Any >& lArguments)
+{
+ // check arguments
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ if (!lArguments.hasElements())
+ throw css::lang::IllegalArgumentException(
+ "Empty argument list!",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 1);
+
+ lArguments[0] >>= xFrame;
+ if (!xFrame.is())
+ throw css::lang::IllegalArgumentException(
+ "No valid frame specified!",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 1);
+
+ {
+ SolarMutexGuard g;
+ // hold the frame as weak reference(!) so it can die everytimes :-)
+ m_xFrame = xFrame;
+ }
+
+ // start listening
+ xFrame->addFrameActionListener(this);
+
+ css::uno::Reference< css::frame::XTitleChangeBroadcaster > xBroadcaster(xFrame, css::uno::UNO_QUERY);
+ if (xBroadcaster.is ())
+ xBroadcaster->addTitleChangeListener (this);
+}
+
+void SAL_CALL TitleBarUpdate::frameAction(const css::frame::FrameActionEvent& aEvent)
+{
+ // we are interested on events only, which must trigger a title bar update
+ // because component was changed.
+ if (
+ (aEvent.Action == css::frame::FrameAction_COMPONENT_ATTACHED ) ||
+ (aEvent.Action == css::frame::FrameAction_COMPONENT_REATTACHED) ||
+ (aEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING )
+ )
+ {
+ impl_forceUpdate ();
+ }
+}
+
+void SAL_CALL TitleBarUpdate::titleChanged(const css::frame::TitleChangedEvent& /* aEvent */)
+{
+ impl_forceUpdate ();
+}
+
+void SAL_CALL TitleBarUpdate::disposing(const css::lang::EventObject&)
+{
+ css::uno::Reference< css::frame::XFrame > xFrame(m_xFrame.get(), css::uno::UNO_QUERY);
+ if (xFrame.is())
+ xFrame->removeFrameActionListener(this);
+
+ // nothing todo here - because we hold the frame as weak reference only
+}
+
+//http://live.gnome.org/GnomeShell/ApplicationBased
+//http://msdn.microsoft.com/en-us/library/dd378459(v=VS.85).aspx
+void TitleBarUpdate::impl_updateApplicationID(const css::uno::Reference< css::frame::XFrame >& xFrame)
+{
+ css::uno::Reference< css::awt::XWindow > xWindow = xFrame->getContainerWindow ();
+ if ( ! xWindow.is() )
+ return;
+
+#if !defined(MACOSX)
+ OUString sApplicationID;
+ try
+ {
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager =
+ css::frame::ModuleManager::create( m_xContext );
+
+ OUString sDesktopName;
+ OUString aModuleId = xModuleManager->identify(xFrame);
+ if ( aModuleId.startsWith("com.sun.star.text.") || aModuleId.startsWith("com.sun.star.xforms.") )
+ sDesktopName = "Writer";
+ else if ( aModuleId.startsWith("com.sun.star.sheet.") )
+ sDesktopName = "Calc";
+ else if ( aModuleId.startsWith("com.sun.star.presentation.") )
+ sDesktopName = "Impress";
+ else if ( aModuleId.startsWith("com.sun.star.drawing." ) )
+ sDesktopName = "Draw";
+ else if ( aModuleId.startsWith("com.sun.star.formula." ) )
+ sDesktopName = "Math";
+ else if ( aModuleId.startsWith("com.sun.star.sdb.") )
+ sDesktopName = "Base";
+ else
+ sDesktopName = "Startcenter";
+#if defined(_WIN32)
+ // We use a hardcoded product name matching the registry keys so applications can be associated with file types
+ sApplicationID = "TheDocumentFoundation.LibreOffice." + sDesktopName;
+#else
+ sApplicationID = utl::ConfigManager::getProductName().toAsciiLowerCase() + "-" + sDesktopName.toAsciiLowerCase();
+#endif
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+#else
+ OUString const sApplicationID;
+#endif
+
+ // VCL SYNCHRONIZED ->
+ SolarMutexGuard aSolarGuard;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->GetType() == WindowType::WORKWINDOW )
+ {
+ WorkWindow* pWorkWindow = static_cast<WorkWindow*>(pWindow.get());
+ pWorkWindow->SetApplicationID( sApplicationID );
+ }
+ // <- VCL SYNCHRONIZED
+}
+
+bool TitleBarUpdate::implst_getModuleInfo(const css::uno::Reference< css::frame::XFrame >& xFrame,
+ TModuleInfo& rInfo )
+{
+ if ( ! xFrame.is ())
+ return false;
+
+ try
+ {
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager =
+ css::frame::ModuleManager::create( m_xContext );
+
+ rInfo.sID = xModuleManager->identify(xFrame);
+ ::comphelper::SequenceAsHashMap lProps = xModuleManager->getByName (rInfo.sID);
+
+ rInfo.nIcon = lProps.getUnpackedValueOrDefault (OFFICEFACTORY_PROPNAME_ASCII_ICON , INVALID_ICON_ID );
+
+ // Note: If we could retrieve a module id ... everything is OK.
+ // UIName and Icon ID are optional values !
+ bool bSuccess = !rInfo.sID.isEmpty();
+ return bSuccess;
+ }
+ catch(const css::uno::Exception&)
+ {}
+
+ return false;
+}
+
+void TitleBarUpdate::impl_forceUpdate()
+{
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ {
+ SolarMutexGuard g;
+ xFrame.set(m_xFrame.get(), css::uno::UNO_QUERY);
+ }
+
+ // frame already gone ? We hold it weak only ...
+ if ( ! xFrame.is())
+ return;
+
+ // no window -> no chance to set/update title and icon
+ css::uno::Reference< css::awt::XWindow > xWindow = xFrame->getContainerWindow();
+ if ( ! xWindow.is())
+ return;
+
+ impl_updateIcon (xFrame);
+ impl_updateTitle (xFrame);
+#if !defined(MACOSX)
+ impl_updateApplicationID (xFrame);
+#endif
+}
+
+void TitleBarUpdate::impl_updateIcon(const css::uno::Reference< css::frame::XFrame >& xFrame)
+{
+ css::uno::Reference< css::frame::XController > xController = xFrame->getController ();
+ css::uno::Reference< css::awt::XWindow > xWindow = xFrame->getContainerWindow ();
+
+ if (
+ ( ! xController.is() ) ||
+ ( ! xWindow.is() )
+ )
+ return;
+
+ // a) set default value to an invalid one. So we can start further searches for right icon id, if
+ // first steps failed!
+ sal_Int32 nIcon = INVALID_ICON_ID;
+
+ // b) try to find information on controller property set directly
+ // Don't forget to catch possible exceptions - because these property is an optional one!
+ css::uno::Reference< css::beans::XPropertySet > xSet( xController, css::uno::UNO_QUERY );
+ if ( xSet.is() )
+ {
+ try
+ {
+ css::uno::Reference< css::beans::XPropertySetInfo > const xPSI( xSet->getPropertySetInfo(), css::uno::UNO_SET_THROW );
+ if ( xPSI->hasPropertyByName( "IconId" ) )
+ xSet->getPropertyValue( "IconId" ) >>= nIcon;
+ }
+ catch(const css::uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ }
+
+ // c) if b) failed ... identify the used module and retrieve set icon from module config.
+ // Tirck :-) Module was already specified outside and aInfo contains all needed information.
+ if ( nIcon == INVALID_ICON_ID )
+ {
+ TModuleInfo aInfo;
+ if (implst_getModuleInfo(xFrame, aInfo))
+ nIcon = aInfo.nIcon;
+ }
+
+ // d) if all steps failed - use fallback :-)
+ // ... means using the global staroffice icon
+ if( nIcon == INVALID_ICON_ID )
+ nIcon = DEFAULT_ICON_ID;
+
+ // e) set icon on container window now
+ // Don't forget SolarMutex! We use vcl directly :-(
+ // Check window pointer for right WorkWindow class too!!!
+
+ // VCL SYNCHRONIZED ->
+ SolarMutexGuard aSolarGuard;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && ( pWindow->GetType() == WindowType::WORKWINDOW ) )
+ {
+ WorkWindow* pWorkWindow = static_cast<WorkWindow*>(pWindow.get());
+ pWorkWindow->SetIcon( static_cast<sal_uInt16>(nIcon) );
+
+ css::uno::Reference< css::frame::XModel > xModel = xController->getModel();
+ OUString aURL;
+ if( xModel.is() )
+ aURL = xModel->getURL();
+ pWorkWindow->SetRepresentedURL( aURL );
+ }
+ // <- VCL SYNCHRONIZED
+}
+
+void TitleBarUpdate::impl_updateTitle(const css::uno::Reference< css::frame::XFrame >& xFrame)
+{
+ // no window ... no chance to set any title -> return
+ css::uno::Reference< css::awt::XWindow > xWindow = xFrame->getContainerWindow ();
+ if ( ! xWindow.is() )
+ return;
+
+ css::uno::Reference< css::frame::XTitle > xTitle(xFrame, css::uno::UNO_QUERY);
+ if ( ! xTitle.is() )
+ return;
+
+ const OUString sTitle = xTitle->getTitle ();
+
+ // VCL SYNCHRONIZED ->
+ SolarMutexGuard aSolarGuard;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && ( pWindow->GetType() == WindowType::WORKWINDOW ) )
+ {
+ WorkWindow* pWorkWindow = static_cast<WorkWindow*>(pWindow.get());
+ pWorkWindow->SetText( sTitle );
+ }
+ // <- VCL SYNCHRONIZED
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/helper/uiconfigelementwrapperbase.cxx b/framework/source/helper/uiconfigelementwrapperbase.cxx
new file mode 100644
index 000000000..95d5b4c65
--- /dev/null
+++ b/framework/source/helper/uiconfigelementwrapperbase.cxx
@@ -0,0 +1,481 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/uiconfigelementwrapperbase.hxx>
+#include <properties.h>
+#include <uielement/constitemcontainer.hxx>
+#include <uielement/rootitemcontainer.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/ui/XUIConfiguration.hpp>
+
+#include <vcl/svapp.hxx>
+#include <comphelper/sequence.hxx>
+
+const int UIELEMENT_PROPHANDLE_CONFIGSOURCE = 1;
+const int UIELEMENT_PROPHANDLE_FRAME = 2;
+const int UIELEMENT_PROPHANDLE_PERSISTENT = 3;
+const int UIELEMENT_PROPHANDLE_RESOURCEURL = 4;
+const int UIELEMENT_PROPHANDLE_TYPE = 5;
+const int UIELEMENT_PROPHANDLE_XMENUBAR = 6;
+const int UIELEMENT_PROPHANDLE_CONFIGLISTENER = 7;
+const int UIELEMENT_PROPHANDLE_NOCLOSE = 8;
+constexpr OUStringLiteral UIELEMENT_PROPNAME_CONFIGLISTENER = u"ConfigListener";
+constexpr OUStringLiteral UIELEMENT_PROPNAME_CONFIGSOURCE = u"ConfigurationSource";
+constexpr OUStringLiteral UIELEMENT_PROPNAME_FRAME = u"Frame";
+constexpr OUStringLiteral UIELEMENT_PROPNAME_PERSISTENT = u"Persistent";
+constexpr OUStringLiteral UIELEMENT_PROPNAME_RESOURCEURL = u"ResourceURL";
+constexpr OUStringLiteral UIELEMENT_PROPNAME_TYPE = u"Type";
+constexpr OUStringLiteral UIELEMENT_PROPNAME_XMENUBAR = u"XMenuBar";
+constexpr OUStringLiteral UIELEMENT_PROPNAME_NOCLOSE = u"NoClose";
+using namespace com::sun::star::beans;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+using namespace ::com::sun::star::ui;
+
+namespace framework
+{
+
+UIConfigElementWrapperBase::UIConfigElementWrapperBase( sal_Int16 nType )
+ : ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType >( m_aMutex )
+ , ::cppu::OPropertySetHelper ( *static_cast< ::cppu::OBroadcastHelper* >(this) )
+ , m_nType ( nType )
+ , m_bPersistent ( true )
+ , m_bInitialized ( false )
+ , m_bConfigListener ( false )
+ , m_bConfigListening ( false )
+ , m_bDisposed ( false )
+ , m_bNoClose ( false )
+ , m_aListenerContainer ( m_aMutex )
+{
+}
+
+UIConfigElementWrapperBase::~UIConfigElementWrapperBase()
+{
+}
+
+Any SAL_CALL UIConfigElementWrapperBase::queryInterface( const Type& _rType )
+{
+ Any aRet = UIConfigElementWrapperBase_BASE::queryInterface( _rType );
+ if ( !aRet.hasValue() )
+ aRet = OPropertySetHelper::queryInterface( _rType );
+ return aRet;
+}
+
+Sequence< Type > SAL_CALL UIConfigElementWrapperBase::getTypes( )
+{
+ return comphelper::concatSequences(
+ UIConfigElementWrapperBase_BASE::getTypes(),
+ ::cppu::OPropertySetHelper::getTypes()
+ );
+}
+
+// XComponent
+void SAL_CALL UIConfigElementWrapperBase::addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener )
+{
+ m_aListenerContainer.addInterface( cppu::UnoType<css::lang::XEventListener>::get(), xListener );
+}
+
+void SAL_CALL UIConfigElementWrapperBase::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener )
+{
+ m_aListenerContainer.removeInterface( cppu::UnoType<css::lang::XEventListener>::get(), aListener );
+}
+
+// XEventListener
+void SAL_CALL UIConfigElementWrapperBase::disposing( const EventObject& )
+{
+ SolarMutexGuard g;
+ m_xConfigSource.clear();
+}
+
+void SAL_CALL UIConfigElementWrapperBase::initialize( const Sequence< Any >& aArguments )
+{
+ SolarMutexGuard g;
+
+ if ( m_bInitialized )
+ return;
+
+ for ( const Any& rArg : aArguments )
+ {
+ PropertyValue aPropValue;
+ if ( rArg >>= aPropValue )
+ {
+ if ( aPropValue.Name == UIELEMENT_PROPNAME_CONFIGSOURCE )
+ setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_CONFIGSOURCE, aPropValue.Value );
+ else if ( aPropValue.Name == UIELEMENT_PROPNAME_FRAME )
+ setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_FRAME, aPropValue.Value );
+ else if ( aPropValue.Name == UIELEMENT_PROPNAME_PERSISTENT )
+ setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_PERSISTENT, aPropValue.Value );
+ else if ( aPropValue.Name == UIELEMENT_PROPNAME_RESOURCEURL )
+ setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_RESOURCEURL, aPropValue.Value );
+ else if ( aPropValue.Name == UIELEMENT_PROPNAME_TYPE )
+ setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_TYPE, aPropValue.Value );
+ else if ( aPropValue.Name == UIELEMENT_PROPNAME_CONFIGLISTENER )
+ setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_CONFIGLISTENER, aPropValue.Value );
+ else if ( aPropValue.Name == UIELEMENT_PROPNAME_NOCLOSE )
+ setFastPropertyValue_NoBroadcast( UIELEMENT_PROPHANDLE_NOCLOSE, aPropValue.Value );
+ }
+ }
+
+ m_bInitialized = true;
+}
+
+// XUpdatable
+void SAL_CALL UIConfigElementWrapperBase::update()
+{
+ // can be implemented by derived class
+}
+
+void SAL_CALL UIConfigElementWrapperBase::elementInserted( const css::ui::ConfigurationEvent& )
+{
+ // can be implemented by derived class
+}
+
+void SAL_CALL UIConfigElementWrapperBase::elementRemoved( const css::ui::ConfigurationEvent& )
+{
+ // can be implemented by derived class
+}
+
+void SAL_CALL UIConfigElementWrapperBase::elementReplaced( const css::ui::ConfigurationEvent& )
+{
+ // can be implemented by derived class
+}
+
+// XPropertySet helper
+sal_Bool SAL_CALL UIConfigElementWrapperBase::convertFastPropertyValue( Any& aConvertedValue ,
+ Any& aOldValue ,
+ sal_Int32 nHandle ,
+ const Any& aValue )
+{
+ // Initialize state with sal_False !!!
+ // (Handle can be invalid)
+ bool bReturn = false;
+
+ switch( nHandle )
+ {
+ case UIELEMENT_PROPHANDLE_CONFIGLISTENER:
+ bReturn = PropHelper::willPropertyBeChanged(
+ css::uno::Any(m_bConfigListener),
+ aValue,
+ aOldValue,
+ aConvertedValue);
+ break;
+
+ case UIELEMENT_PROPHANDLE_CONFIGSOURCE:
+ bReturn = PropHelper::willPropertyBeChanged(
+ css::uno::Any(m_xConfigSource),
+ aValue,
+ aOldValue,
+ aConvertedValue);
+ break;
+
+ case UIELEMENT_PROPHANDLE_FRAME:
+ {
+ Reference< XFrame > xFrame( m_xWeakFrame );
+ bReturn = PropHelper::willPropertyBeChanged(
+ css::uno::Any(xFrame),
+ aValue,
+ aOldValue,
+ aConvertedValue);
+ }
+ break;
+
+ case UIELEMENT_PROPHANDLE_PERSISTENT:
+ bReturn = PropHelper::willPropertyBeChanged(
+ css::uno::Any(m_bPersistent),
+ aValue,
+ aOldValue,
+ aConvertedValue);
+ break;
+
+ case UIELEMENT_PROPHANDLE_RESOURCEURL:
+ bReturn = PropHelper::willPropertyBeChanged(
+ css::uno::Any(m_aResourceURL),
+ aValue,
+ aOldValue,
+ aConvertedValue);
+ break;
+
+ case UIELEMENT_PROPHANDLE_TYPE :
+ bReturn = PropHelper::willPropertyBeChanged(
+ css::uno::Any(m_nType),
+ aValue,
+ aOldValue,
+ aConvertedValue);
+ break;
+
+ case UIELEMENT_PROPHANDLE_XMENUBAR :
+ bReturn = PropHelper::willPropertyBeChanged(
+ css::uno::Any(m_xMenuBar),
+ aValue,
+ aOldValue,
+ aConvertedValue);
+ break;
+
+ case UIELEMENT_PROPHANDLE_NOCLOSE:
+ bReturn = PropHelper::willPropertyBeChanged(
+ css::uno::Any(m_bNoClose),
+ aValue,
+ aOldValue,
+ aConvertedValue);
+ break;
+ }
+
+ // Return state of operation.
+ return bReturn;
+}
+
+void SAL_CALL UIConfigElementWrapperBase::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle ,
+ const css::uno::Any& aValue )
+{
+ switch( nHandle )
+ {
+ case UIELEMENT_PROPHANDLE_CONFIGLISTENER:
+ {
+ bool bBool( m_bConfigListener );
+ aValue >>= bBool;
+ if ( m_bConfigListener != bBool )
+ {
+ if ( m_bConfigListening )
+ {
+ if ( m_xConfigSource.is() && !bBool )
+ {
+ try
+ {
+ Reference< XUIConfiguration > xUIConfig( m_xConfigSource, UNO_QUERY );
+ if ( xUIConfig.is() )
+ {
+ xUIConfig->removeConfigurationListener( Reference< XUIConfigurationListener >(this) );
+ m_bConfigListening = false;
+ }
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+ }
+ else
+ {
+ if ( m_xConfigSource.is() && bBool )
+ {
+ try
+ {
+ Reference< XUIConfiguration > xUIConfig( m_xConfigSource, UNO_QUERY );
+ if ( xUIConfig.is() )
+ {
+ xUIConfig->addConfigurationListener( Reference< XUIConfigurationListener >(this) );
+ m_bConfigListening = true;
+ }
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+ }
+
+ m_bConfigListener = bBool;
+ }
+ }
+ break;
+ case UIELEMENT_PROPHANDLE_CONFIGSOURCE:
+ aValue >>= m_xConfigSource;
+ break;
+ case UIELEMENT_PROPHANDLE_FRAME:
+ {
+ Reference< XFrame > xFrame;
+
+ aValue >>= xFrame;
+ m_xWeakFrame = xFrame;
+ break;
+ }
+ case UIELEMENT_PROPHANDLE_PERSISTENT:
+ {
+ bool bBool( m_bPersistent );
+ aValue >>= bBool;
+ m_bPersistent = bBool;
+ break;
+ }
+ case UIELEMENT_PROPHANDLE_RESOURCEURL:
+ aValue >>= m_aResourceURL;
+ break;
+ case UIELEMENT_PROPHANDLE_TYPE:
+ aValue >>= m_nType;
+ break;
+ case UIELEMENT_PROPHANDLE_XMENUBAR:
+ aValue >>= m_xMenuBar;
+ break;
+ case UIELEMENT_PROPHANDLE_NOCLOSE:
+ {
+ bool bBool( m_bNoClose );
+ aValue >>= bBool;
+ m_bNoClose = bBool;
+ break;
+ }
+ }
+}
+
+void SAL_CALL UIConfigElementWrapperBase::getFastPropertyValue( css::uno::Any& aValue ,
+ sal_Int32 nHandle ) const
+{
+ switch( nHandle )
+ {
+ case UIELEMENT_PROPHANDLE_CONFIGLISTENER:
+ aValue <<= m_bConfigListener;
+ break;
+ case UIELEMENT_PROPHANDLE_CONFIGSOURCE:
+ aValue <<= m_xConfigSource;
+ break;
+ case UIELEMENT_PROPHANDLE_FRAME:
+ {
+ Reference< XFrame > xFrame( m_xWeakFrame );
+ aValue <<= xFrame;
+ break;
+ }
+ case UIELEMENT_PROPHANDLE_PERSISTENT:
+ aValue <<= m_bPersistent;
+ break;
+ case UIELEMENT_PROPHANDLE_RESOURCEURL:
+ aValue <<= m_aResourceURL;
+ break;
+ case UIELEMENT_PROPHANDLE_TYPE:
+ aValue <<= m_nType;
+ break;
+ case UIELEMENT_PROPHANDLE_XMENUBAR:
+ aValue <<= m_xMenuBar;
+ break;
+ case UIELEMENT_PROPHANDLE_NOCLOSE:
+ aValue <<= m_bNoClose;
+ break;
+ }
+}
+
+::cppu::IPropertyArrayHelper& SAL_CALL UIConfigElementWrapperBase::getInfoHelper()
+{
+ // Define static member to give structure of properties to baseclass "OPropertySetHelper".
+ // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable.
+ // "true" say: Table is sorted by name.
+ static ::cppu::OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true );
+
+ return ourInfoHelper;
+}
+
+css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL UIConfigElementWrapperBase::getPropertySetInfo()
+{
+ // Create structure of propertysetinfo for baseclass "OPropertySetHelper".
+ // (Use method "getInfoHelper()".)
+ static css::uno::Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+
+ return xInfo;
+}
+
+css::uno::Sequence< css::beans::Property > UIConfigElementWrapperBase::impl_getStaticPropertyDescriptor()
+{
+ // Create property array to initialize sequence!
+ // Table of all predefined properties of this class. It's used from OPropertySetHelper-class!
+ // Don't forget to change the defines (see begin of this file), if you add, change or delete a property in this list!!!
+ // It's necessary for methods of OPropertySetHelper.
+ // ATTENTION:
+ // YOU MUST SORT FOLLOW TABLE BY NAME ALPHABETICAL !!!
+
+ return
+ {
+ css::beans::Property( UIELEMENT_PROPNAME_CONFIGLISTENER, UIELEMENT_PROPHANDLE_CONFIGLISTENER , cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::TRANSIENT ),
+ css::beans::Property( UIELEMENT_PROPNAME_CONFIGSOURCE, UIELEMENT_PROPHANDLE_CONFIGSOURCE , cppu::UnoType<css::ui::XUIConfigurationManager>::get(), css::beans::PropertyAttribute::TRANSIENT ),
+ css::beans::Property( UIELEMENT_PROPNAME_FRAME, UIELEMENT_PROPHANDLE_FRAME , cppu::UnoType<css::frame::XFrame>::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
+ css::beans::Property( UIELEMENT_PROPNAME_NOCLOSE, UIELEMENT_PROPHANDLE_NOCLOSE , cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::TRANSIENT ),
+ css::beans::Property( UIELEMENT_PROPNAME_PERSISTENT, UIELEMENT_PROPHANDLE_PERSISTENT , cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::TRANSIENT ),
+ css::beans::Property( UIELEMENT_PROPNAME_RESOURCEURL, UIELEMENT_PROPHANDLE_RESOURCEURL , cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
+ css::beans::Property( UIELEMENT_PROPNAME_TYPE, UIELEMENT_PROPHANDLE_TYPE , cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
+ css::beans::Property( UIELEMENT_PROPNAME_XMENUBAR, UIELEMENT_PROPHANDLE_XMENUBAR , cppu::UnoType<css::awt::XMenuBar>::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY )
+ };
+}
+
+void SAL_CALL UIConfigElementWrapperBase::setSettings( const Reference< XIndexAccess >& xSettings )
+{
+ SolarMutexClearableGuard aLock;
+
+ if ( !xSettings.is() )
+ return;
+
+ // Create a copy of the data if the container is not const
+ Reference< XIndexReplace > xReplace( xSettings, UNO_QUERY );
+ if ( xReplace.is() )
+ m_xConfigData = new ConstItemContainer( xSettings );
+ else
+ m_xConfigData = xSettings;
+
+ if ( m_xConfigSource.is() && m_bPersistent )
+ {
+ OUString aResourceURL( m_aResourceURL );
+ Reference< XUIConfigurationManager > xUICfgMgr( m_xConfigSource );
+
+ aLock.clear();
+
+ try
+ {
+ xUICfgMgr->replaceSettings( aResourceURL, m_xConfigData );
+ }
+ catch( const NoSuchElementException& )
+ {
+ }
+ }
+ else if ( !m_bPersistent )
+ {
+ // Transient menubar => Fill menubar with new data
+ impl_fillNewData();
+ }
+}
+void UIConfigElementWrapperBase::impl_fillNewData()
+{
+}
+Reference< XIndexAccess > SAL_CALL UIConfigElementWrapperBase::getSettings( sal_Bool bWriteable )
+{
+ SolarMutexGuard g;
+
+ if ( bWriteable )
+ return Reference< XIndexAccess >( new RootItemContainer( m_xConfigData ) );
+
+ return m_xConfigData;
+}
+
+Reference< XFrame > SAL_CALL UIConfigElementWrapperBase::getFrame()
+{
+ SolarMutexGuard g;
+ Reference< XFrame > xFrame( m_xWeakFrame );
+ return xFrame;
+}
+
+OUString SAL_CALL UIConfigElementWrapperBase::getResourceURL()
+{
+ SolarMutexGuard g;
+ return m_aResourceURL;
+}
+
+::sal_Int16 SAL_CALL UIConfigElementWrapperBase::getType()
+{
+ SolarMutexGuard g;
+ return m_nType;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/helper/uielementwrapperbase.cxx b/framework/source/helper/uielementwrapperbase.cxx
new file mode 100644
index 000000000..829e4f952
--- /dev/null
+++ b/framework/source/helper/uielementwrapperbase.cxx
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/uielementwrapperbase.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <vcl/svapp.hxx>
+#include <comphelper/sequence.hxx>
+
+const int UIELEMENT_PROPHANDLE_RESOURCEURL = 1;
+const int UIELEMENT_PROPHANDLE_TYPE = 2;
+const int UIELEMENT_PROPHANDLE_FRAME = 3;
+constexpr OUStringLiteral UIELEMENT_PROPNAME_RESOURCEURL = u"ResourceURL";
+constexpr OUStringLiteral UIELEMENT_PROPNAME_TYPE = u"Type";
+constexpr OUStringLiteral UIELEMENT_PROPNAME_FRAME = u"Frame";
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::frame;
+
+namespace framework
+{
+
+UIElementWrapperBase::UIElementWrapperBase( sal_Int16 nType )
+ : ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType >( m_aMutex )
+ , ::cppu::OPropertySetHelper ( *static_cast< ::cppu::OBroadcastHelper* >(this) )
+ , m_aListenerContainer ( m_aMutex )
+ , m_nType ( nType )
+ , m_bInitialized ( false )
+ , m_bDisposed ( false )
+{
+}
+
+UIElementWrapperBase::~UIElementWrapperBase()
+{
+}
+
+Any SAL_CALL UIElementWrapperBase::queryInterface( const Type& _rType )
+{
+ Any aRet = UIElementWrapperBase_BASE::queryInterface( _rType );
+ if ( !aRet.hasValue() )
+ aRet = OPropertySetHelper::queryInterface( _rType );
+ return aRet;
+}
+
+Sequence< Type > SAL_CALL UIElementWrapperBase::getTypes( )
+{
+ return comphelper::concatSequences(
+ UIElementWrapperBase_BASE::getTypes(),
+ ::cppu::OPropertySetHelper::getTypes()
+ );
+}
+
+void SAL_CALL UIElementWrapperBase::addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener )
+{
+ m_aListenerContainer.addInterface( cppu::UnoType<css::lang::XEventListener>::get(), xListener );
+}
+
+void SAL_CALL UIElementWrapperBase::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener )
+{
+ m_aListenerContainer.removeInterface( cppu::UnoType<css::lang::XEventListener>::get(), xListener );
+}
+
+void SAL_CALL UIElementWrapperBase::initialize( const Sequence< Any >& aArguments )
+{
+ SolarMutexGuard g;
+
+ if ( m_bInitialized )
+ return;
+
+ for ( const Any& rArg : aArguments )
+ {
+ PropertyValue aPropValue;
+ if ( rArg >>= aPropValue )
+ {
+ if ( aPropValue.Name == "ResourceURL" )
+ aPropValue.Value >>= m_aResourceURL;
+ else if ( aPropValue.Name == "Frame" )
+ {
+ Reference< XFrame > xFrame;
+ aPropValue.Value >>= xFrame;
+ m_xWeakFrame = xFrame;
+ }
+ }
+ }
+
+ m_bInitialized = true;
+}
+
+// XUIElement
+css::uno::Reference< css::frame::XFrame > SAL_CALL UIElementWrapperBase::getFrame()
+{
+ css::uno::Reference< css::frame::XFrame > xFrame( m_xWeakFrame );
+ return xFrame;
+}
+
+OUString SAL_CALL UIElementWrapperBase::getResourceURL()
+{
+ return m_aResourceURL;
+}
+
+::sal_Int16 SAL_CALL UIElementWrapperBase::getType()
+{
+ return m_nType;
+}
+
+// XUpdatable
+void SAL_CALL UIElementWrapperBase::update()
+{
+ // can be implemented by derived class
+}
+
+// XPropertySet helper
+sal_Bool SAL_CALL UIElementWrapperBase::convertFastPropertyValue( Any& /*aConvertedValue*/ ,
+ Any& /*aOldValue*/ ,
+ sal_Int32 /*nHandle*/ ,
+ const Any& /*aValue*/ )
+{
+ // Initialize state with sal_False !!!
+ // (Handle can be invalid)
+ return false;
+}
+
+void SAL_CALL UIElementWrapperBase::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/ ,
+ const css::uno::Any& /*aValue*/ )
+{
+}
+
+void SAL_CALL UIElementWrapperBase::getFastPropertyValue( css::uno::Any& aValue ,
+ sal_Int32 nHandle ) const
+{
+ switch( nHandle )
+ {
+ case UIELEMENT_PROPHANDLE_RESOURCEURL:
+ aValue <<= m_aResourceURL;
+ break;
+ case UIELEMENT_PROPHANDLE_TYPE:
+ aValue <<= m_nType;
+ break;
+ case UIELEMENT_PROPHANDLE_FRAME:
+ Reference< XFrame > xFrame( m_xWeakFrame );
+ aValue <<= xFrame;
+ break;
+ }
+}
+
+::cppu::IPropertyArrayHelper& SAL_CALL UIElementWrapperBase::getInfoHelper()
+{
+ // Define static member to give structure of properties to baseclass "OPropertySetHelper".
+ // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable.
+ // "true" say: Table is sorted by name.
+ static ::cppu::OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true );
+
+ return ourInfoHelper;
+}
+
+css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL UIElementWrapperBase::getPropertySetInfo()
+{
+ // Create structure of propertysetinfo for baseclass "OPropertySetHelper".
+ // (Use method "getInfoHelper()".)
+ static css::uno::Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+
+ return xInfo;
+}
+
+css::uno::Sequence< css::beans::Property > UIElementWrapperBase::impl_getStaticPropertyDescriptor()
+{
+ // Create a property array to initialize sequence!
+ // Table of all predefined properties of this class. It's used from OPropertySetHelper-class!
+ // Don't forget to change the defines (see begin of this file), if you add, change or delete a property in this list!!!
+ // It's necessary for methods of OPropertySetHelper.
+ // ATTENTION:
+ // YOU MUST SORT FOLLOW TABLE BY NAME ALPHABETICAL !!!
+
+ return
+ {
+ css::beans::Property( UIELEMENT_PROPNAME_FRAME, UIELEMENT_PROPHANDLE_FRAME , cppu::UnoType<XFrame>::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
+ css::beans::Property( UIELEMENT_PROPNAME_RESOURCEURL, UIELEMENT_PROPHANDLE_RESOURCEURL , cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
+ css::beans::Property( UIELEMENT_PROPNAME_TYPE, UIELEMENT_PROPHANDLE_TYPE , cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY )
+ };
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/helper/vclstatusindicator.cxx b/framework/source/helper/vclstatusindicator.cxx
new file mode 100644
index 000000000..b493eacc7
--- /dev/null
+++ b/framework/source/helper/vclstatusindicator.cxx
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/vclstatusindicator.hxx>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+namespace framework {
+
+VCLStatusIndicator::VCLStatusIndicator(css::uno::Reference< css::awt::XWindow > xParentWindow)
+ : m_xParentWindow (std::move(xParentWindow ))
+ , m_pStatusBar (nullptr )
+ , m_nRange (0 )
+ , m_nValue (0 )
+{
+ if (!m_xParentWindow.is())
+ throw css::uno::RuntimeException(
+ "Can't work without a parent window!",
+ static_cast< css::task::XStatusIndicator* >(this));
+}
+
+VCLStatusIndicator::~VCLStatusIndicator()
+{
+}
+
+void SAL_CALL VCLStatusIndicator::start(const OUString& sText ,
+ sal_Int32 nRange)
+{
+ SolarMutexGuard aSolarGuard;
+
+ VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow(m_xParentWindow);
+ if (!m_pStatusBar)
+ m_pStatusBar = VclPtr<StatusBar>::Create(pParentWindow, WB_3DLOOK|WB_BORDER);
+
+ VCLStatusIndicator::impl_recalcLayout(m_pStatusBar, pParentWindow);
+
+ m_pStatusBar->Show();
+ m_pStatusBar->StartProgressMode(sText);
+ m_pStatusBar->SetProgressValue(0);
+
+ // force repaint!
+ pParentWindow->Show();
+ pParentWindow->Invalidate(InvalidateFlags::Children);
+ pParentWindow->GetOutDev()->Flush();
+
+ m_nRange = nRange;
+ m_nValue = 0;
+}
+
+void SAL_CALL VCLStatusIndicator::reset()
+{
+ SolarMutexGuard aSolarGuard;
+ if (m_pStatusBar)
+ {
+ m_pStatusBar->SetProgressValue(0);
+ m_pStatusBar->SetText(OUString());
+ }
+}
+
+void SAL_CALL VCLStatusIndicator::end()
+{
+ SolarMutexGuard aSolarGuard;
+
+ m_nRange = 0;
+ m_nValue = 0;
+
+ if (m_pStatusBar)
+ {
+ m_pStatusBar->EndProgressMode();
+ m_pStatusBar->Show(false);
+
+ m_pStatusBar.disposeAndClear();
+ }
+}
+
+void SAL_CALL VCLStatusIndicator::setText(const OUString& sText)
+{
+ SolarMutexGuard aSolarGuard;
+ if (m_pStatusBar)
+ m_pStatusBar->SetText(sText);
+}
+
+void SAL_CALL VCLStatusIndicator::setValue(sal_Int32 nValue)
+{
+ SolarMutexGuard aSolarGuard;
+
+ if (nValue <= m_nRange)
+ m_nValue = nValue;
+ else
+ m_nValue = m_nRange;
+
+ sal_Int32 nRange = m_nRange;
+ nValue = m_nValue;
+
+ // normalize value to fit the range of 0-100%
+ sal_uInt16 nPercent = sal::static_int_cast< sal_uInt16 >(
+ ::std::min(
+ ((nValue*100) / ::std::max(nRange,sal_Int32(1))), sal_Int32(100)));
+
+ if (m_pStatusBar)
+ m_pStatusBar->SetProgressValue(nPercent);
+}
+
+void VCLStatusIndicator::impl_recalcLayout(vcl::Window* pStatusBar ,
+ vcl::Window const * pParentWindow)
+{
+ if (
+ (!pStatusBar ) ||
+ (!pParentWindow)
+ )
+ return;
+
+ Size aParentSize = pParentWindow->GetSizePixel();
+ pStatusBar->setPosSizePixel(0,
+ 0,
+ aParentSize.Width(),
+ aParentSize.Height());
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/helper/wakeupthread.cxx b/framework/source/helper/wakeupthread.cxx
new file mode 100644
index 000000000..40487c83b
--- /dev/null
+++ b/framework/source/helper/wakeupthread.cxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/util/XUpdatable.hpp>
+
+#include <helper/wakeupthread.hxx>
+#include <chrono>
+
+using namespace std::chrono_literals;
+
+void framework::WakeUpThread::execute() {
+ for (;;) {
+ {
+ std::unique_lock g(mutex_);
+ condition_.wait_for(g, 25ms, [this] { return terminate_; });
+ if (terminate_) {
+ break;
+ }
+ }
+ css::uno::Reference<css::util::XUpdatable> up(updatable_);
+ if (up.is()) {
+ up->update();
+ }
+ }
+}
+
+framework::WakeUpThread::WakeUpThread(
+ css::uno::Reference<css::util::XUpdatable> const & updatable):
+ Thread("WakeUpThread"), updatable_(updatable), terminate_(false)
+{}
+
+void framework::WakeUpThread::stop() {
+ {
+ std::unique_lock g(mutex_);
+ terminate_ = true;
+ }
+ condition_.notify_one();
+ join();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/accelerators/acceleratorcache.hxx b/framework/source/inc/accelerators/acceleratorcache.hxx
new file mode 100644
index 000000000..adfdf8fe5
--- /dev/null
+++ b/framework/source/inc/accelerators/acceleratorcache.hxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <stdtypes.h>
+
+#include <com/sun/star/awt/KeyEvent.hpp>
+
+#include <unordered_map>
+#include <vector>
+
+// definition
+
+namespace framework
+{
+
+/**
+ @short implements a cache for any accelerator configuration.
+
+ @descr It's implemented threadsafe, supports copy-on-write pattern
+ and a flush mechanism to support concurrent access to the same
+ configuration.
+
+ copy-on-write ... How? Do the following:
+ */
+class AcceleratorCache
+{
+ public:
+
+ /**
+ commands -> keys
+ */
+ typedef ::std::vector< css::awt::KeyEvent > TKeyList;
+
+ private:
+
+ typedef std::unordered_map<OUString, TKeyList> TCommand2Keys;
+
+ /**
+ keys -> commands
+ */
+ typedef std::unordered_map< css::awt::KeyEvent ,
+ OUString ,
+ KeyEventHashCode ,
+ KeyEventEqualsFunc > TKey2Commands;
+
+ /** map commands to keys in relation 1:n.
+ First key is interpreted as preferred one! */
+ TCommand2Keys m_lCommand2Keys;
+
+ /** map keys to commands in relation 1:1. */
+ TKey2Commands m_lKey2Commands;
+
+ public:
+ /** @short checks if the specified key exists.
+
+ @param aKey
+ the key, which should be checked.
+
+ @return [bool]
+ sal_True if the specified key exists inside this container.
+ */
+ bool hasKey(const css::awt::KeyEvent& aKey) const;
+ bool hasCommand(const OUString& sCommand) const;
+
+ TKeyList getAllKeys() const;
+
+ /** @short add a new or change an existing key-command pair
+ of this container.
+
+ @param aKey
+ describe the key.
+
+ @param sCommand
+ describe the command.
+ */
+ void setKeyCommandPair(const css::awt::KeyEvent& aKey ,
+ const OUString& sCommand);
+
+ /** @short returns the list of keys, which are registered
+ for this command.
+
+ @param sCommand
+ describe the command.
+
+ @return [TKeyList]
+ the list of registered keys. Can be empty!
+ */
+ TKeyList getKeysByCommand(const OUString& sCommand) const;
+
+ OUString getCommandByKey(const css::awt::KeyEvent& aKey) const;
+ void removeKey(const css::awt::KeyEvent& aKey);
+ void removeCommand(const OUString& sCommand);
+};
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/accelerators/acceleratorconfiguration.hxx b/framework/source/inc/accelerators/acceleratorconfiguration.hxx
new file mode 100644
index 000000000..725148ebb
--- /dev/null
+++ b/framework/source/inc/accelerators/acceleratorconfiguration.hxx
@@ -0,0 +1,312 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <accelerators/presethandler.hxx>
+#include <accelerators/acceleratorcache.hxx>
+
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/ui/XAcceleratorConfiguration.hpp>
+
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/util/XChangesListener.hpp>
+
+// TODO use XPresetHandler interface instead if available
+#include <com/sun/star/form/XReset.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+// definition
+
+namespace framework
+{
+
+inline constexpr OUStringLiteral CFG_ENTRY_PRIMARY = u"PrimaryKeys";
+inline constexpr OUStringLiteral CFG_ENTRY_GLOBAL = u"Global";
+inline constexpr OUStringLiteral CFG_ENTRY_MODULES = u"Modules";
+
+/**
+ implements a read/write access to the accelerator configuration.
+ */
+class XMLBasedAcceleratorConfiguration : public ::cppu::WeakImplHelper<
+ css::form::XReset, // TODO use XPresetHandler instead if available
+ css::ui::XAcceleratorConfiguration > // => css::ui::XUIConfigurationPersistence
+ // css::ui::XUIConfigurationStorage
+ // css::ui::XUIConfiguration
+{
+
+ // member
+
+ protected:
+
+ /** the global uno service manager.
+ Must be used to create own needed services. */
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ /** used to:
+ i ) copy configuration files from the share to the user layer
+ ii ) provide access to these config files
+ iii) cache all sub storages on the path from the top to the bottom(!)
+ iv ) provide commit for changes. */
+ PresetHandler m_aPresetHandler;
+
+ /** contains the cached configuration data */
+ AcceleratorCache m_aReadCache;
+
+ /** used to implement the copy on write pattern! */
+ std::unique_ptr<AcceleratorCache> m_pWriteCache;
+
+ // native interface!
+
+ public:
+
+ XMLBasedAcceleratorConfiguration( const css::uno::Reference< css::uno::XComponentContext >& xContext);
+ virtual ~XMLBasedAcceleratorConfiguration( ) override;
+
+ // uno interface!
+
+ public:
+
+ // XAcceleratorConfiguration
+ virtual css::uno::Sequence< css::awt::KeyEvent > SAL_CALL getAllKeyEvents() override;
+
+ virtual OUString SAL_CALL getCommandByKeyEvent(const css::awt::KeyEvent& aKeyEvent) override;
+
+ virtual void SAL_CALL setKeyEvent(const css::awt::KeyEvent& aKeyEvent,
+ const OUString& sCommand ) override;
+
+ virtual void SAL_CALL removeKeyEvent(const css::awt::KeyEvent& aKeyEvent) override;
+
+ virtual css::uno::Sequence< css::awt::KeyEvent > SAL_CALL getKeyEventsByCommand(const OUString& sCommand) override;
+
+ virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPreferredKeyEventsForCommandList(const css::uno::Sequence< OUString >& lCommandList) override;
+
+ virtual void SAL_CALL removeCommandFromAllKeyEvents(const OUString& sCommand) override;
+
+ // XUIConfigurationPersistence
+ virtual void SAL_CALL reload() override;
+
+ virtual void SAL_CALL store() override;
+
+ virtual void SAL_CALL storeToStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) override;
+
+ virtual sal_Bool SAL_CALL isModified() override;
+
+ virtual sal_Bool SAL_CALL isReadOnly() override;
+
+ // XUIConfigurationStorage
+ virtual void SAL_CALL setStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) override;
+
+ virtual sal_Bool SAL_CALL hasStorage() override;
+
+ // XUIConfiguration
+ virtual void SAL_CALL addConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& xListener) override;
+
+ virtual void SAL_CALL removeConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& xListener) override;
+
+ // XReset
+ // TODO use XPresetHandler instead if available
+ virtual void SAL_CALL reset() override;
+
+ virtual void SAL_CALL addResetListener(const css::uno::Reference< css::form::XResetListener >& xListener) override;
+
+ virtual void SAL_CALL removeResetListener(const css::uno::Reference< css::form::XResetListener >& xListener) override;
+
+ // called when changes occurred in the storage
+ void changesOccurred();
+
+ // helper for derived classes
+
+ protected:
+
+ /** @short return the current office locale.
+
+ @descr We do not cache this value, because we are not listen
+ for changes on the configuration layer ...
+
+ @return OUString
+ The current office locale as BCP47 string.
+ */
+ OUString impl_ts_getLocale() const;
+
+ // helper
+
+ private:
+
+ /** @short load a configuration set, using the given stream.
+
+ @param xStream
+ provides the XML structure as stream.
+ */
+ void impl_ts_load(const css::uno::Reference< css::io::XInputStream >& xStream);
+
+ /** @short save a configuration set, using the given stream.
+
+ @param xStream
+ the XML structure can be written there.
+ */
+ void impl_ts_save(const css::uno::Reference< css::io::XOutputStream >& xStream);
+
+ /** @short returns a reference to one of our internal cache members.
+
+ @descr We implement the copy-on-write pattern. Doing so
+ we know two caches internally. The second one is used
+ only, if the container was changed.
+
+ This method here returns access to one of these
+ caches - depending on the change state of this
+ configuration service.
+
+ @param bWriteAccessRequested
+ if the outside code wish to change the container
+ it must call this method with "sal_True". So the internal
+ cache can be prepared for that (means copy-on-write ...).
+
+ @return [AcceleratorCache]
+ c++ reference(!) to one of our internal caches.
+ */
+ AcceleratorCache& impl_getCFG(bool bWriteAccessRequested = false);
+
+};
+
+class XCUBasedAcceleratorConfiguration : public ::cppu::WeakImplHelper<
+ css::util::XChangesListener,
+ css::form::XReset, // TODO use XPresetHandler instead if available
+ css::ui::XAcceleratorConfiguration > // => css::ui::XUIConfigurationPersistence
+ // css::ui::XUIConfigurationStorage
+ // css::ui::XUIConfiguration
+{
+
+ // member
+
+ protected:
+
+ /** the global uno service manager.
+ Must be used to create own needed services. */
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ css::uno::Reference< css::container::XNameAccess > m_xCfg;
+ AcceleratorCache m_aPrimaryReadCache;
+ AcceleratorCache m_aSecondaryReadCache;
+ std::unique_ptr<AcceleratorCache> m_pPrimaryWriteCache;
+ std::unique_ptr<AcceleratorCache> m_pSecondaryWriteCache;
+
+ OUString m_sGlobalOrModules;
+ OUString m_sModuleCFG;
+
+ // native interface!
+
+ public:
+
+ XCUBasedAcceleratorConfiguration( css::uno::Reference< css::uno::XComponentContext > xContext );
+ virtual ~XCUBasedAcceleratorConfiguration( ) override;
+
+ // uno interface!
+
+ public:
+
+ // XAcceleratorConfiguration
+ virtual css::uno::Sequence< css::awt::KeyEvent > SAL_CALL getAllKeyEvents() override;
+
+ virtual OUString SAL_CALL getCommandByKeyEvent(const css::awt::KeyEvent& aKeyEvent) override;
+
+ virtual void SAL_CALL setKeyEvent(const css::awt::KeyEvent& aKeyEvent,
+ const OUString& sCommand ) override;
+
+ virtual void SAL_CALL removeKeyEvent(const css::awt::KeyEvent& aKeyEvent) override;
+
+ virtual css::uno::Sequence< css::awt::KeyEvent > SAL_CALL getKeyEventsByCommand(const OUString& sCommand) override;
+
+ virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPreferredKeyEventsForCommandList(const css::uno::Sequence< OUString >& lCommandList) override;
+
+ virtual void SAL_CALL removeCommandFromAllKeyEvents(const OUString& sCommand) override;
+
+ // XUIConfigurationPersistence
+ virtual void SAL_CALL reload() override;
+
+ virtual void SAL_CALL store() override;
+
+ virtual void SAL_CALL storeToStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) override;
+
+ virtual sal_Bool SAL_CALL isModified() override;
+
+ virtual sal_Bool SAL_CALL isReadOnly() override;
+
+ // XUIConfigurationStorage
+ virtual void SAL_CALL setStorage(const css::uno::Reference< css::embed::XStorage >& xStorage) override;
+
+ virtual sal_Bool SAL_CALL hasStorage() override;
+
+ // XUIConfiguration
+ virtual void SAL_CALL addConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& xListener) override;
+
+ virtual void SAL_CALL removeConfigurationListener(const css::uno::Reference< css::ui::XUIConfigurationListener >& xListener) override;
+
+ // XReset
+ // TODO use XPresetHandler instead if available
+ virtual void SAL_CALL reset() override;
+
+ virtual void SAL_CALL addResetListener(const css::uno::Reference< css::form::XResetListener >& xListener) override;
+
+ virtual void SAL_CALL removeResetListener(const css::uno::Reference< css::form::XResetListener >& xListener) override;
+
+ // css.util.XChangesListener
+ virtual void SAL_CALL changesOccurred(const css::util::ChangesEvent& aEvent) override;
+
+ // css.lang.XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override;
+
+ // helper for derived classes
+
+ protected:
+
+ /** @short return the current office locale.
+
+ @descr We do not cache this value, because we are not listen
+ for changes on the configuration layer ...
+
+ @return OUString
+ The current office locale as BCP47 string.
+ */
+ OUString impl_ts_getLocale() const;
+
+ // helper
+
+ private:
+
+ void impl_ts_load(bool bPreferred, const css::uno::Reference< css::container::XNameAccess >& xCfg);
+ void impl_ts_save(bool bPreferred);
+
+ void insertKeyToConfiguration(const css::awt::KeyEvent& aKeyEvent, const OUString& sCommand, const bool bPreferred);
+ void removeKeyFromConfiguration(const css::awt::KeyEvent& aKeyEvent, const bool bPreferred);
+
+ void reloadChanged(const OUString& sPrimarySecondary, std::u16string_view sGlobalModules, const OUString& sModule, const OUString& sKey);
+ AcceleratorCache& impl_getCFG(bool bPreferred, bool bWriteAccessRequested = false);
+
+};
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/accelerators/keymapping.hxx b/framework/source/inc/accelerators/keymapping.hxx
new file mode 100644
index 000000000..88429a012
--- /dev/null
+++ b/framework/source/inc/accelerators/keymapping.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <unordered_map>
+
+// definition
+
+namespace framework
+{
+
+/**
+ can be used to map key identifier to the
+ corresponding key codes ...
+ */
+class KeyMapping
+{
+
+ // const, types
+
+ private:
+
+ /** @short is used to map a key code
+ to the right key identifier, which is
+ used to make the xml file "human readable"
+ */
+ struct KeyIdentifierInfo
+ {
+ sal_Int16 Code;
+ const char* Identifier;
+ };
+
+ /** @short hash structure to map identifier to key codes. */
+ typedef std::unordered_map<OUString, sal_Int16> Identifier2CodeHash;
+
+ /** @short hash structure to map key codes to identifier. */
+ typedef std::unordered_map<sal_Int16, OUString> Code2IdentifierHash;
+
+ // member
+
+ private:
+
+ static KeyIdentifierInfo const KeyIdentifierMap[];
+
+ /** @short hash to map identifier to key codes. */
+ Identifier2CodeHash m_lIdentifierHash;
+
+ /** @short hash to map key codes to identifier. */
+ Code2IdentifierHash m_lCodeHash;
+
+ // interface
+
+ public:
+
+ KeyMapping();
+
+ static KeyMapping & get();
+
+ /** @short return a suitable key code
+ for the specified key identifier.
+
+ @param sIdentifier
+ string value, which describe the key.
+
+ @return [css::awt::KeyEvent]
+ the corresponding key code as
+ short value.
+
+ @throw [css::lang::IllegalArgumentException]
+ if the given identifier does not describe
+ a well known key code.
+ */
+ sal_uInt16 mapIdentifierToCode(const OUString& sIdentifier);
+
+ /** @short return a suitable key identifier
+ for the specified key code.
+
+ @param nCode
+ short value, which describe the key.
+
+ @return The corresponding string identifier.
+ */
+ OUString mapCodeToIdentifier(sal_uInt16 nCode);
+
+ // helper
+
+ private:
+
+ /** @short check if the given string describe a numeric
+ value ... and convert it.
+
+ @param sIdentifier
+ the string value, which should be converted.
+
+ @param rCode
+ contains the converted code, but is defined only
+ if this method returns sal_True!
+
+ @return [boolean]
+ sal_True if conversion was successful.
+ */
+ bool impl_st_interpretIdentifierAsPureKeyCode(std::u16string_view sIdentifier,
+ sal_uInt16& rCode );
+};
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/accelerators/presethandler.hxx b/framework/source/inc/accelerators/presethandler.hxx
new file mode 100644
index 000000000..b0fee38b4
--- /dev/null
+++ b/framework/source/inc/accelerators/presethandler.hxx
@@ -0,0 +1,378 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <accelerators/storageholder.hxx>
+
+#include <com/sun/star/embed/XStorage.hpp>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <i18nlangtag/languagetag.hxx>
+
+namespace framework
+{
+/**
+ TODO document me
+
+ <layer>/global/<resourcetype>/<preset>.xml
+ <layer>/modules/<moduleid>/<resourcetype>/<preset>.xml
+
+ RESOURCETYPE PRESET TARGET
+ (share) (user)
+ "accelerator" "default" "current"
+ "word"
+ "excel"
+
+ "menubar" "default" "menubar"
+
+ */
+class PresetHandler
+{
+ public:
+
+ /** @short this handler can provide different
+ types of configuration.
+
+ @descr Means: a global or a module dependent
+ or ... configuration.
+ */
+ enum EConfigType
+ {
+ E_GLOBAL,
+ E_MODULES,
+ E_DOCUMENT
+ };
+
+ private:
+
+ /** @short can be used to create on needed uno resources. */
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ /** @short knows the type of provided configuration.
+
+ @descr e.g. global, modules, ...
+ */
+ EConfigType m_eConfigType;
+
+ /** @short if we run in document mode, we can't use the global root storages!
+ We have to use a special document storage explicitly. */
+ StorageHolder m_lDocumentStorages;
+
+ /** @short holds the folder storage of the share layer alive,
+ where the current configuration set exists.
+
+ @descr Note: If this preset handler works in document mode
+ this member is meant relative to the document root...
+ not to the share layer root!
+
+ Further is defined, that m_xWorkingStorageUser
+ is equals to m_xWorkingStorageShare then!
+ */
+ css::uno::Reference< css::embed::XStorage > m_xWorkingStorageShare;
+
+ /** @short global language-independent storage
+ */
+ css::uno::Reference< css::embed::XStorage > m_xWorkingStorageNoLang;
+
+ /** @short holds the folder storage of the user layer alive,
+ where the current configuration set exists.
+
+ @descr Note: If this preset handler works in document mode
+ this member is meant relative to the document root...
+ not to the user layer root!
+
+ Further is defined, that m_xWorkingStorageUser
+ is equals to m_xWorkingStorageShare then!
+ */
+ css::uno::Reference< css::embed::XStorage > m_xWorkingStorageUser;
+
+ /** @short knows the relative path from the root. */
+ OUString m_sRelPathShare;
+ OUString m_sRelPathUser;
+
+ // native interface
+
+ public:
+
+ /** @short does nothing real.
+
+ @param xContext
+ points to a uno service manager, which is used internally
+ to create own needed uno resources.
+ */
+ PresetHandler(css::uno::Reference< css::uno::XComponentContext > xContext);
+
+ /** @short copy ctor */
+ PresetHandler(const PresetHandler& rCopy);
+
+ /** @short closes all open storages ... if user forgot that .-) */
+ ~PresetHandler();
+
+ /** @short free all currently cache(!) storages. */
+ void forgetCachedStorages();
+
+ /** @short return access to the internally used and cached root storage.
+
+ @descr These root storages are the base of all further opened
+ presets and targets. They are provided here only, to support
+ older implementations, which base on them ...
+
+ getOrCreate...() - What does it mean?
+ Such root storage will be created one times only and
+ cached then internally till the last instance of such PresetHandler
+ dies.
+
+ @return css::embed::XStorage
+ which represent a root storage.
+ */
+ css::uno::Reference< css::embed::XStorage > getOrCreateRootStorageShare();
+ css::uno::Reference< css::embed::XStorage > getOrCreateRootStorageUser();
+
+ /** @short provides access to the current working storages.
+
+ @descr Working storages are the "lowest" storages, where the
+ preset and target files exists.
+
+ @return css::embed::XStorage
+ which the current working storage.
+ */
+ css::uno::Reference< css::embed::XStorage > getWorkingStorageUser() const;
+
+ /** @short check if there is a parent storage well known for
+ the specified child storage and return it.
+
+ @param xChild
+ the child storage where a paranet storage should be searched for.
+
+ @return css::embed::XStorage
+ A valid storage if a paranet exists. NULL otherwise.
+ */
+ css::uno::Reference< css::embed::XStorage > getParentStorageShare();
+ css::uno::Reference< css::embed::XStorage > getParentStorageUser ();
+
+ /** @short free all internal structures and let this handler
+ work on a new type of configuration sets.
+
+ @param eConfigType
+ differ between global or module dependent configuration.
+
+ @param sResourceType
+ differ between menubar/toolbar/accelerator/... configuration.
+
+ @param sModule
+ if sResourceType is set to a module dependent configuration,
+ it address the current application module.
+
+ @param xDocumentRoot
+ if sResourceType is set to E_DOCUMENT, this value points to the
+ root storage inside the document, where we can save our
+ configuration files. Note: that's not the real root of the document...
+ its only a sub storage. But we interpret it as our root storage.
+
+ @param rLanguageTag
+ in case this configuration supports localized entries,
+ the current locale must be set.
+
+ Localization will be represented as directory structure
+ of provided presets. Means: you call us with a preset name "default";
+ and we use e.g. "/en-US/default.xml" internally.
+
+ If no localization exists for this preset set, this class
+ will work in default mode - means "no locale" - automatically.
+ e.g. "/default.xml"
+
+ @throw css::uno::RuntimeException(!)
+ if the specified resource couldn't be located.
+ */
+ void connectToResource( EConfigType eConfigType ,
+ std::u16string_view sResourceType ,
+ std::u16string_view sModule ,
+ const css::uno::Reference< css::embed::XStorage >& xDocumentRoot ,
+ const LanguageTag& rLanguageTag = LanguageTag(LANGUAGE_USER_PRIV_NOTRANSLATE));
+
+ /** @short try to copy the specified preset from the share
+ layer to the user layer and establish it as the
+ specified target.
+
+ @descr Means: copy share/.../<preset>.xml user/.../<target>.xml
+ Note: The target will be overwritten completely or
+ created as new by this operation!
+
+ @param sPreset
+ the ALIAS name of an existing preset.
+
+ @param sTarget
+ the ALIAS name of the target.
+
+ @throw css::container::NoSuchElementException
+ if the specified preset does not exists.
+
+ @throw css::io::IOException
+ if copying failed.
+ */
+ void copyPresetToTarget(std::u16string_view sPreset,
+ std::u16string_view sTarget);
+
+ /** @short open the specified preset as stream object
+ and return it.
+
+ @descr Note: Because presets resist inside the share
+ layer, they will be opened readonly every time.
+
+ @param sPreset
+ the ALIAS name of an existing preset.
+
+ Accesses the global language-independent storage instead of the preset storage
+
+ @return The opened preset stream ... or NULL if the preset does not exists.
+ */
+ css::uno::Reference< css::io::XStream > openPreset(std::u16string_view sPreset);
+
+ /** @short open the specified target as stream object
+ and return it.
+
+ @descr Note: Targets resist inside the user
+ layer. Normally they are opened in read/write mode.
+ But it will be opened readonly automatically if that isn't possible
+ (may be the file is write protected on the system ...).
+
+ @param sTarget
+ the ALIAS name of the target.
+
+ @return The opened target stream ... or NULL if the target does not exists
+ or couldn't be created as new one.
+ */
+ css::uno::Reference< css::io::XStream > openTarget(
+ std::u16string_view sTarget, sal_Int32 nMode);
+
+ /** @short do anything which is necessary to flush all changes
+ back to disk.
+
+ @descr We have to call commit on all cached sub storages on the
+ path from the root storage upside down to the working storage
+ (which are not really used, but required to be holded alive!).
+ */
+ void commitUserChanges();
+
+ /** TODO */
+ void addStorageListener(XMLBasedAcceleratorConfiguration* pListener);
+ void removeStorageListener(XMLBasedAcceleratorConfiguration* pListener);
+
+ // helper
+
+ private:
+
+ /** @short open a config path ignoring errors (catching exceptions).
+
+ @descr We catch only normal exceptions here - no runtime exceptions.
+
+ @param sPath
+ the configuration path, which should be opened.
+
+ @param eMode
+ the open mode (READ/READWRITE)
+
+ @param bShare
+ force using of the share layer instead of the user layer.
+
+ @return An opened storage in case method was successful - null otherwise.
+ */
+ css::uno::Reference< css::embed::XStorage > impl_openPathIgnoringErrors(const OUString& sPath ,
+ sal_Int32 eMode ,
+ bool bShare);
+
+ /** @short try to find the specified locale inside list of possible ones.
+
+ @descr The list of possible locale values was e.g. retrieved from the system
+ (configuration, directory listing etcpp). The locale normally represent
+ the current office locale. This method search for a suitable item by using
+ different algorithm.
+ a) exact search
+ b) search with using fallbacks
+
+ @param lLocalizedValues
+ list of BCP47 language tags / locale codes
+
+ @param rLanguageTag
+ [IN ] the current office locale, which should be searched inside lLocalizedValues.
+ [OUT] in case fallbacks was allowed, it contains afterwards the fallback locale.
+
+ @param bAllowFallbacks
+ enable/disable using of fallbacks
+
+ @return An iterator, which points directly into lLocalizedValue list.
+ As a negative result the special iterator lLocalizedValues.end() will be returned.
+ */
+ ::std::vector< OUString >::const_iterator impl_findMatchingLocalizedValue(const ::std::vector< OUString >& lLocalizedValues,
+ OUString& rLanguageTag ,
+ bool bAllowFallbacks );
+
+ /** @short open a config path ignoring errors (catching exceptions).
+
+ @descr We catch only normal exceptions here - no runtime exceptions.
+ Further the path itself is tries in different versions (using locale
+ specific attributes).
+ e.g. "path/e-US" => "path/en" => "path/de"
+
+ @param sPath
+ the configuration path, which should be opened.
+ It's further used as out parameter too, so we can return the localized
+ path!
+
+ @param eMode
+ the open mode (READ/READWRITE)
+
+ @param bShare
+ force using of the share layer instead of the user layer.
+
+ @param rLanguageTag
+ [IN ] contains the start locale for searching localized sub dirs.
+ [OUT] contains the locale of a found localized sub dir
+
+ @param bAllowFallback
+ enable/disable fallback handling for locales
+
+ @return An opened storage in case method was successful - null otherwise.
+ */
+ css::uno::Reference< css::embed::XStorage > impl_openLocalizedPathIgnoringErrors(OUString& sPath ,
+ sal_Int32 eMode ,
+ bool bShare ,
+ OUString& rLanguageTag ,
+ bool bAllowFallback);
+
+ /** @short returns the names of all sub storages of specified storage.
+
+ @param xFolder
+ the base storage for this operation.
+
+ @return [vector< string >]
+ a list of folder names.
+ */
+ ::std::vector< OUString > impl_getSubFolderNames(const css::uno::Reference< css::embed::XStorage >& xFolder);
+};
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/accelerators/storageholder.hxx b/framework/source/inc/accelerators/storageholder.hxx
new file mode 100644
index 000000000..355bedeaa
--- /dev/null
+++ b/framework/source/inc/accelerators/storageholder.hxx
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/embed/XStorage.hpp>
+
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+namespace framework
+{
+
+class XMLBasedAcceleratorConfiguration;
+/**
+ TODO document me
+ */
+class StorageHolder final
+{
+
+ // types
+ public:
+
+ /** @short TODO */
+ typedef ::std::vector< css::uno::Reference< css::embed::XStorage > > TStorageList;
+
+ typedef ::std::vector< XMLBasedAcceleratorConfiguration* > TStorageListenerList;
+
+ struct TStorageInfo
+ {
+ public:
+ css::uno::Reference< css::embed::XStorage > Storage;
+ sal_Int32 UseCount;
+ TStorageListenerList Listener;
+
+ TStorageInfo()
+ : UseCount(0)
+ {}
+ };
+
+ /** @short TODO */
+ typedef std::unordered_map< OUString,
+ TStorageInfo > TPath2StorageInfo;
+
+ // member
+ private:
+ mutable std::mutex m_mutex;
+
+ /** @short TODO */
+ css::uno::Reference< css::embed::XStorage > m_xRoot;
+
+ /** @short TODO */
+ TPath2StorageInfo m_lStorages;
+
+ // interface
+ public:
+
+ /** @short TODO
+ */
+ StorageHolder();
+
+ /** @short TODO
+ */
+ ~StorageHolder();
+
+ /** @short TODO
+ */
+ void forgetCachedStorages();
+
+ /** @short TODO
+ */
+ void setRootStorage(const css::uno::Reference< css::embed::XStorage >& xRoot);
+
+ /** @short TODO
+ */
+ css::uno::Reference< css::embed::XStorage > getRootStorage() const;
+
+ /** @short TODO
+ open or get!
+ */
+ css::uno::Reference< css::embed::XStorage > openPath(const OUString& sPath ,
+ sal_Int32 nOpenMode);
+
+ /** @short TODO
+ */
+ StorageHolder::TStorageList getAllPathStorages(const OUString& sPath);
+
+ /** @short TODO
+ */
+ void commitPath(const OUString& sPath);
+
+ /** @short TODO
+ */
+ void closePath(const OUString& sPath);
+
+ /** @short TODO
+ */
+ void notifyPath(const OUString& sPath);
+
+ /** @short TODO
+ */
+ void addStorageListener( XMLBasedAcceleratorConfiguration* pListener,
+ const OUString& sPath );
+
+ /** @short TODO
+ */
+ void removeStorageListener( XMLBasedAcceleratorConfiguration* pListener,
+ const OUString& sPath );
+
+ /** @short TODO
+ */
+ OUString getPathOfStorage(const css::uno::Reference< css::embed::XStorage >& xStorage);
+
+ /** @short TODO
+ */
+ css::uno::Reference< css::embed::XStorage > getParentStorage(const css::uno::Reference< css::embed::XStorage >& xChild);
+
+ /** @short TODO
+ */
+ css::uno::Reference< css::embed::XStorage > getParentStorage(const OUString& sChildPath);
+
+ /** @short TODO
+ */
+ StorageHolder& operator=(const StorageHolder& rCopy);
+
+ /** @short opens a sub element of the specified base storage.
+ If eOpenMode contains an ELEMENT_WRITE flag remove it and try it with the rest of eOpenMode flags
+ again.
+
+ @descr First this method try to open the requested sub element
+ using the given open mode. If it failed there is second step,
+ which tries to do the same again ... but removing a might existing
+ WRITE flag from the open mode. The user can suppress this fallback
+ handling by setting the parameter bAllowFallback to sal_False.
+
+ @param xBaseStorage
+ the storage, where the sub element should be searched.
+
+ @param sSubElement
+ the full name of the sub element.
+ e.g. "default.xml"
+
+ @param eOpenMode
+ a flag field, which set the open mode for this operation.
+
+ */
+ static css::uno::Reference< css::embed::XStorage > openSubStorageWithFallback(const css::uno::Reference< css::embed::XStorage >& xBaseStorage ,
+ const OUString& sSubStorage ,
+ sal_Int32 eOpenMode);
+
+ // helper
+ private:
+
+ /** @short TODO
+ */
+ static OUString impl_st_normPath(const OUString& sPath);
+
+ /** @short TODO
+ */
+ static std::vector<OUString> impl_st_parsePath(std::u16string_view sPath);
+};
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/dispatch/dispatchdisabler.hxx b/framework/source/inc/dispatch/dispatchdisabler.hxx
new file mode 100644
index 000000000..662eeb5d7
--- /dev/null
+++ b/framework/source/inc/dispatch/dispatchdisabler.hxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include <set>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XInterceptorInfo.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp>
+
+namespace framework {
+
+/**
+ * Implementation of a service to make it easy to disable a whole
+ * suite of UNO commands in a batch - and have that act in-process.
+ *
+ * Often external re-use of LibreOffice wants a very cut-down set
+ * of functionality included, and disabling elements remotely one
+ * by one performs poorly.
+ */
+class DispatchDisabler final : public ::cppu::WeakImplHelper<
+ css::lang::XInitialization,
+ css::container::XNameContainer,
+ css::frame::XDispatchProviderInterceptor,
+ css::frame::XInterceptorInfo,
+ css::lang::XServiceInfo >
+{
+ std::set<OUString> maDisabledURLs;
+ css::uno::Reference< css::frame::XDispatchProvider > mxSlave;
+ css::uno::Reference< css::frame::XDispatchProvider > mxMaster;
+public:
+ DispatchDisabler(const css::uno::Reference< css::uno::XComponentContext >& rxContext);
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const ::css::uno::Sequence< ::css::uno::Any >& aArguments ) override;
+
+ // XDispatchProvider
+ virtual ::css::uno::Reference< ::css::frame::XDispatch > SAL_CALL
+ queryDispatch( const ::css::util::URL& URL,
+ const OUString& TargetFrameName,
+ ::sal_Int32 SearchFlags ) override;
+ virtual ::css::uno::Sequence< ::css::uno::Reference< ::css::frame::XDispatch > > SAL_CALL
+ queryDispatches( const ::css::uno::Sequence< ::css::frame::DispatchDescriptor >& Requests ) override;
+
+ // XDispatchProviderInterceptor
+ virtual ::css::uno::Reference< ::css::frame::XDispatchProvider > SAL_CALL
+ getSlaveDispatchProvider() override;
+ virtual void SAL_CALL
+ setSlaveDispatchProvider( const ::css::uno::Reference< ::css::frame::XDispatchProvider >& NewDispatchProvider ) override;
+ virtual ::css::uno::Reference< ::css::frame::XDispatchProvider > SAL_CALL
+ getMasterDispatchProvider() override;
+ virtual void SAL_CALL
+ setMasterDispatchProvider( const ::css::uno::Reference< ::css::frame::XDispatchProvider >& NewSupplier ) override;
+
+ // XInterceptorInfo
+ virtual ::css::uno::Sequence< OUString > SAL_CALL
+ getInterceptedURLs() override;
+
+ // XElementAccess
+ virtual ::css::uno::Type SAL_CALL getElementType() override;
+ virtual ::sal_Bool SAL_CALL hasElements() override;
+
+ // XNameAccess
+ virtual ::css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual ::css::uno::Sequence< OUString > SAL_CALL getElementNames() override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const ::css::uno::Any& aElement ) override;
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const ::css::uno::Any& aElement ) override;
+ virtual void SAL_CALL removeByName( const OUString& Name ) override;
+
+ /* interface XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+};
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/dispatch/loaddispatcher.hxx b/framework/source/inc/dispatch/loaddispatcher.hxx
new file mode 100644
index 000000000..195a46af8
--- /dev/null
+++ b/framework/source/inc/dispatch/loaddispatcher.hxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <loadenv/loadenv.hxx>
+
+#include <com/sun/star/frame/XNotifyingDispatch.hpp>
+#include <com/sun/star/frame/XSynchronousDispatch.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weakref.hxx>
+
+namespace framework{
+
+/** @short implements a dispatch object which can be used to load
+ non-visible components (by using the mechanism of ContentHandler)
+ or visible-components (by using the mechanism of FrameLoader).
+ */
+class LoadDispatcher final : public ::cppu::WeakImplHelper< css::frame::XNotifyingDispatch, // => XDispatch => XInterface
+ css::frame::XSynchronousDispatch >
+{
+
+ // member
+
+ private:
+ osl::Mutex m_mutex;
+
+ /** @short TODO document me */
+ css::uno::WeakReference< css::frame::XFrame > m_xOwnerFrame;
+
+ /** @short TODO document me */
+ OUString m_sTarget;
+
+ /** @short TODO document me */
+ sal_Int32 m_nSearchFlags;
+
+ /** @short TODO document me */
+ LoadEnv m_aLoader;
+
+ // native interface
+
+ public:
+
+ /** @short creates a new instance and initialize it with all necessary parameters.
+
+ @descr Every instance of such LoadDispatcher can be used for the specified context only.
+ That means: it can be used to load any further requested content into the here(!)
+ specified target frame.
+
+ @param xContext
+ will be used to create own needed services on demand.
+
+ @param xOwnerFrame
+ used as startpoint to locate the right target frame.
+
+ @param sTargetName
+ the name or the target frame for loading or a special qualifier
+ which define such target.
+
+ @param nSearchFlags
+ used in case sTargetFrame isn't a special one.
+ */
+ LoadDispatcher(const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Reference< css::frame::XFrame >& xOwnerFrame ,
+ OUString sTargetName ,
+ sal_Int32 nSearchFlags);
+
+ /** @short used to free internal resources.
+ */
+ virtual ~LoadDispatcher() override;
+
+ // uno interface
+
+ public:
+
+ // XNotifyingDispatch
+ virtual void SAL_CALL dispatchWithNotification(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) override;
+
+ // XDispatch
+ virtual void SAL_CALL dispatch(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments) override;
+
+ virtual void SAL_CALL addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
+ const css::util::URL& aURL ) override;
+
+ virtual void SAL_CALL removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
+ const css::util::URL& aURL ) override;
+
+ // XSynchronousDispatch
+ virtual css::uno::Any SAL_CALL dispatchWithReturnValue( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override;
+
+ private:
+ css::uno::Any impl_dispatch( const css::util::URL& rURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener );
+}; // class LoadDispatcher
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/dispatch/windowcommanddispatch.hxx b/framework/source/inc/dispatch/windowcommanddispatch.hxx
new file mode 100644
index 000000000..4a8a22d81
--- /dev/null
+++ b/framework/source/inc/dispatch/windowcommanddispatch.hxx
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+
+#include <cppuhelper/weakref.hxx>
+#include <tools/link.hxx>
+#include <mutex>
+
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+class VclWindowEvent;
+
+namespace framework{
+
+/** @short internal helper to bind e.g. MAC-Menu events to our internal dispatch API.
+
+ @descr On e.g. MAC platform system menus are merged together with some fix entries as
+ e.g. "Pereferences" or "About". These menu entries trigger hard coded commands.
+ Here we map these commands to the right URLs and dispatch them.
+
+ This helper knows a frame and its container window (where VCL provide the hard coded
+ commands). We hold those objects weak so there is no need to react for complex UNO dispose/ing()
+ scenarios. On the other side VCL does not hold us alive (because it doesn't know our UNO reference).
+ So we register at the VCL level as an event listener and
+ */
+class WindowCommandDispatch final
+{
+ private:
+ std::mutex m_mutex;
+
+ /// can be used to create own needed services on demand.
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ /// knows the frame, where we dispatch our commands as weak reference
+ css::uno::WeakReference< css::frame::XFrame > m_xFrame;
+
+ /// knows the VCL window (where the hard coded commands occurred) as weak XWindow reference
+ css::uno::WeakReference< css::awt::XWindow > m_xWindow;
+
+ // native interface
+
+ public:
+
+ /** @short creates a new instance and initialize it with all necessary parameters.
+
+ @descr Every instance of such MACDispatch can be used for the specified context only.
+ Means: 1 MACDispatch object is bound to 1 Frame/Window pair in which context
+ the detected commands will be executed.
+
+ @param xContext
+ will be used to create own needed services on demand.
+
+ @param xFrame
+ used as for new detected commands.
+ */
+ WindowCommandDispatch(css::uno::Reference< css::uno::XComponentContext > xContext ,
+ const css::uno::Reference< css::frame::XFrame >& xFrame);
+
+ /** @short used to free internal resources.
+ */
+ ~WindowCommandDispatch();
+
+ // implementation
+
+ private:
+
+ /** @short establish all listener connections we need.
+
+ @descr Those listener connections will be created one times only (see ctor).
+ Afterwards we listen for incoming events till our referred frame/window pair
+ will be closed.
+ */
+ void impl_startListening();
+
+ /** @short drop all listener connections we need.
+
+ */
+ void impl_stopListening();
+
+ /** @short callback from VCL to notify new commands
+ */
+ DECL_LINK( impl_notifyCommand, VclWindowEvent&, void );
+
+}; // class WindowCommandDispatch
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/loadenv/actionlockguard.hxx b/framework/source/inc/loadenv/actionlockguard.hxx
new file mode 100644
index 000000000..ee52fcc0d
--- /dev/null
+++ b/framework/source/inc/loadenv/actionlockguard.hxx
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/document/XActionLockable.hpp>
+#include <mutex>
+
+namespace framework{
+
+/** @short implements a guard, which can use the interface
+ <type scope="css::document">XActionLockable</type>.
+
+ @descr This guard should be used to be sure, that any lock will be
+ released. Otherwise the locked document can hinder the office on shutdown!
+*/
+class ActionLockGuard final
+{
+
+ // member
+
+ private:
+ std::mutex m_mutex;
+
+ /** @short points to the object, which can be locked from outside. */
+ css::uno::Reference< css::document::XActionLockable > m_xActionLock;
+
+ /** @short knows if a lock exists on the internal lock object
+ forced by this guard instance. */
+ bool m_bActionLocked;
+
+ // interface
+
+ public:
+
+ /** @short default ctor to initialize a "non working guard".
+
+ @descr That can be useful in cases, where no resource still exists,
+ but will be available next time. Then this guard can be used
+ in a mode "use guard for more than one resources".
+ */
+ ActionLockGuard()
+ : m_bActionLocked(false)
+ {
+ }
+
+ /** @short release this guard instance and make sure, that no lock
+ will exist afterwards on the internal wrapped resource.
+ */
+ ~ActionLockGuard()
+ {
+ unlock();
+ }
+
+ /** @short set a new resource for locking at this guard.
+
+ @descr This call will fail, if an internal resource already exists
+ and is currently locked.
+
+ @param xLock
+ points to the outside resource, which should be locked.
+
+ @return sal_True, if new resource could be set and locked.
+ sal_False otherwise.
+ */
+ bool setResource(const css::uno::Reference< css::document::XActionLockable >& xLock)
+ {
+ std::unique_lock g(m_mutex);
+
+ if (m_bActionLocked || !xLock.is())
+ return false;
+
+ m_xActionLock = xLock;
+ m_xActionLock->addActionLock();
+ m_bActionLocked = m_xActionLock->isActionLocked();
+
+ return true;
+ }
+
+ /** @short set a new resource for locking at this guard.
+
+ @descr This call will fail, if an internal resource already exists
+ and is currently locked.
+
+ @param xLock
+ points to the outside resource, which should be locked.
+
+ @return sal_True, if new resource could be set and locked.
+ sal_False otherwise.
+ */
+ void freeResource()
+ {
+ // SAFE -> ..........................
+ std::unique_lock aMutexLock(m_mutex);
+
+ css::uno::Reference< css::document::XActionLockable > xLock = m_xActionLock;
+ bool bLocked = m_bActionLocked;
+
+ m_xActionLock.clear();
+ m_bActionLocked = false;
+
+ aMutexLock.unlock();
+ // <- SAFE ..........................
+
+ if (bLocked && xLock.is())
+ xLock->removeActionLock();
+ }
+
+ /** @short unlock the internal wrapped resource, if it's not already done. */
+ void unlock()
+ {
+ std::unique_lock g(m_mutex);
+ if (m_bActionLocked && m_xActionLock.is())
+ {
+ m_xActionLock->removeActionLock();
+ // don't check for any locks here ...
+ // May another guard use the same lock object :-(
+ m_bActionLocked = false;
+ }
+ }
+};
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/loadenv/loadenv.hxx b/framework/source/inc/loadenv/loadenv.hxx
new file mode 100644
index 000000000..351d39b94
--- /dev/null
+++ b/framework/source/inc/loadenv/loadenv.hxx
@@ -0,0 +1,540 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <loadenv/actionlockguard.hxx>
+
+#include <com/sun/star/frame/XComponentLoader.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <rtl/ref.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <o3tl/typed_flags_set.hxx>
+
+
+/** @short enable/disable special features
+ of a load request.
+
+ @desrc Such features must outcome without
+ any special parameters.
+ To make enabling/disabling of
+ features very easy (e.g. at the ctor of
+ this class) these values must be combinable
+ as flags. That means: its values must be in
+ range of [2^n]!
+ */
+enum class LoadEnvFeatures
+{
+ /// we should be informed, if no feature is enabled :-)
+ NONE = 0,
+ /// enable using of UI elements during loading (means progress, interaction handler etcpp.)
+ WorkWithUI = 1,
+ /// enable loading of resources, which are not related to a target frame! (see concept of ContentHandler)
+ AllowContentHandler = 2
+};
+namespace o3tl {
+ template<> struct typed_flags<LoadEnvFeatures> : is_typed_flags<LoadEnvFeatures, 0x3> {};
+}
+
+
+namespace framework {
+
+class QuietInteraction;
+
+/** @short implements general mechanism for loading documents.
+
+ @descr An instance of this class can be used inside the API calls
+ XComponentLoader::loadComponentFromURL() and
+ XDispatch::dispatch().
+ */
+class LoadEnv
+{
+public:
+ /** @short classify a content.
+
+ @descr The load environment must know, if a content
+ is related to a target frame or not. Only "visible"
+ components, which fulfill the requirements of the
+ model-controller-view paradigm can be loaded into a frame.
+ Such contents are classified as E_CAN_BE_LOADED.
+
+ But e.g. for the dispatch framework exists special ContentHandler
+ objects, which can load a content in "non visible" mode ...
+ and do not need a target frame for its operation. Such
+ ContentHandler e.g. plays sounds.
+ Such contents are classified as E_CAN_BE_HANDLED.
+
+ And last but not least a content can be "not valid" in general.
+ */
+ enum EContentType
+ {
+ /// identifies a content, which seems to be invalid in general
+ E_UNSUPPORTED_CONTENT,
+ /// identifies a content, which can be used with a ContentHandler and is not related to a target frame
+ E_CAN_BE_HANDLED,
+ /// identifies a content, which can be loaded into a target frame
+ E_CAN_BE_LOADED,
+ /// special mode for non real loading, In such case the model is given directly!
+ E_CAN_BE_SET
+ };
+
+private:
+ mutable osl::Mutex m_mutex;
+
+ /** @short reference to a uno service manager, which must be used
+ to created on needed services on demand.
+ */
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ /** @short points to the frame, which uses this LoadEnv object
+ and must be used to start target search there.
+ */
+ css::uno::Reference< css::frame::XFrame > m_xBaseFrame;
+
+ /** @short points to the frame, into which the new component was loaded.
+
+ @descr Note: This reference will be empty if loading failed
+ or a non visible content was loaded!
+ It can be the same frame as m_xBaseFrame it describe, in case
+ the target "_self", "" or the search flag "SELF" was used.
+ Otherwise it's the new created or recycled frame, which was
+ used for loading and contains further the new component.
+
+ Please use method getTarget() or getTargetComponent()
+ to return the frame/controller or model to any interested
+ user of the results of this load request.
+ */
+ css::uno::Reference< css::frame::XFrame > m_xTargetFrame;
+
+ /** @short contains the name of the target, in which the specified resource
+ of this instance must be loaded.
+ */
+ OUString m_sTarget;
+
+ /** @short if m_sTarget is not a special one, this flags regulate searching
+ of a suitable one.
+ */
+ sal_Int32 m_nSearchFlags;
+
+ /** @short contains all needed information about the resource,
+ which should be loaded.
+
+ @descr Inside this struct e.g. the URL, its type and filter name,
+ the stream or a model directly are saved.
+ */
+ utl::MediaDescriptor m_lMediaDescriptor;
+
+ /** @short because the mediadescriptor contains the complete URL ... but
+ some functionality need the structured version, we hold it twice :-(.
+ */
+ css::util::URL m_aURL;
+
+ /** @short enable/disable special features of a load request. */
+ LoadEnvFeatures m_eFeature;
+
+ /** @short classify the content, which should be loaded by this instance. */
+ EContentType m_eContentType;
+
+ /** @short it indicates, that the member m_xTargetFrame was new created for this
+ load request and must be closed in case loading (not handling!)
+ operation failed. The default value is sal_False!
+ */
+ bool m_bCloseFrameOnError;
+
+ /** @short it indicates, that the old document (which was located inside m_xBaseFrame
+ in combination with the m_sTarget value "_self") was suspended.
+ Normally it will be replaced by the new loaded document. But in case
+ loading (not handling!) failed, it must be reactivated.
+ The default value is sal_False!
+ */
+ bool m_bReactivateControllerOnError;
+
+ /** @short it holds one (!) asynchronous used contenthandler or frameloader
+ alive, till the asynchronous operation will be finished.
+ */
+ css::uno::Reference< css::uno::XInterface > m_xAsynchronousJob;
+
+ /** @short holds the information about the finished load process.
+
+ @descr The content of m_xTargetFrame can't be used as valid indicator,
+ (in case the existing old document was reactivated)
+ we must hold the result of the load process explicitly.
+ */
+ bool m_bLoaded;
+
+ /** @short holds an XActionLock on the internal used task member.
+
+ @seealso m_xTargetFrame
+ */
+ ActionLockGuard m_aTargetLock;
+
+ rtl::Reference<QuietInteraction> m_pQuietInteraction;
+
+public:
+
+ /** @short initialize a new instance of this load environment.
+
+ @param xContext
+ reference to a uno service manager, which can be used internally
+ to create on needed services on demand.
+
+ @throw Currently there is no reason to throw such exception!
+
+ @throw A RuntimeException in case any internal process indicates, that
+ the whole runtime can't be used any longer.
+ */
+ LoadEnv(css::uno::Reference< css::uno::XComponentContext > xContext);
+
+ /** @short deinitialize an instance of this class in the right way.
+ */
+ ~LoadEnv();
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::io::IOException
+ /// @throws css::uno::RuntimeException
+ static css::uno::Reference< css::lang::XComponent > loadComponentFromURL(const css::uno::Reference< css::frame::XComponentLoader >& xLoader,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const OUString& sURL ,
+ const OUString& sTarget,
+ sal_Int32 nFlags ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArgs );
+
+ /** @short start loading of a resource
+
+ @descr The parameter for targeting, the content description, and
+ some environment specifier (UI, dispatch functionality)
+ can be set here. Of course a still running load request
+ will be detected here and a suitable exception will be thrown.
+ Such constellation can be detected outside by using provided
+ synchronisation methods or callbacks.
+
+ There is no direct return value possible here. Because it depends
+ from the usage of this instance! E.g. for loading a "visible component"
+ a frame with a controller/model inside can be possible. For loading
+ of a "non visible component" only an information about a successfully start
+ can be provided.
+ Further it can't be guaranteed, that the internal process runs synchronous.
+ that's why we prefer using of specialized methods afterwards e.g. to:
+ - wait till the internal job will be finished
+ and get the results
+ - or to let it run without any further control from outside.
+
+ @param sURL
+ points to the resource, which should be loaded.
+
+ @param lMediaDescriptor
+ contains additional information for the following load request.
+
+ @param xBaseFrame
+ points to the frame which must be used as start point for target search.
+
+ @param sTarget
+ regulate searching/creating of frames, which should contain the
+ new loaded component afterwards.
+
+ @param nSearchFlags
+ regulate searching of targets, if sTarget is not a special one.
+
+ @param eFeature
+ flag field, which enable/disable special features of this
+ new instance for following load call.
+
+ @throw A LoadEnvException e.g. if another load operation is till in progress
+ or initialization of a new one fail by other reasons.
+ The real reason, a suitable message and ID will be given here immediately.
+
+ @throw A RuntimeException in case any internal process indicates, that
+ the whole runtime can't be used any longer.
+ */
+ void startLoading(const OUString& sURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lMediaDescriptor,
+ const css::uno::Reference< css::frame::XFrame >& xBaseFrame ,
+ const OUString& sTarget ,
+ sal_Int32 nSearchFlags ,
+ LoadEnvFeatures eFeature = LoadEnvFeatures::NONE);
+
+ /** @short wait for an already running load request (started by calling
+ startLoading() before).
+
+ @descr The timeout parameter can be used to wait some times only
+ or forever. The return value indicates if the load request
+ was finished during the specified timeout period.
+ But it indicates not, if the load request was successful or not!
+
+ @param nTimeout
+ specify a timeout in [ms].
+ A value 0 let it wait forever!
+
+ @return sal_True if the started load process could be finished in time;
+ sal_False if the specified time was over.
+
+ @throw ... currently not used :-)
+
+ @throw A RuntimeException in case any internal process indicates, that
+ the whole runtime can't be used any longer.
+ */
+ bool waitWhileLoading(sal_uInt32 nTimeout = 0);
+
+ /** TODO document me ... */
+ css::uno::Reference< css::lang::XComponent > getTargetComponent() const;
+
+public:
+
+ /** @short checks if the specified content can be handled by a
+ ContentHandler only and is not related to a target frame,
+ or if it can be loaded by a FrameLoader into a target frame
+ as "visible" component.
+
+ @descr using:
+ switch(classifyContent(...))
+ {
+ case E_CAN_BE_HANDLED :
+ handleIt(...);
+ break;
+
+ case E_CAN_BE_LOADED :
+ xFrame = locateTargetFrame();
+ loadIt(xFrame);
+ break;
+
+ case E_NOT_A_CONTENT :
+ default : throw ...;
+ }
+
+ @param sURL
+ describe the content.
+
+ @param lMediaDescriptor
+ describe the content more detailed!
+
+ @return A suitable enum value, which classify the specified content.
+ */
+ static EContentType classifyContent(const OUString& sURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lMediaDescriptor);
+
+ /** TODO document me ... */
+ static void initializeUIDefaults(
+ const css::uno::Reference< css::uno::XComponentContext >& i_rxContext,
+ utl::MediaDescriptor& io_lMediaDescriptor,
+ const bool _bUIMode,
+ rtl::Reference<QuietInteraction>* o_ppQuiteInteraction
+ );
+
+ /** TODO document me ... */
+ void impl_setResult(bool bResult);
+
+ /** TODO document me ... */
+ css::uno::Reference< css::uno::XInterface > impl_searchLoader();
+
+ /** @short it means; show the frame, bring it to front,
+ might set the right icon etcpp. in case loading was
+ successfully or reactivate a might existing old document or
+ close the frame if it was created before in case loading failed.
+
+ @throw A LoadEnvException only in cases, where an internal error indicates,
+ that the complete load environment seems to be not usable in general.
+ In such cases a RuntimeException would be to hard for the outside code :-)
+
+ @throw A RuntimeException in case any internal process indicates, that
+ the whole runtime can't be used any longer.
+ */
+ void impl_reactForLoadingState();
+
+private:
+ void start();
+
+ /** @short tries to detect the type and the filter of the specified content.
+
+ @descr This method update the available media descriptor of this instance,
+ so it contains the right type, a corresponding filter, may a
+ valid frame loader etc. In case detection failed, this descriptor
+ is corrected first, before a suitable exception will be thrown.
+ (Excepting a RuntimeException occurrence!)
+
+ @attention Not all types we know, are supported by filters. So it does not
+ indicates an error, if no suitable filter(loader etcpp will be found
+ for a type. But a type must be detected for the specified content.
+ Otherwise it's an error and loading can't be finished successfully.
+
+ @throw A LoadEnvException if detection failed.
+
+ @throw A RuntimeException in case any internal process indicates, that
+ the whole runtime can't be used any longer.
+ */
+ void impl_detectTypeAndFilter();
+
+ /** @short tries to use ContentHandler objects for loading.
+
+ @descr It searches for a suitable content handler object, registered
+ for the detected content type (must be done before by calling
+ impl_detectTypeAndFilter()). Because such handler does not depend
+ from a real target frame, location of such frame will be
+ suppressed here.
+ In case handle failed all new created resources will be
+ removed before a suitable exception is thrown.
+ (Excepting a RuntimeException occurrence!)
+
+ @return TODO
+
+ @throw A LoadEnvException if handling failed.
+
+ @throw A RuntimeException in case any internal process indicates, that
+ the whole runtime can't be used any longer.
+ */
+ bool impl_handleContent();
+
+ /** @short tries to use FrameLoader objects for loading.
+
+ @descr First the target frame will be located. If it could be found
+ or new created a filter/frame loader will be instantiated and
+ used to load the content into this frame.
+ In case loading failed all new created resources will be
+ removed before a suitable exception is thrown.
+ (Excepting a RuntimeException occurrence!)
+
+ @return TODO
+
+ @throw A LoadEnvException if loading failed.
+
+ @throw A RuntimeException in case any internal process indicates, that
+ the whole runtime can't be used any longer.
+ */
+ bool impl_loadContent();
+
+ /** @short checks if the specified content is already loaded.
+
+ @descr It depends from the set target information, if such
+ search is allowed or not! So this method checks first,
+ if the target is the special one "_default".
+ If not it returns with an empty result immediately!
+ In case search is allowed, an existing document with the
+ same URL is searched. If it could be found, the corresponding
+ view will get the focus and this method return the corresponding frame.
+ Optional jumpmarks will be accepted here too. So the
+ view of the document will be updated to show the position
+ inside the document, which is related to the jumpmark.
+
+ @return A valid reference to the target frame, which contains the already loaded content
+ and could be activated successfully. An empty reference otherwise.
+
+ @throw A LoadEnvException only in cases, where an internal error indicates,
+ that the complete load environment seems to be not usable in general.
+ In such cases a RuntimeException would be to hard for the outside code :-)
+
+ @throw A RuntimeException in case any internal process indicates, that
+ the whole runtime can't be used any longer.
+ */
+ css::uno::Reference< css::frame::XFrame > impl_searchAlreadyLoaded();
+
+ /** @short search for any target frame, which seems to be usable
+ for this load request.
+
+ @descr Because this special feature is bound to the target specifier "_default"
+ its checked inside first. If it's not set => this method return an empty
+ reference. Otherwise any currently existing frame will be analyzed, if
+ it can be used here. The following rules exists:
+
+ <ul>
+ <li>The frame must be empty ...</li>
+ <li>or contains an empty document of the same application module
+ which the new document will have (Note: the filter of the new content
+ must be well known here!)</li>
+ <li>and(!) this target must not be already used by any other load request.</li>
+ </ul>
+
+ If a suitable target is located it will be locked. That's why the last rule
+ exists! If this method returns a valid frame reference, it was locked to be usable
+ for this load request only. (Don't forget to reset this state later!)
+ Concurrent LoadEnv instances can synchronize her work be using such locks :-) HOPEFULLY
+
+ @throw A LoadEnvException only in cases, where an internal error indicates,
+ that the complete load environment seems to be not usable in general.
+ In such cases a RuntimeException would be to hard for the outside code :-)
+
+ @throw A RuntimeException in case any internal process indicates, that
+ the whole runtime can't be used any longer.
+ */
+ css::uno::Reference< css::frame::XFrame > impl_searchRecycleTarget();
+
+ /** @short because showing of a frame is needed more than once...
+ it's implemented as a separate method .-)
+
+ @descr Note: Showing of a frame is bound to a special feature...
+ a) If we recycle any existing frame, we must bring it to front.
+ Showing of such frame is not needed really... because we recycle
+ visible frames only!
+ b) If the document was already shown (e.g. by our progress implementation)
+ we do nothing here. The reason behind: The document was already shown...
+ and it was already make a top window...
+ If the user activated another frame inbetween (because loading needed some time)
+ it's not allowed to disturb the user again. Then the frame must resists in the background.
+ c) If the frame was not shown before... but loading of a visible document into this frame
+ was finished... we need both actions: setVisible() and toFront().
+
+ @param xWindow
+ points to the container window of a frame.
+
+ @param bForceToFront
+ if it's set to sal_False... showing of the window is done more intelligent.
+ setVisible() is called only if the window was not shown before.
+ This mode is needed by b) and c)
+ If it's set to sal_True... both actions has to be done: setVisible(), toFront()!
+ This mode is needed by a)
+ */
+ void impl_makeFrameWindowVisible(const css::uno::Reference< css::awt::XWindow >& xWindow ,
+ bool bForceToFront);
+
+ /** @short checks whether a frame is already used for another load request or not.
+
+ @descr Such frames can't be used for our "recycle feature"!
+
+ @param xFrame
+ the frame, which should be checked.
+
+ @return [sal_Bool]
+ sal_True if this frame is already used for loading,
+ sal_False otherwise.
+ */
+ bool impl_isFrameAlreadyUsedForLoading(const css::uno::Reference< css::frame::XFrame >& xFrame) const;
+
+ /** @short try to determine the used application module
+ of this load request and apply right position and size
+ for this document window... hopefully before we show it .-)
+ */
+ void impl_applyPersistentWindowState(const css::uno::Reference< css::awt::XWindow >& xWindow);
+
+ /** @short determine if it's allowed to open new document frames.
+ */
+ bool impl_furtherDocsAllowed();
+
+ /** @short jumps to the requested bookmark inside a given document.
+ */
+ void impl_jumpToMark(const css::uno::Reference< css::frame::XFrame >& xFrame,
+ const css::util::URL& aURL );
+
+ /** @short determine if this loader has an interactive dialog shown before
+ loading the document.
+ */
+ bool impl_filterHasInteractiveDialog() const;
+};
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/loadenv/loadenvexception.hxx b/framework/source/inc/loadenv/loadenvexception.hxx
new file mode 100644
index 000000000..62a17d2ac
--- /dev/null
+++ b/framework/source/inc/loadenv/loadenvexception.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Any.hxx>
+#include <utility>
+
+namespace framework{
+
+/** @short specify an exception, which can be used inside the
+ load environment only.
+
+ @descr Of course outside code must wrap it, to transport
+ the occurred information to its caller.
+ */
+class LoadEnvException
+{
+ public:
+ /** @short Can be used as an ID for an instance of a LoadEnvException.
+ @descr To prevent errors on adding/removing/changing such IDs here,
+ an enum field is used. Its int values are self organized...
+ */
+ enum EIDs
+ {
+ /** @short The specified URL/Stream/etcpp. can not be handled by a LoadEnv instance. */
+ ID_UNSUPPORTED_CONTENT,
+
+ /** @short indicates a corrupted media descriptor.
+ @descr Some parts are required - some other ones are optional. Such exception
+ should be thrown, if a required item does not exists. */
+ ID_INVALID_MEDIADESCRIPTOR,
+
+ /** @short Its similar to a uno::RuntimeException...
+ @descr But such runtime exception can break the whole office code.
+ So its capsulated to this specialized load environment only.
+ Mostly it indicates a missing but needed resource ... e.g the
+ global desktop reference! */
+ ID_INVALID_ENVIRONMENT,
+
+ /** @short indicates a failed search for the right target frame. */
+ ID_NO_TARGET_FOUND,
+
+ /** @short TODO */
+ ID_COULD_NOT_REACTIVATE_CONTROLLER,
+
+ /** @short indicates an already running load operation. Of course the same
+ instance can't be used for multiple load requests at the same time.
+ */
+ ID_STILL_RUNNING,
+
+ /** @short sometimes we can't specify the reason for an error, because we
+ was interrupted by a called code in an unexpected way ...
+ */
+ ID_GENERAL_ERROR
+ };
+
+ sal_Int32 m_nID;
+ OUString m_sMessage;
+ css::uno::Any m_exOriginal;
+
+ LoadEnvException(
+ sal_Int32 id, OUString message = OUString(),
+ css::uno::Any original = css::uno::Any()):
+ m_nID(id), m_sMessage(std::move(message)), m_exOriginal(std::move(original))
+ {}
+};
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/loadenv/targethelper.hxx b/framework/source/inc/loadenv/targethelper.hxx
new file mode 100644
index 000000000..1f0d93d7e
--- /dev/null
+++ b/framework/source/inc/loadenv/targethelper.hxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+namespace framework{
+
+/** @short can be used to detect, if a target name (used e.g. for XFrame.findFrame())
+ has a special meaning or can be used as normal frame name (e.g. for XFrame.setName()).
+ */
+class TargetHelper
+{
+
+ public:
+
+ /** @short it's used at the following interfaces to classify
+ target names.
+ */
+ enum class ESpecialTarget
+ {
+ Blank,
+ Default,
+ Beamer,
+ HelpTask
+ };
+
+ // interface
+
+ public:
+
+ /** @short it checks the given unknown target name,
+ if it's the expected special one.
+
+ @note An empty target is similar to "_self"!
+
+ @param sCheckTarget
+ must be the unknown target name, which should be checked.
+
+ @param eSpecialTarget
+ represent the expected target.
+
+ @return It returns <TRUE/> if <var>sCheckTarget</var> represent
+ the expected <var>eSpecialTarget</var> value; <FALSE/> otherwise.
+ */
+ static bool matchSpecialTarget(std::u16string_view sCheckTarget ,
+ ESpecialTarget eSpecialTarget);
+
+ /** @short it checks, if the given name can be used
+ to set it at a frame using XFrame.setName() method.
+
+ @descr Because we handle special targets in a hard coded way
+ (means we do not check the real name of a frame then)
+ such named frames will never be found!
+
+ And in case such special names can exists one times only
+ by definition inside the same frame tree (e.g. _beamer and
+ OFFICE_HELP_TASK) it's not a good idea to allow anything here :-)
+
+ Of course we can't check unknown names, which are not special ones.
+ But we decide, that it's not allowed to use "_" as first sign
+ (because we reserve this letter for our own purposes!)
+ and the value must not a well known special target.
+
+ @param sName
+ the new frame name, which should be checked.
+ */
+ static bool isValidNameForFrame(std::u16string_view sName);
+};
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/pattern/frame.hxx b/framework/source/inc/pattern/frame.hxx
new file mode 100644
index 000000000..947a03668
--- /dev/null
+++ b/framework/source/inc/pattern/frame.hxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+
+// namespaces
+
+namespace framework::pattern::frame{
+
+/** @short close (or dispose) the given resource.
+
+ @descr It try to close the given resource first.
+ Delegating of the ownership can be influenced from
+ outside. If closing isn't possible (because the
+ needed interface isn't available) dispose() is tried instead.
+ All possible exceptions are handled inside.
+ So the user of this method has to look for the return value only.
+
+ @attention The given resource will not be cleared.
+ But later using of it can produce an exception!
+
+ @param xResource
+ the object, which should be closed here.
+
+ @return [bool]
+ sal_True if closing failed.
+ */
+inline bool closeIt(const css::uno::Reference< css::uno::XInterface >& xResource)
+{
+ css::uno::Reference< css::util::XCloseable > xClose (xResource, css::uno::UNO_QUERY);
+ css::uno::Reference< css::lang::XComponent > xDispose(xResource, css::uno::UNO_QUERY);
+
+ try
+ {
+ if (xClose.is())
+ xClose->close(false/*bDelegateOwnership*/);
+ else
+ if (xDispose.is())
+ xDispose->dispose();
+ else
+ return false;
+ }
+ catch(const css::util::CloseVetoException&)
+ { return false; }
+ catch(const css::lang::DisposedException&)
+ {} // disposed is closed is ...
+ catch(const css::uno::RuntimeException&)
+ { throw; } // should not be suppressed!
+ catch(const css::uno::Exception&)
+ { return false; } // ??? We defined to return a boolean value instead of throwing exceptions...
+ // (OK: RuntimeExceptions should not be caught inside the core..)
+
+ return true;
+}
+
+} // namespace framework::pattern::frame
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/inc/pattern/window.hxx b/framework/source/inc/pattern/window.hxx
new file mode 100644
index 000000000..92134915d
--- /dev/null
+++ b/framework/source/inc/pattern/window.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/XTopWindow.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+
+// namespaces
+
+namespace framework{
+
+class WindowHelper
+{
+ public:
+
+
+static bool isTopWindow(const css::uno::Reference< css::awt::XWindow >& xWindow)
+{
+ // even child frame containing top level windows (e.g. query designer of database) will be closed
+ css::uno::Reference< css::awt::XTopWindow > xTopWindowCheck(xWindow, css::uno::UNO_QUERY);
+ if (xTopWindowCheck.is())
+ {
+ // Note: Toolkit interface XTopWindow sometimes is used by real VCL-child-windows also .-)
+ // Be sure that these window is really a "top system window".
+ // Attention ! Checking Window->GetParent() is not the right approach here.
+ // Because sometimes VCL create "implicit border windows" as parents even we created
+ // a simple XWindow using the toolkit only .-(
+ SolarMutexGuard aSolarGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->IsSystemWindow() )
+ return true;
+ }
+
+ return false;
+}
+
+};
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/interaction/quietinteraction.cxx b/framework/source/interaction/quietinteraction.cxx
new file mode 100644
index 000000000..e9dc21857
--- /dev/null
+++ b/framework/source/interaction/quietinteraction.cxx
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <interaction/quietinteraction.hxx>
+
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/document/XInteractionFilterSelect.hpp>
+#include <com/sun/star/document/XInteractionFilterOptions.hpp>
+#include <com/sun/star/document/FilterOptionsRequest.hpp>
+#include <com/sun/star/task/ErrorCodeRequest.hpp>
+
+#include <com/sun/star/document/LockedDocumentRequest.hpp>
+
+#include <vcl/errcode.hxx>
+#include <vcl/svapp.hxx>
+
+namespace framework{
+
+QuietInteraction::QuietInteraction()
+{
+}
+
+void SAL_CALL QuietInteraction::handle( const css::uno::Reference< css::task::XInteractionRequest >& xRequest )
+{
+ // safe the request for outside analyzing every time!
+ css::uno::Any aRequest = xRequest->getRequest();
+ {
+ SolarMutexGuard g;
+ m_aRequest = aRequest;
+ }
+
+ // analyze the request
+ // We need XAbort as possible continuation as minimum!
+ // An optional filter selection we can handle too.
+ css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations = xRequest->getContinuations();
+ css::uno::Reference< css::task::XInteractionAbort > xAbort;
+ css::uno::Reference< css::task::XInteractionApprove > xApprove;
+ css::uno::Reference< css::document::XInteractionFilterSelect > xFilter;
+ css::uno::Reference< css::document::XInteractionFilterOptions > xFOptions;
+
+ sal_Int32 nCount=lContinuations.getLength();
+ for (sal_Int32 i=0; i<nCount; ++i)
+ {
+ if ( ! xAbort.is() )
+ xAbort.set( lContinuations[i], css::uno::UNO_QUERY );
+
+ if( ! xApprove.is() )
+ xApprove.set( lContinuations[i], css::uno::UNO_QUERY );
+
+ if ( ! xFilter.is() )
+ xFilter.set( lContinuations[i], css::uno::UNO_QUERY );
+
+ if ( ! xFOptions.is() )
+ xFOptions.set( lContinuations[i], css::uno::UNO_QUERY );
+ }
+
+ // differ between abortable interactions (error, unknown filter...)
+ // and other ones (ambiguous but not unknown filter...)
+ css::task::ErrorCodeRequest aErrorCodeRequest;
+ css::document::LockedDocumentRequest aLockedDocumentRequest;
+ css::document::FilterOptionsRequest aFilterOptionsRequest;
+
+ if( aRequest >>= aErrorCodeRequest )
+ {
+ // warnings can be ignored => approve
+ // errors must break loading => abort
+ bool bWarning = ErrCode(aErrorCodeRequest.ErrCode).IsWarning();
+ if (xApprove.is() && bWarning)
+ xApprove->select();
+ else
+ if (xAbort.is())
+ xAbort->select();
+ }
+ else
+ if( aRequest >>= aLockedDocumentRequest )
+ {
+ // the locked document should be opened readonly by default
+ if (xApprove.is())
+ xApprove->select();
+ else
+ if (xAbort.is())
+ xAbort->select();
+ }
+ else
+ if (aRequest>>=aFilterOptionsRequest)
+ {
+ if (xFOptions.is())
+ {
+ // let the default filter options be used
+ xFOptions->select();
+ }
+ }
+ else
+ if (xAbort.is())
+ xAbort->select();
+}
+
+css::uno::Any QuietInteraction::getRequest() const
+{
+ SolarMutexGuard g;
+ return m_aRequest;
+}
+
+bool QuietInteraction::wasUsed() const
+{
+ SolarMutexGuard g;
+ return m_aRequest.hasValue();
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/jobs/helponstartup.cxx b/framework/source/jobs/helponstartup.cxx
new file mode 100644
index 000000000..2795a3f45
--- /dev/null
+++ b/framework/source/jobs/helponstartup.cxx
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// include own header
+#include <jobs/helponstartup.hxx>
+#include <services.h>
+#include <targets.h>
+
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Setup.hxx>
+
+// include others
+#include <comphelper/sequenceashashmap.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/help.hxx>
+
+// include interfaces
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XFramesSupplier.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+namespace framework{
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL HelpOnStartup::getImplementationName()
+{
+ return "com.sun.star.comp.framework.HelpOnStartup";
+}
+
+sal_Bool SAL_CALL HelpOnStartup::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL HelpOnStartup::getSupportedServiceNames()
+{
+ return { SERVICENAME_JOB };
+}
+
+HelpOnStartup::HelpOnStartup(css::uno::Reference< css::uno::XComponentContext > xContext)
+ : m_xContext (std::move(xContext))
+{
+ // create some needed uno services and cache it
+ m_xModuleManager = css::frame::ModuleManager::create( m_xContext );
+
+ m_xDesktop = css::frame::Desktop::create(m_xContext);
+
+ // ask for office locale
+ m_sLocale = officecfg::Setup::L10N::ooLocale::get();
+
+ // detect system
+ m_sSystem = officecfg::Office::Common::Help::System::get();
+
+ // Start listening for disposing events of these services,
+ // so we can react e.g. for an office shutdown
+ css::uno::Reference< css::lang::XComponent > xComponent;
+ xComponent.set(m_xModuleManager, css::uno::UNO_QUERY);
+ if (xComponent.is())
+ xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this));
+ if (m_xDesktop.is())
+ m_xDesktop->addEventListener(static_cast< css::lang::XEventListener* >(this));
+ xComponent.set(m_xConfig, css::uno::UNO_QUERY);
+ if (xComponent.is())
+ xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this));
+}
+
+HelpOnStartup::~HelpOnStartup()
+{
+}
+
+// css.task.XJob
+css::uno::Any SAL_CALL HelpOnStartup::execute(const css::uno::Sequence< css::beans::NamedValue >& lArguments)
+{
+ // Analyze the given arguments; try to locate a model there and
+ // classify it's used application module.
+ OUString sModule = its_getModuleIdFromEnv(lArguments);
+
+ // Attention: we are bound to events for opening any document inside the office.
+ // That includes e.g. the help module itself. But we have to do nothing then!
+ if (sModule.isEmpty())
+ return css::uno::Any();
+
+ // check current state of the help module
+ // a) help isn't open => show default page for the detected module
+ // b) help shows any other default page(!) => show default page for the detected module
+ // c) help shows any other content => do nothing (user travelled to any other content and leaved the set of default pages)
+ OUString sCurrentHelpURL = its_getCurrentHelpURL();
+ bool bCurrentHelpURLIsAnyDefaultURL = its_isHelpUrlADefaultOne(sCurrentHelpURL);
+ bool bShowIt = false;
+
+ // a)
+ if (sCurrentHelpURL.isEmpty())
+ bShowIt = true;
+ // b)
+ else if (bCurrentHelpURLIsAnyDefaultURL)
+ bShowIt = true;
+
+ if (bShowIt)
+ {
+ // retrieve the help URL for the detected application module
+ OUString sModuleDependentHelpURL = its_checkIfHelpEnabledAndGetURL(sModule);
+ if (!sModuleDependentHelpURL.isEmpty())
+ {
+ // Show this help page.
+ // Note: The help window brings itself to front ...
+ Help* pHelp = Application::GetHelp();
+ if (pHelp)
+ pHelp->Start(sModuleDependentHelpURL);
+ }
+ }
+
+ return css::uno::Any();
+}
+
+void SAL_CALL HelpOnStartup::disposing(const css::lang::EventObject& aEvent)
+{
+ std::unique_lock g(m_mutex);
+ if (aEvent.Source == m_xModuleManager)
+ m_xModuleManager.clear();
+ else if (aEvent.Source == m_xDesktop)
+ m_xDesktop.clear();
+ else if (aEvent.Source == m_xConfig)
+ m_xConfig.clear();
+}
+
+OUString HelpOnStartup::its_getModuleIdFromEnv(const css::uno::Sequence< css::beans::NamedValue >& lArguments)
+{
+ ::comphelper::SequenceAsHashMap lArgs (lArguments);
+ ::comphelper::SequenceAsHashMap lEnvironment = lArgs.getUnpackedValueOrDefault("Environment", css::uno::Sequence< css::beans::NamedValue >());
+
+ // check for right environment.
+ // If it's not a DocumentEvent, which triggered this job,
+ // we can't work correctly! => return immediately and do nothing
+ OUString sEnvType = lEnvironment.getUnpackedValueOrDefault("EnvType", OUString());
+ if (sEnvType != "DOCUMENTEVENT")
+ return OUString();
+
+ css::uno::Reference< css::frame::XModel > xDoc = lEnvironment.getUnpackedValueOrDefault("Model", css::uno::Reference< css::frame::XModel >());
+ if (!xDoc.is())
+ return OUString();
+
+ // be sure that we work on top level documents only, which are registered
+ // on the desktop instance. Ignore e.g. life previews, which are top frames too ...
+ // but not registered at this global desktop instance.
+ css::uno::Reference< css::frame::XDesktop > xDesktopCheck;
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ css::uno::Reference< css::frame::XController > xController = xDoc->getCurrentController();
+ if (xController.is())
+ xFrame = xController->getFrame();
+ if (xFrame.is() && xFrame->isTop())
+ xDesktopCheck.set(xFrame->getCreator(), css::uno::UNO_QUERY);
+ if (!xDesktopCheck.is())
+ return OUString();
+
+ // OK - now we are sure this document is a top level document.
+ // Classify it.
+ // SAFE ->
+ std::unique_lock aLock(m_mutex);
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager = m_xModuleManager;
+ aLock.unlock();
+ // <- SAFE
+
+ OUString sModuleId;
+ try
+ {
+ sModuleId = xModuleManager->identify(xDoc);
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { sModuleId.clear(); }
+
+ return sModuleId;
+}
+
+OUString HelpOnStartup::its_getCurrentHelpURL()
+{
+ // SAFE ->
+ std::unique_lock aLock(m_mutex);
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = m_xDesktop;
+ aLock.unlock();
+ // <- SAFE
+
+ if (!xDesktop.is())
+ return OUString();
+
+ css::uno::Reference< css::frame::XFrame > xHelp = xDesktop->findFrame(SPECIALTARGET_HELPTASK, css::frame::FrameSearchFlag::CHILDREN);
+ if (!xHelp.is())
+ return OUString();
+
+ OUString sCurrentHelpURL;
+ try
+ {
+ css::uno::Reference< css::frame::XFramesSupplier > xHelpRoot (xHelp , css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::container::XIndexAccess > xHelpChildren(xHelpRoot->getFrames(), css::uno::UNO_QUERY_THROW);
+
+ css::uno::Reference< css::frame::XFrame > xHelpChild;
+ css::uno::Reference< css::frame::XController > xHelpView;
+ css::uno::Reference< css::frame::XModel > xHelpContent;
+
+ xHelpChildren->getByIndex(0) >>= xHelpChild;
+ if (xHelpChild.is())
+ xHelpView = xHelpChild->getController();
+ if (xHelpView.is())
+ xHelpContent = xHelpView->getModel();
+ if (xHelpContent.is())
+ sCurrentHelpURL = xHelpContent->getURL();
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { sCurrentHelpURL.clear(); }
+
+ return sCurrentHelpURL;
+}
+
+bool HelpOnStartup::its_isHelpUrlADefaultOne(std::u16string_view sHelpURL)
+{
+ if (sHelpURL.empty())
+ return false;
+
+ // SAFE ->
+ std::unique_lock aLock(m_mutex);
+ css::uno::Reference< css::container::XNameAccess > xConfig = m_xConfig;
+ OUString sLocale = m_sLocale;
+ OUString sSystem = m_sSystem;
+ aLock.unlock();
+ // <- SAFE
+
+ if (!xConfig.is())
+ return false;
+
+ // check given help url against all default ones
+ const css::uno::Sequence< OUString > lModules = xConfig->getElementNames();
+ const OUString* pModules = lModules.getConstArray();
+ ::sal_Int32 c = lModules.getLength();
+ ::sal_Int32 i = 0;
+
+ for (i=0; i<c; ++i)
+ {
+ try
+ {
+ css::uno::Reference< css::container::XNameAccess > xModuleConfig;
+ xConfig->getByName(pModules[i]) >>= xModuleConfig;
+ if (!xModuleConfig.is())
+ continue;
+
+ OUString sHelpBaseURL;
+ xModuleConfig->getByName("ooSetupFactoryHelpBaseURL") >>= sHelpBaseURL;
+ OUString sHelpURLForModule = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem);
+ if (sHelpURL == sHelpURLForModule)
+ return true;
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ {}
+ }
+
+ return false;
+}
+
+OUString HelpOnStartup::its_checkIfHelpEnabledAndGetURL(const OUString& sModule)
+{
+ // SAFE ->
+ std::unique_lock aLock(m_mutex);
+ css::uno::Reference< css::container::XNameAccess > xConfig = m_xConfig;
+ OUString sLocale = m_sLocale;
+ OUString sSystem = m_sSystem;
+ aLock.unlock();
+ // <- SAFE
+
+ OUString sHelpURL;
+
+ try
+ {
+ css::uno::Reference< css::container::XNameAccess > xModuleConfig;
+ if (xConfig.is())
+ xConfig->getByName(sModule) >>= xModuleConfig;
+
+ bool bHelpEnabled = false;
+ if (xModuleConfig.is())
+ xModuleConfig->getByName("ooSetupFactoryHelpOnOpen") >>= bHelpEnabled;
+
+ if (bHelpEnabled)
+ {
+ OUString sHelpBaseURL;
+ xModuleConfig->getByName("ooSetupFactoryHelpBaseURL") >>= sHelpBaseURL;
+ sHelpURL = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem);
+ }
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { sHelpURL.clear(); }
+
+ return sHelpURL;
+}
+
+OUString HelpOnStartup::ist_createHelpURL(std::u16string_view sBaseURL,
+ std::u16string_view sLocale ,
+ std::u16string_view sSystem )
+{
+ return OUString::Concat(sBaseURL) + "?Language=" + sLocale + "&System=" + sSystem;
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_HelpOnStartup_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::HelpOnStartup(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/jobs/job.cxx b/framework/source/jobs/job.cxx
new file mode 100644
index 000000000..726c037cf
--- /dev/null
+++ b/framework/source/jobs/job.cxx
@@ -0,0 +1,857 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <jobs/job.hxx>
+#include <jobs/jobresult.hxx>
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <com/sun/star/task/XJob.hpp>
+#include <com/sun/star/task/XAsyncJob.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/util/XCloseBroadcaster.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+namespace framework{
+
+/**
+ @short standard ctor
+ @descr It initialize this new instance. But it set some generic parameters here only.
+ Specialized information (e.g. the alias or service name ofthis job) will be set
+ later using the method setJobData().
+
+ @param xContext
+ reference to the uno service manager
+
+ @param xFrame
+ reference to the frame, in which environment we run
+ (May be null!)
+*/
+Job::Job( /*IN*/ const css::uno::Reference< css::uno::XComponentContext >& xContext ,
+ /*IN*/ css::uno::Reference< css::frame::XFrame > xFrame )
+ : m_aJobCfg (xContext )
+ , m_xContext (xContext )
+ , m_xFrame (std::move(xFrame ))
+ , m_bListenOnDesktop (false )
+ , m_bListenOnFrame (false )
+ , m_bListenOnModel (false )
+ , m_bPendingCloseFrame (false )
+ , m_bPendingCloseModel (false )
+ , m_eRunState (E_NEW )
+{
+}
+
+/**
+ @short standard ctor
+ @descr It initialize this new instance. But it set some generic parameters here only.
+ Specialized information (e.g. the alias or service name ofthis job) will be set
+ later using the method setJobData().
+
+ @param xContext
+ reference to the uno service manager
+
+ @param xModel
+ reference to the model, in which environment we run
+ (May be null!)
+*/
+Job::Job( /*IN*/ const css::uno::Reference< css::uno::XComponentContext >& xContext ,
+ /*IN*/ css::uno::Reference< css::frame::XModel > xModel )
+ : m_aJobCfg (xContext )
+ , m_xContext (xContext )
+ , m_xModel (std::move(xModel ))
+ , m_bListenOnDesktop (false )
+ , m_bListenOnFrame (false )
+ , m_bListenOnModel (false )
+ , m_bPendingCloseFrame (false )
+ , m_bPendingCloseModel (false )
+ , m_eRunState (E_NEW )
+{
+}
+
+/**
+ @short superfluous!
+ @descr Releasing of memory and reference must be done inside die() call.
+ Otherwise it's a bug.
+*/
+Job::~Job()
+{
+}
+
+/**
+ @short set (or delete) a listener for sending dispatch result events
+ @descr Because this object is used in a wrapped mode ... the original listener
+ for such events can't be registered here directly. Because the
+ listener expect to get the original object given as source of the event.
+ That's why we get this source here too, to fake(!) it at sending time!
+
+ @param xListener
+ the original listener for dispatch result events
+
+ @param xSourceFake
+ our user, which got the registration request for this listener
+*/
+void Job::setDispatchResultFake( /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ,
+ /*IN*/ const css::uno::Reference< css::uno::XInterface >& xSourceFake )
+{
+ SolarMutexGuard g;
+
+ // reject dangerous calls
+ if (m_eRunState != E_NEW)
+ {
+ SAL_INFO("fwk", "Job::setJobData(): job may still running or already finished");
+ return;
+ }
+
+ m_xResultListener = xListener;
+ m_xResultSourceFake = xSourceFake;
+}
+
+void Job::setJobData( const JobData& aData )
+{
+ SolarMutexGuard g;
+
+ // reject dangerous calls
+ if (m_eRunState != E_NEW)
+ {
+ SAL_INFO("fwk", "Job::setJobData(): job may still running or already finished");
+ return;
+ }
+
+ m_aJobCfg = aData;
+}
+
+/**
+ @short runs the job
+ @descr It doesn't matter, if the job is an asynchronous or
+ synchronous one. This method returns only if it was finished
+ or cancelled.
+
+ @param lDynamicArgs
+ optional arguments for job execution
+ In case the represented job is a configured one (which uses static
+ arguments too) all information will be merged!
+*/
+void Job::execute( /*IN*/ const css::uno::Sequence< css::beans::NamedValue >& lDynamicArgs )
+{
+ /* SAFE { */
+ class SolarMutexAntiGuard {
+ SolarMutexResettableGuard & m_rGuard;
+ public:
+ SolarMutexAntiGuard(SolarMutexResettableGuard & rGuard) : m_rGuard(rGuard)
+ {
+ m_rGuard.clear();
+ }
+ ~SolarMutexAntiGuard()
+ {
+ m_rGuard.reset();
+ }
+ };
+ SolarMutexResettableGuard aWriteLock;
+
+ // reject dangerous calls
+ if (m_eRunState != E_NEW)
+ {
+ SAL_INFO("fwk", "Job::execute(): job may still running or already finished");
+ return;
+ }
+
+ // create the environment and mark this job as running ...
+ m_eRunState = E_RUNNING;
+ impl_startListening();
+
+ css::uno::Reference< css::task::XAsyncJob > xAJob;
+ css::uno::Reference< css::task::XJob > xSJob;
+ css::uno::Sequence< css::beans::NamedValue > lJobArgs = impl_generateJobArgs(lDynamicArgs);
+
+ // It's necessary to hold us self alive!
+ // Otherwise we might die by ref count ...
+ css::uno::Reference< css::task::XJobListener > xThis(this);
+
+ try
+ {
+ // create the job
+ // We must check for the supported interface on demand!
+ // But we prefer the synchronous one ...
+ m_xJob = m_xContext->getServiceManager()->createInstanceWithContext(m_aJobCfg.getService(), m_xContext);
+ xSJob.set(m_xJob, css::uno::UNO_QUERY);
+ if (!xSJob.is())
+ xAJob.set(m_xJob, css::uno::UNO_QUERY);
+
+ // execute it asynchronous
+ if (xAJob.is())
+ {
+ m_aAsyncWait.reset();
+ SolarMutexAntiGuard const ag(aWriteLock);
+ /* } SAFE */
+ xAJob->executeAsync(lJobArgs, xThis);
+ // wait for finishing this job - so this method
+ // does the same for synchronous and asynchronous jobs!
+ m_aAsyncWait.wait();
+ /* SAFE { */
+ // Note: Result handling was already done inside the callback!
+ }
+ // execute it synchron
+ else if (xSJob.is())
+ {
+ css::uno::Any aResult;
+ {
+ SolarMutexAntiGuard const ag(aWriteLock);
+ /* } SAFE */
+ aResult = xSJob->execute(lJobArgs);
+ }
+ /* SAFE { */
+ impl_reactForJobResult(aResult);
+ }
+ }
+ #if OSL_DEBUG_LEVEL > 0
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_INFO_EXCEPTION("fwk", "Job::execute(): Got exception during job execution");
+ }
+ #else
+ catch(const css::uno::Exception&)
+ {}
+ #endif
+
+ // deinitialize the environment and mark this job as finished...
+ // but don't overwrite any information about STOPPED or might DISPOSED jobs!
+ impl_stopListening();
+ if (m_eRunState == E_RUNNING)
+ m_eRunState = E_STOPPED_OR_FINISHED;
+
+ // If we got a close request from our frame or model...
+ // but we disagreed with that by throwing a veto exception...
+ // and got the ownership...
+ // we have to close the resource frame or model now -
+ // and to disable ourself!
+ if (m_bPendingCloseFrame)
+ {
+ m_bPendingCloseFrame = false;
+ css::uno::Reference< css::util::XCloseable > xClose(m_xFrame, css::uno::UNO_QUERY);
+ if (xClose.is())
+ {
+ try
+ {
+ xClose->close(true);
+ }
+ catch(const css::util::CloseVetoException&) {}
+ }
+ }
+
+ if (m_bPendingCloseModel)
+ {
+ m_bPendingCloseModel = false;
+ css::uno::Reference< css::util::XCloseable > xClose(m_xModel, css::uno::UNO_QUERY);
+ if (xClose.is())
+ {
+ try
+ {
+ xClose->close(true);
+ }
+ catch(const css::util::CloseVetoException&) {}
+ }
+ }
+
+ aWriteLock.clear();
+ /* SAFE { */
+
+ // release this instance ...
+ die();
+}
+
+/**
+ @short kill this job
+ @descr It doesn't matter if this request is called from inside or
+ from outside. We release our internal structures and stop
+ every activity. After doing so - this instance will not be
+ usable any longer! Of course we try to handle further requests
+ carefully. Maybe someone else holds a reference to us ...
+*/
+void Job::die()
+{
+ SolarMutexGuard g;
+
+ impl_stopListening();
+
+ if (m_eRunState != E_DISPOSED)
+ {
+ try
+ {
+ css::uno::Reference< css::lang::XComponent > xDispose(m_xJob, css::uno::UNO_QUERY);
+ if (xDispose.is())
+ {
+ xDispose->dispose();
+ m_eRunState = E_DISPOSED;
+ }
+ }
+ catch(const css::lang::DisposedException&)
+ {
+ m_eRunState = E_DISPOSED;
+ }
+ }
+
+ m_xJob.clear();
+ m_xFrame.clear();
+ m_xModel.clear();
+ m_xDesktop.clear();
+ m_xResultListener.clear();
+ m_xResultSourceFake.clear();
+ m_bPendingCloseFrame = false;
+ m_bPendingCloseModel = false;
+}
+
+/**
+ @short generates list of arguments for job execute
+ @descr There exist a set of information, which can be needed by a job.
+ a) it's static configuration data (Equals for all jobs. )
+ b) it's specific configuration data (Different for every job.)
+ c) some environment values (e.g. the frame, for which this job was started)
+ d) any other dynamic data (e.g. parameters of a dispatch() request)
+ We collect all this information and generate one list which include all others.
+
+ @param lDynamicArgs
+ list of dynamic arguments (given by a corresponding dispatch() call)
+ Can be empty too.
+
+ @return A list which includes all mentioned sub lists.
+*/
+css::uno::Sequence< css::beans::NamedValue > Job::impl_generateJobArgs( /*IN*/ const css::uno::Sequence< css::beans::NamedValue >& lDynamicArgs )
+{
+ css::uno::Sequence< css::beans::NamedValue > lAllArgs;
+
+ /* SAFE { */
+ SolarMutexClearableGuard aReadLock;
+
+ // the real structure of the returned list depends from the environment of this job!
+ JobData::EMode eMode = m_aJobCfg.getMode();
+
+ // Create list of environment variables. This list must be part of the
+ // returned structure every time... but some of its members are optional!
+ sal_Int32 nLen = 1;
+ if (m_xFrame.is())
+ ++nLen;
+ if (m_xModel.is())
+ ++nLen;
+ if (eMode==JobData::E_EVENT)
+ ++nLen;
+ css::uno::Sequence< css::beans::NamedValue > lEnvArgs(nLen);
+ auto plEnvArgs = lEnvArgs.getArray();
+ plEnvArgs[0].Name = "EnvType";
+ plEnvArgs[0].Value <<= m_aJobCfg.getEnvironmentDescriptor();
+
+ sal_Int32 i = 0;
+ if (m_xFrame.is())
+ {
+ ++i;
+ plEnvArgs[i].Name = "Frame";
+ plEnvArgs[i].Value <<= m_xFrame;
+ }
+ if (m_xModel.is())
+ {
+ ++i;
+ plEnvArgs[i].Name = "Model";
+ plEnvArgs[i].Value <<= m_xModel;
+ }
+ if (eMode==JobData::E_EVENT)
+ {
+ ++i;
+ plEnvArgs[i].Name = "EventName";
+ plEnvArgs[i].Value <<= m_aJobCfg.getEvent();
+ }
+
+ // get the configuration data from the job data container ... if possible
+ // Means: if this job has any configuration data. Note: only really
+ // filled lists will be set to the return structure at the end of this method.
+ css::uno::Sequence< css::beans::NamedValue > lConfigArgs;
+ std::vector< css::beans::NamedValue > lJobConfigArgs;
+ if (eMode==JobData::E_ALIAS || eMode==JobData::E_EVENT)
+ {
+ lConfigArgs = m_aJobCfg.getConfig();
+ lJobConfigArgs = m_aJobCfg.getJobConfig();
+ }
+
+ aReadLock.clear();
+ /* } SAFE */
+
+ // Add all valid (not empty) lists to the return list
+ if (lConfigArgs.hasElements())
+ {
+ sal_Int32 nLength = lAllArgs.getLength();
+ lAllArgs.realloc(nLength+1);
+ auto plAllArgs = lAllArgs.getArray();
+ plAllArgs[nLength].Name = "Config";
+ plAllArgs[nLength].Value <<= lConfigArgs;
+ }
+ if (!lJobConfigArgs.empty())
+ {
+ sal_Int32 nLength = lAllArgs.getLength();
+ lAllArgs.realloc(nLength+1);
+ auto plAllArgs = lAllArgs.getArray();
+ plAllArgs[nLength].Name = "JobConfig";
+ plAllArgs[nLength].Value <<= comphelper::containerToSequence(lJobConfigArgs);
+ }
+ if (lEnvArgs.hasElements())
+ {
+ sal_Int32 nLength = lAllArgs.getLength();
+ lAllArgs.realloc(nLength+1);
+ auto plAllArgs = lAllArgs.getArray();
+ plAllArgs[nLength].Name = "Environment";
+ plAllArgs[nLength].Value <<= lEnvArgs;
+ }
+ if (lDynamicArgs.hasElements())
+ {
+ sal_Int32 nLength = lAllArgs.getLength();
+ lAllArgs.realloc(nLength+1);
+ auto plAllArgs = lAllArgs.getArray();
+ plAllArgs[nLength].Name = "DynamicData";
+ plAllArgs[nLength].Value <<= lDynamicArgs;
+ }
+
+ return lAllArgs;
+}
+
+/**
+ @short analyze the given job result and change the job configuration
+ @descr Note: Some results can be handled only, if this job has a valid configuration!
+ For "not configured jobs" (means pure services) they can be ignored.
+ But these cases are handled by our JobData member. We can call it every time.
+ It does the right things automatically. E.g. if the job has no configuration ...
+ it does nothing during setJobConfig()!
+
+ @param aResult
+ the job result for analyzing
+*/
+void Job::impl_reactForJobResult( /*IN*/ const css::uno::Any& aResult )
+{
+ SolarMutexGuard g;
+
+ // analyze the result set ...
+ JobResult aAnalyzedResult(aResult);
+
+ // some of the following operations will be supported for different environments
+ // or different type of jobs only.
+ JobData::EEnvironment eEnvironment = m_aJobCfg.getEnvironment();
+
+ // write back the job specific configuration data ...
+ // If the environment allow it and if this job has a configuration!
+ if (
+ (m_aJobCfg.hasConfig() ) &&
+ (aAnalyzedResult.existPart(JobResult::E_ARGUMENTS))
+ )
+ {
+ m_aJobCfg.setJobConfig(aAnalyzedResult.getArguments());
+ }
+
+ // disable a job for further executions.
+ // Note: this option is available inside the environment EXECUTOR only
+ if (
+// (eEnvironment == JobData::E_EXECUTION ) &&
+ (m_aJobCfg.hasConfig() ) &&
+ (aAnalyzedResult.existPart(JobResult::E_DEACTIVATE))
+ )
+ {
+ m_aJobCfg.disableJob();
+ }
+
+ // notify any interested listener with the may given result state.
+ // Note: this option is available inside the environment DISPATCH only
+ if (
+ (eEnvironment == JobData::E_DISPATCH ) &&
+ (m_xResultListener.is() ) &&
+ (aAnalyzedResult.existPart(JobResult::E_DISPATCHRESULT))
+ )
+ {
+ // Attention: Because the listener expect that the original object send this event ...
+ // and we nor the job are the right ones ...
+ // our user has set itself before. So we can fake this source address!
+ css::frame::DispatchResultEvent aEvent = aAnalyzedResult.getDispatchResult();
+ aEvent.Source = m_xResultSourceFake;
+ m_xResultListener->dispatchFinished(aEvent);
+ }
+}
+
+/**
+ @short starts listening for office shutdown and closing of our
+ given target frame (if it's a valid reference)
+ @descr We will register ourself as terminate listener
+ at the global desktop instance. That will hold us
+ alive and additional we get the information, if the
+ office wish to shutdown. If then an internal job
+ is running we will have the chance to suppress that
+ by throwing a veto exception. If our internal wrapped
+ job finished his work, we can release this listener
+ connection.
+
+ Further we are listener for closing of the (possible valid)
+ given frame. We must be sure, that this resource won't be gone
+ if our internal job is still running.
+*/
+void Job::impl_startListening()
+{
+ SolarMutexGuard g;
+
+ // listening for office shutdown
+ if (!m_xDesktop.is() && !m_bListenOnDesktop)
+ {
+ try
+ {
+ m_xDesktop = css::frame::Desktop::create( m_xContext );
+ css::uno::Reference< css::frame::XTerminateListener > xThis(this);
+ m_xDesktop->addTerminateListener(xThis);
+ m_bListenOnDesktop = true;
+ }
+ catch(const css::uno::Exception&)
+ {
+ m_xDesktop.clear();
+ }
+ }
+
+ // listening for frame closing
+ if (m_xFrame.is() && !m_bListenOnFrame)
+ {
+ try
+ {
+ css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xFrame , css::uno::UNO_QUERY);
+ css::uno::Reference< css::util::XCloseListener > xThis(this);
+ if (xCloseable.is())
+ {
+ xCloseable->addCloseListener(xThis);
+ m_bListenOnFrame = true;
+ }
+ }
+ catch(const css::uno::Exception&)
+ {
+ m_bListenOnFrame = false;
+ }
+ }
+
+ // listening for model closing
+ if (!m_xModel.is() || m_bListenOnModel)
+ return;
+
+ try
+ {
+ css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xModel , css::uno::UNO_QUERY);
+ css::uno::Reference< css::util::XCloseListener > xThis(this);
+ if (xCloseable.is())
+ {
+ xCloseable->addCloseListener(xThis);
+ m_bListenOnModel = true;
+ }
+ }
+ catch(const css::uno::Exception&)
+ {
+ m_bListenOnModel = false;
+ }
+}
+
+/**
+ @short release listener connection for office shutdown
+ @descr see description of impl_startListening()
+*/
+void Job::impl_stopListening()
+{
+ SolarMutexGuard g;
+
+ // stop listening for office shutdown
+ if (m_xDesktop.is() && m_bListenOnDesktop)
+ {
+ try
+ {
+ css::uno::Reference< css::frame::XTerminateListener > xThis(this);
+ m_xDesktop->removeTerminateListener(xThis);
+ m_xDesktop.clear();
+ m_bListenOnDesktop = false;
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+ }
+
+ // stop listening for frame closing
+ if (m_xFrame.is() && m_bListenOnFrame)
+ {
+ try
+ {
+ css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xFrame , css::uno::UNO_QUERY);
+ css::uno::Reference< css::util::XCloseListener > xThis(this);
+ if (xCloseable.is())
+ {
+ xCloseable->removeCloseListener(xThis);
+ m_bListenOnFrame = false;
+ }
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+ }
+
+ // stop listening for model closing
+ if (!(m_xModel.is() && m_bListenOnModel))
+ return;
+
+ try
+ {
+ css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xModel , css::uno::UNO_QUERY);
+ css::uno::Reference< css::util::XCloseListener > xThis(this);
+ if (xCloseable.is())
+ {
+ xCloseable->removeCloseListener(xThis);
+ m_bListenOnModel = false;
+ }
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+}
+
+/**
+ @short callback from any asynchronous executed job
+
+ @descr Our execute() method waits for this callback.
+ We have to react for the possible results here,
+ to kill the running job and disable the blocked condition
+ so execute() can be finished too.
+
+ @param xJob
+ the job, which was running and inform us now
+
+ @param aResult
+ its results
+*/
+void SAL_CALL Job::jobFinished( /*IN*/ const css::uno::Reference< css::task::XAsyncJob >& xJob ,
+ /*IN*/ const css::uno::Any& aResult )
+{
+ SolarMutexGuard g;
+
+ // It's necessary to check this.
+ // May this job was cancelled by any other reason
+ // some milliseconds before. :-)
+ if (m_xJob.is() && m_xJob==xJob)
+ {
+ // react for his results
+ // (means enable/disable it for further requests
+ // or save arguments or notify listener ...)
+ impl_reactForJobResult(aResult);
+
+ // Let the job die!
+ m_xJob.clear();
+ }
+
+ // And let the start method "execute()" finishing it's job.
+ // But do it every time. So any outside blocking code can finish
+ // his work too.
+ m_aAsyncWait.set();
+}
+
+/**
+ @short prevent internal wrapped job against office termination
+ @descr This event is broadcasted by the desktop instance and ask for an office termination.
+ If the internal wrapped job is still in progress, we disagree with that by throwing the
+ right veto exception. If not - we agree. But then we must be aware, that another event
+ notifyTermination() can follow. Then we have no chance to do the same. Then we have to
+ accept that and stop our work instandly.
+
+ @param aEvent
+ describes the broadcaster and must be the desktop instance
+
+ @throw TerminateVetoException
+ if our internal wrapped job is still running.
+ */
+void SAL_CALL Job::queryTermination( /*IN*/ const css::lang::EventObject& )
+{
+ SolarMutexGuard g;
+
+ // Otherwise try to close() it
+ css::uno::Reference< css::util::XCloseable > xClose(m_xJob, css::uno::UNO_QUERY);
+ if (xClose.is())
+ {
+ try
+ {
+ xClose->close(false);
+ m_eRunState = E_STOPPED_OR_FINISHED;
+ }
+ catch(const css::util::CloseVetoException&) {}
+ }
+
+ if (m_eRunState != E_STOPPED_OR_FINISHED)
+ {
+ css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
+ throw css::frame::TerminationVetoException("job still in progress", xThis);
+ }
+}
+
+/**
+ @short inform us about office termination
+ @descr Instead of the method queryTermination(), here is no chance to disagree with that.
+ We have to accept it and cancel all current processes inside.
+ It can occur only, if job was not already started if queryTermination() was called here.
+ Then we had not thrown a veto exception. But now we must agree with this situation and break
+ all our internal processes. It's not a good idea to mark this instance as non startable any longer
+ inside queryTermination() if no job was running too. Because that would disable this job and may
+ the office does not really shutdown, because another listener has thrown the suitable exception.
+
+ @param aEvent
+ describes the broadcaster and must be the desktop instance
+ */
+void SAL_CALL Job::notifyTermination( /*IN*/ const css::lang::EventObject& )
+{
+ die();
+ // Do nothing else here. Our internal resources was released ...
+}
+
+/**
+ @short prevent internal wrapped job against frame closing
+ @descr This event is broadcasted by the frame instance and ask for closing.
+ If the internal wrapped job is still in progress, we disagree with that by throwing the
+ right veto exception. If not - we agree. But then we must be aware, that another event
+ notifyClosing() can follow. Then we have no chance to do the same. Then we have to
+ accept that and stop our work instandly.
+
+ @param aEvent
+ describes the broadcaster and must be the frame instance
+
+ @param bGetsOwnership
+ If it's set to <sal_True> and we throw the right veto exception, we have to close this frame later
+ if our internal processes will be finished. If it's set to <FALSE/> we can ignore it.
+
+ @throw CloseVetoException
+ if our internal wrapped job is still running.
+ */
+void SAL_CALL Job::queryClosing( const css::lang::EventObject& aEvent ,
+ sal_Bool bGetsOwnership )
+{
+ SolarMutexGuard g;
+
+ // do nothing, if no internal job is still running ...
+ // The frame or model can be closed then successfully.
+ if (m_eRunState != E_RUNNING)
+ return;
+
+ // try close() first at the job.
+ // The job can agree or disagree with this request.
+ css::uno::Reference< css::util::XCloseable > xClose(m_xJob, css::uno::UNO_QUERY);
+ if (xClose.is())
+ {
+ xClose->close(bGetsOwnership);
+ // Here we can say: "this job was stopped successfully". Because
+ // no veto exception was thrown!
+ m_eRunState = E_STOPPED_OR_FINISHED;
+ return;
+ }
+
+ // try dispose() then
+ // Here the job has no chance for a veto.
+ // But we must be aware of an "already disposed exception"...
+ try
+ {
+ css::uno::Reference< css::lang::XComponent > xDispose(m_xJob, css::uno::UNO_QUERY);
+ if (xDispose.is())
+ {
+ xDispose->dispose();
+ m_eRunState = E_DISPOSED;
+ }
+ }
+ catch(const css::lang::DisposedException&)
+ {
+ // the job was already disposed by any other mechanism !?
+ // But it's not interesting for us. For us this job is stopped now.
+ m_eRunState = E_DISPOSED;
+ }
+
+ if (m_eRunState != E_DISPOSED)
+ {
+ // analyze event source - to find out, which resource called queryClosing() at this
+ // job wrapper. We must bind a "pending close" request to this resource.
+ // Closing of the corresponding resource will be done if our internal job finish it's work.
+ m_bPendingCloseFrame = (m_xFrame.is() && aEvent.Source == m_xFrame);
+ m_bPendingCloseModel = (m_xModel.is() && aEvent.Source == m_xModel);
+
+ // throw suitable veto exception - because the internal job could not be cancelled.
+ css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
+ throw css::util::CloseVetoException("job still in progress", xThis);
+ }
+
+ // No veto ...
+ // But don't call die() here or free our internal member.
+ // This must be done inside notifyClosing() only. Otherwise the
+ // might stopped job has no chance to return its results or
+ // call us back. We must give him the chance to finish it's work successfully.
+}
+
+/**
+ @short inform us about frame closing
+ @descr Instead of the method queryClosing(), here is no chance to disagree with that.
+ We have to accept it and cancel all current processes inside.
+
+ @param aEvent
+ describes the broadcaster and must be the frame or model instance we know
+ */
+void SAL_CALL Job::notifyClosing( const css::lang::EventObject& )
+{
+ die();
+ // Do nothing else here. Our internal resources was released ...
+}
+
+/**
+ @short shouldn't be called normally
+ @descr But it doesn't matter, who called it. We have to kill our internal
+ running processes hardly.
+
+ @param aEvent
+ describe the broadcaster
+*/
+void SAL_CALL Job::disposing( const css::lang::EventObject& aEvent )
+{
+ /* SAFE { */
+ {
+ SolarMutexGuard aWriteLock;
+
+ if (m_xDesktop.is() && aEvent.Source == m_xDesktop)
+ {
+ m_xDesktop.clear();
+ m_bListenOnDesktop = false;
+ }
+ else if (m_xFrame.is() && aEvent.Source == m_xFrame)
+ {
+ m_xFrame.clear();
+ m_bListenOnFrame = false;
+ }
+ else if (m_xModel.is() && aEvent.Source == m_xModel)
+ {
+ m_xModel.clear();
+ m_bListenOnModel = false;
+ }
+ }
+ /* } SAFE */
+
+ die();
+ // Do nothing else here. Our internal resources was released ...
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/jobs/jobdata.cxx b/framework/source/jobs/jobdata.cxx
new file mode 100644
index 000000000..863bc8c4b
--- /dev/null
+++ b/framework/source/jobs/jobdata.cxx
@@ -0,0 +1,563 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <jobs/configaccess.hxx>
+#include <jobs/jobdata.hxx>
+#include <classes/converter.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XMultiHierarchicalPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+
+#include <tools/wldcrd.hxx>
+#include <unotools/configpaths.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+namespace framework{
+
+/**
+ @short standard ctor
+ @descr It initialize this new instance.
+ But for real working it's necessary to call setAlias() or setService() later.
+ Because we need the job data ...
+
+ @param rxContext
+ reference to the uno service manager
+*/
+JobData::JobData( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : m_xContext (std::move(xContext ))
+{
+ // share code for member initialization with defaults!
+ impl_reset();
+}
+
+/**
+ @short copy ctor
+ @descr Sometimes such job data container must be moved from one using place
+ to another one. Then a copy ctor and copy operator must be available.
+
+ @param rCopy
+ the original instance, from which we must copy all data
+*/
+JobData::JobData( const JobData& rCopy )
+{
+ // use the copy operator to share the same code
+ *this = rCopy;
+}
+
+/**
+ @short operator for copying JobData instances
+ @descr Sometimes such job data container must be moved from one using place
+ to another one. Then a copy ctor and copy operator must be available.
+
+ @param rCopy
+ the original instance, from which we must copy all data
+*/
+JobData& JobData::operator=( const JobData& rCopy )
+{
+ SolarMutexGuard g;
+ // Please don't copy the uno service manager reference.
+ // That can change the uno context, which isn't a good idea!
+ m_eMode = rCopy.m_eMode;
+ m_eEnvironment = rCopy.m_eEnvironment;
+ m_sAlias = rCopy.m_sAlias;
+ m_sService = rCopy.m_sService;
+ m_sContext = rCopy.m_sContext;
+ m_sEvent = rCopy.m_sEvent;
+ m_lArguments = rCopy.m_lArguments;
+ return *this;
+}
+
+/**
+ @short let this instance die
+ @descr There is no chance any longer to work. We have to
+ release all used resources and free used memory.
+*/
+JobData::~JobData()
+{
+ impl_reset();
+}
+
+/**
+ @short initialize this instance as a job with configuration
+ @descr They given alias can be used to address some configuration data.
+ We read it and fill our internal structures. Of course old information
+ will be lost doing so.
+
+ @param sAlias
+ the alias name of this job, used to locate job properties inside cfg
+*/
+void JobData::setAlias( const OUString& sAlias )
+{
+ SolarMutexGuard g;
+ // delete all old information! Otherwise we mix it with the new one ...
+ impl_reset();
+
+ // take over the new information
+ m_sAlias = sAlias;
+ m_eMode = E_ALIAS;
+
+ // try to open the configuration set of this job directly and get a property access to it
+ // We open it readonly here
+ ConfigAccess aConfig(
+ m_xContext,
+ ("/org.openoffice.Office.Jobs/Jobs/"
+ + utl::wrapConfigurationElementName(m_sAlias)));
+ aConfig.open(ConfigAccess::E_READONLY);
+ if (aConfig.getMode()==ConfigAccess::E_CLOSED)
+ {
+ impl_reset();
+ return;
+ }
+
+ css::uno::Reference< css::beans::XPropertySet > xJobProperties(aConfig.cfg(), css::uno::UNO_QUERY);
+ if (xJobProperties.is())
+ {
+ css::uno::Any aValue;
+
+ // read uno implementation name
+ aValue = xJobProperties->getPropertyValue("Service");
+ aValue >>= m_sService;
+
+ // read module context list
+ aValue = xJobProperties->getPropertyValue("Context");
+ aValue >>= m_sContext;
+
+ // read whole argument list
+ aValue = xJobProperties->getPropertyValue("Arguments");
+ css::uno::Reference< css::container::XNameAccess > xArgumentList;
+ if (
+ (aValue >>= xArgumentList) &&
+ (xArgumentList.is() )
+ )
+ {
+ css::uno::Sequence< OUString > lArgumentNames = xArgumentList->getElementNames();
+ sal_Int32 nCount = lArgumentNames.getLength();
+ m_lArguments.resize(nCount);
+ for (sal_Int32 i=0; i<nCount; ++i)
+ {
+ m_lArguments[i].Name = lArgumentNames[i];
+ m_lArguments[i].Value = xArgumentList->getByName(m_lArguments[i].Name);
+ }
+ }
+ }
+
+ aConfig.close();
+}
+
+/**
+ @short initialize this instance as a job without configuration
+ @descr This job has no configuration data. We have to forget all old information
+ and set only some of them new, so this instance can work.
+
+ @param sService
+ the uno service name of this "non configured" job
+*/
+void JobData::setService( const OUString& sService )
+{
+ SolarMutexGuard g;
+ // delete all old information! Otherwise we mix it with the new one ...
+ impl_reset();
+ // take over the new information
+ m_sService = sService;
+ m_eMode = E_SERVICE;
+}
+
+/**
+ @short initialize this instance with new job values.
+ @descr It reads automatically all properties of the specified
+ job (using it's alias name) and "register it" for the
+ given event. This registration will not be validated against
+ the underlying configuration! (That must be done from outside.
+ Because the caller must have the configuration already open to
+ get the values for sEvent and sAlias! And doing so it can perform
+ only, if the time stamp values are read outside too.
+ Further it makes no sense to initialize and start a disabled job.
+ So this initialization method will be called for enabled jobs only.)
+
+ @param sEvent
+ the triggered event, for which this job should be started
+
+ @param sAlias
+ mark the required job inside event registration list
+*/
+void JobData::setEvent( const OUString& sEvent ,
+ const OUString& sAlias )
+{
+ // share code to read all job properties!
+ setAlias(sAlias);
+
+ SolarMutexGuard g;
+ // take over the new information - which differ against set one of method setAlias()!
+ m_sEvent = sEvent;
+ m_eMode = E_EVENT;
+}
+
+/**
+ @short set the new job specific arguments
+ @descr If a job finish his work, it can give us a new list of arguments (which
+ will not interpreted by us). We write it back to the configuration only
+ (if this job has its own configuration!).
+ So a job can have persistent data without implementing anything
+ or define own config areas for that.
+
+ @param lArguments
+ list of arguments, which should be set for this job
+ */
+void JobData::setJobConfig( std::vector< css::beans::NamedValue >&& lArguments )
+{
+ SolarMutexGuard g;
+
+ // update member
+ m_lArguments = std::move(lArguments);
+
+ // update the configuration ... if possible!
+ if (m_eMode!=E_ALIAS)
+ return;
+
+ // It doesn't matter if this config object was already opened before.
+ // It doesn nothing here then ... or it change the mode automatically, if
+ // it was opened using another one before.
+ ConfigAccess aConfig(
+ m_xContext,
+ ("/org.openoffice.Office.Jobs/Jobs/"
+ + utl::wrapConfigurationElementName(m_sAlias)));
+ aConfig.open(ConfigAccess::E_READWRITE);
+ if (aConfig.getMode()==ConfigAccess::E_CLOSED)
+ return;
+
+ css::uno::Reference< css::beans::XMultiHierarchicalPropertySet > xArgumentList(aConfig.cfg(), css::uno::UNO_QUERY);
+ if (xArgumentList.is())
+ {
+ sal_Int32 nCount = m_lArguments.size();
+ css::uno::Sequence< OUString > lNames (nCount);
+ auto lNamesRange = asNonConstRange(lNames);
+ css::uno::Sequence< css::uno::Any > lValues(nCount);
+ auto lValuesRange = asNonConstRange(lValues);
+
+ for (sal_Int32 i=0; i<nCount; ++i)
+ {
+ lNamesRange [i] = m_lArguments[i].Name;
+ lValuesRange[i] = m_lArguments[i].Value;
+ }
+
+ xArgumentList->setHierarchicalPropertyValues(lNames, lValues);
+ }
+ aConfig.close();
+}
+
+/**
+ @short set a new environment descriptor for this job
+ @descr It must(!) be done every time this container is initialized
+ with new job data e.g.: setAlias()/setEvent()/setService() ...
+ Otherwise the environment will be unknown!
+ */
+void JobData::setEnvironment( EEnvironment eEnvironment )
+{
+ SolarMutexGuard g;
+ m_eEnvironment = eEnvironment;
+}
+
+/**
+ @short these functions provides access to our internal members
+ @descr These member represent any information about the job
+ and can be used from outside to e.g. start a job.
+ */
+JobData::EMode JobData::getMode() const
+{
+ SolarMutexGuard g;
+ return m_eMode;
+}
+
+JobData::EEnvironment JobData::getEnvironment() const
+{
+ SolarMutexGuard g;
+ return m_eEnvironment;
+}
+
+OUString JobData::getEnvironmentDescriptor() const
+{
+ OUString sDescriptor;
+ SolarMutexGuard g;
+ switch(m_eEnvironment)
+ {
+ case E_EXECUTION :
+ sDescriptor = "EXECUTOR";
+ break;
+
+ case E_DISPATCH :
+ sDescriptor = "DISPATCH";
+ break;
+
+ case E_DOCUMENTEVENT :
+ sDescriptor = "DOCUMENTEVENT";
+ break;
+ default:
+ break;
+ }
+ return sDescriptor;
+}
+
+OUString JobData::getService() const
+{
+ SolarMutexGuard g;
+ return m_sService;
+}
+
+OUString JobData::getEvent() const
+{
+ SolarMutexGuard g;
+ return m_sEvent;
+}
+
+std::vector< css::beans::NamedValue > JobData::getJobConfig() const
+{
+ SolarMutexGuard g;
+ return m_lArguments;
+}
+
+css::uno::Sequence< css::beans::NamedValue > JobData::getConfig() const
+{
+ SolarMutexGuard g;
+ css::uno::Sequence< css::beans::NamedValue > lConfig;
+ if (m_eMode==E_ALIAS)
+ {
+ lConfig = { { "Alias", css::uno::Any(m_sAlias) },
+ { "Service", css::uno::Any(m_sService) },
+ { "Context", css::uno::Any(m_sContext) } };
+ }
+ return lConfig;
+}
+
+/**
+ @short return information, if this job is part of the global configuration package
+ org.openoffice.Office.Jobs
+ @descr Because jobs can be executed by the dispatch framework using a uno service name
+ directly - an executed job must not have any configuration really. Such jobs
+ must provide the right interfaces only! But after finishing jobs can return
+ some information (e.g. for updating her configuration ...). We must know
+ if such request is valid or not then.
+
+ @return sal_True if the represented job is part of the underlying configuration package.
+ */
+bool JobData::hasConfig() const
+{
+ SolarMutexGuard g;
+ return (m_eMode==E_ALIAS || m_eMode==E_EVENT);
+}
+
+/**
+ @short mark a job as non startable for further requests
+ @descr We don't remove the configuration entry! We set a timestamp value only.
+ And there exist two of them: one for an administrator... and one for the
+ current user. We change it for the user layer only. So this JobDispatch can't be
+ started any more... till the administrator change his timestamp.
+ That can be useful for post setup scenarios, which must run one time only.
+
+ Note: This method don't do anything, if this represented job doesn't have a configuration!
+ */
+void JobData::disableJob()
+{
+ SolarMutexGuard g;
+
+ // No configuration - not used from EXECUTOR and not triggered from an event => no chance!
+ if (m_eMode!=E_EVENT)
+ return;
+
+ // update the configuration
+ // It doesn't matter if this config object was already opened before.
+ // It doesn nothing here then ... or it change the mode automatically, if
+ // it was opened using another one before.
+ ConfigAccess aConfig(
+ m_xContext,
+ ("/org.openoffice.Office.Jobs/Events/"
+ + utl::wrapConfigurationElementName(m_sEvent) + "/JobList/"
+ + utl::wrapConfigurationElementName(m_sAlias)));
+ aConfig.open(ConfigAccess::E_READWRITE);
+ if (aConfig.getMode()==ConfigAccess::E_CLOSED)
+ return;
+
+ css::uno::Reference< css::beans::XPropertySet > xPropSet(aConfig.cfg(), css::uno::UNO_QUERY);
+ if (xPropSet.is())
+ {
+ // Convert and write the user timestamp to the configuration.
+ css::uno::Any aValue;
+ aValue <<= Converter::convert_DateTime2ISO8601(DateTime( DateTime::SYSTEM));
+ xPropSet->setPropertyValue("UserTime", aValue);
+ }
+
+ aConfig.close();
+}
+
+static bool isEnabled( std::u16string_view sAdminTime ,
+ std::u16string_view sUserTime )
+{
+ /*Attention!
+ To prevent interpreting of TriGraphs inside next const string value,
+ we have to encode all '?' signs. Otherwise e.g. "??-" will be translated
+ to "~" ...
+ */
+ WildCard aISOPattern(u"\?\?\?\?-\?\?-\?\?*");
+
+ bool bValidAdmin = aISOPattern.Matches(sAdminTime);
+ bool bValidUser = aISOPattern.Matches(sUserTime );
+
+ // We check for "isEnabled()" here only.
+ // Note further: ISO8601 formatted strings can be compared as strings directly!
+ // FIXME: this is not true! "T1215" is the same time as "T12:15" or "T121500"
+ return (
+ (!bValidAdmin && !bValidUser ) ||
+ ( bValidAdmin && bValidUser && sAdminTime>=sUserTime)
+ );
+}
+
+void JobData::appendEnabledJobsForEvent( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const OUString& sEvent ,
+ ::std::vector< JobData::TJob2DocEventBinding >& lJobs )
+{
+ std::vector< OUString > lAdditionalJobs = JobData::getEnabledJobsForEvent(rxContext, sEvent);
+ sal_Int32 c = lAdditionalJobs.size();
+ sal_Int32 i = 0;
+
+ for (i=0; i<c; ++i)
+ {
+ JobData::TJob2DocEventBinding aBinding(lAdditionalJobs[i], sEvent);
+ lJobs.push_back(aBinding);
+ }
+}
+
+bool JobData::hasCorrectContext(const OUString& rModuleIdent) const
+{
+ sal_Int32 nContextLen = m_sContext.getLength();
+ sal_Int32 nModuleIdLen = rModuleIdent.getLength();
+
+ if ( nContextLen == 0 )
+ return true;
+
+ if ( nModuleIdLen > 0 )
+ {
+ sal_Int32 nIndex = m_sContext.indexOf( rModuleIdent );
+ if ( nIndex >= 0 && ( nIndex+nModuleIdLen <= nContextLen ))
+ {
+ std::u16string_view sContextModule = m_sContext.subView( nIndex, nModuleIdLen );
+ return sContextModule == rModuleIdent;
+ }
+ }
+
+ return false;
+}
+
+std::vector< OUString > JobData::getEnabledJobsForEvent( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ std::u16string_view sEvent )
+{
+ // create a config access to "/org.openoffice.Office.Jobs/Events"
+ ConfigAccess aConfig(rxContext, "/org.openoffice.Office.Jobs/Events");
+ aConfig.open(ConfigAccess::E_READONLY);
+ if (aConfig.getMode()==ConfigAccess::E_CLOSED)
+ return std::vector< OUString >();
+
+ css::uno::Reference< css::container::XHierarchicalNameAccess > xEventRegistry(aConfig.cfg(), css::uno::UNO_QUERY);
+ if (!xEventRegistry.is())
+ return std::vector< OUString >();
+
+ // check if the given event exist inside list of registered ones
+ OUString sPath(OUString::Concat(sEvent) + "/JobList");
+ if (!xEventRegistry->hasByHierarchicalName(sPath))
+ return std::vector< OUString >();
+
+ // step to the job list, which is a child of the event node inside cfg
+ // e.g. "/org.openoffice.Office.Jobs/Events/<event name>/JobList"
+ css::uno::Any aJobList = xEventRegistry->getByHierarchicalName(sPath);
+ css::uno::Reference< css::container::XNameAccess > xJobList;
+ if (!(aJobList >>= xJobList) || !xJobList.is())
+ return std::vector< OUString >();
+
+ // get all alias names of jobs, which are part of this job list
+ // But Some of them can be disabled by its timestamp values.
+ // We create an additional job name list with the same size, then the original list...
+ // step over all job entries... check her timestamps... and put only job names to the
+ // destination list, which represent an enabled job.
+ const css::uno::Sequence< OUString > lAllJobs = xJobList->getElementNames();
+ sal_Int32 c = lAllJobs.getLength();
+
+ std::vector< OUString > lEnabledJobs(c);
+ sal_Int32 d = 0;
+
+ for (OUString const & jobName : lAllJobs)
+ {
+ css::uno::Reference< css::beans::XPropertySet > xJob;
+ if (
+ !(xJobList->getByName(jobName) >>= xJob) ||
+ !(xJob.is() )
+ )
+ {
+ continue;
+ }
+
+ OUString sAdminTime;
+ xJob->getPropertyValue("AdminTime") >>= sAdminTime;
+
+ OUString sUserTime;
+ xJob->getPropertyValue("UserTime") >>= sUserTime;
+
+ if (!isEnabled(sAdminTime, sUserTime))
+ continue;
+
+ lEnabledJobs[d] = jobName;
+ ++d;
+ }
+ lEnabledJobs.resize(d);
+
+ aConfig.close();
+
+ return lEnabledJobs;
+}
+
+/**
+ @short reset all internal structures
+ @descr If someone recycles this instance, he can switch from one
+ using mode to another one. But then we have to reset all currently
+ used information. Otherwise we mix it and they can make trouble.
+
+ But note: that does not set defaults for internal used members, which
+ does not relate to any job property! e.g. the reference to the global
+ uno service manager. Such information is used for internal processes only
+ and are necessary for our work.
+ */
+void JobData::impl_reset()
+{
+ SolarMutexGuard g;
+ m_eMode = E_UNKNOWN_MODE;
+ m_eEnvironment = E_UNKNOWN_ENVIRONMENT;
+ m_sAlias.clear();
+ m_sService.clear();
+ m_sContext.clear();
+ m_sEvent.clear();
+ m_lArguments.clear();
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/jobs/jobdispatch.cxx b/framework/source/jobs/jobdispatch.cxx
new file mode 100644
index 000000000..d1e227b9a
--- /dev/null
+++ b/framework/source/jobs/jobdispatch.cxx
@@ -0,0 +1,496 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <jobs/configaccess.hxx>
+#include <jobs/joburl.hxx>
+#include <jobs/job.hxx>
+#include <classes/converter.hxx>
+
+#include <com/sun/star/frame/DispatchResultEvent.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XNotifyingDispatch.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <com/sun/star/frame/XDispatchResultListener.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+using namespace framework;
+
+namespace {
+
+/**
+ @short implements a dispatch object for jobs
+ @descr Such dispatch object will be used by the generic dispatch mechanism if
+ a URL "vnd.sun.star.job:alias=<name>" occurs.
+ Then an instance of this class will be created and used.
+ This new instance will be called within his method
+ dispatch() or dispatchWithNotification() for executing the
+ real job. We do it, control the life cycle of this internal
+ wrapped job and inform any interested listener if it finish.
+ */
+class JobDispatch : public ::cppu::WeakImplHelper<
+ css::lang::XServiceInfo
+ , css::lang::XInitialization
+ , css::frame::XDispatchProvider
+ , css::frame::XNotifyingDispatch > // => XDispatch
+{
+private:
+
+ /** reference to the uno service manager */
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ /** reference to the frame, inside which this dispatch is used */
+ css::uno::Reference< css::frame::XFrame > m_xFrame;
+
+ /** name of module (writer, impress etc.) the frame is for */
+ OUString m_sModuleIdentifier;
+
+// native interface methods
+
+public:
+
+ explicit JobDispatch(css::uno::Reference< css::uno::XComponentContext > xContext);
+ virtual ~JobDispatch() override;
+
+ void impl_dispatchEvent ( const OUString& sEvent ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArgs ,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener );
+ void impl_dispatchService( const OUString& sService ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArgs ,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener );
+ void impl_dispatchAlias ( const OUString& sAlias ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArgs ,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener );
+
+public:
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.jobs.JobDispatch";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.frame.ProtocolHandler"};
+ }
+
+ // Xinitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& lArguments ) override;
+
+ // XDispatchProvider
+ virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch ( const css::util::URL& aURL ,
+ const OUString& sTargetFrameName ,
+ sal_Int32 nSearchFlags ) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) override;
+
+ // XNotifyingDispatch
+ virtual void SAL_CALL dispatchWithNotification( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArgs ,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) override;
+
+ // XDispatch
+ virtual void SAL_CALL dispatch ( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArgs ) override;
+ virtual void SAL_CALL addStatusListener ( const css::uno::Reference< css::frame::XStatusListener >& xListener ,
+ const css::util::URL& aURL ) override;
+ virtual void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xListener ,
+ const css::util::URL& aURL ) override;
+};
+
+/**
+ @short standard ctor
+ @descr It initialize this new instance.
+
+ @param xContext
+ reference to the uno service manager
+*/
+JobDispatch::JobDispatch( /*IN*/ css::uno::Reference< css::uno::XComponentContext > xContext )
+ : m_xContext (std::move(xContext ))
+{
+}
+
+/**
+ @short let this instance die
+ @descr We have to release all used resources and free used memory.
+*/
+JobDispatch::~JobDispatch()
+{
+ // release all used resources
+ m_xContext.clear();
+ m_xFrame.clear();
+}
+
+/**
+ @short implementation of XInitialization
+ @descr A protocol handler can provide this functionality, if it wish to get additional information
+ about the context it runs. In this case the frame reference would be given by the outside code.
+
+ @param lArguments
+ the list of initialization arguments
+ First parameter should be the frame reference we need.
+*/
+void SAL_CALL JobDispatch::initialize( const css::uno::Sequence< css::uno::Any >& lArguments )
+{
+ SolarMutexGuard g;
+
+ for (int a=0; a<lArguments.getLength(); ++a)
+ {
+ if (a==0)
+ {
+ lArguments[a] >>= m_xFrame;
+
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager =
+ css::frame::ModuleManager::create(m_xContext);
+ try
+ {
+ m_sModuleIdentifier = xModuleManager->identify( m_xFrame );
+ }
+ catch( const css::uno::Exception& )
+ {}
+ }
+ }
+}
+
+/**
+ @short implementation of XDispatchProvider::queryDispatches()
+ @descr Every protocol handler will be asked for his agreement, if a URL was queried
+ for which this handler is registered. It's the chance for this handler to validate
+ the given URL and return a dispatch object (may be itself) or not.
+
+ @param aURL
+ the queried URL, which should be checked
+
+ @param sTargetFrameName
+ describes the target frame, in which context this handler will be used
+ Is mostly set to "", "_self", "_blank", "_default" or a non special one
+ using SELF/CREATE as search flags.
+
+ @param nSearchFlags
+ Can be SELF or CREATE only and are set only if sTargetFrameName isn't a special target
+*/
+css::uno::Reference< css::frame::XDispatch > SAL_CALL JobDispatch::queryDispatch( /*IN*/ const css::util::URL& aURL ,
+ /*IN*/ const OUString& /*sTargetFrameName*/ ,
+ /*IN*/ sal_Int32 /*nSearchFlags*/ )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatch;
+
+ JobURL aAnalyzedURL(aURL.Complete);
+ if (aAnalyzedURL.isValid())
+ xDispatch = this;
+
+ return xDispatch;
+}
+
+/**
+ @short implementation of XDispatchProvider::queryDispatches()
+ @descr It's an optimized access for remote, so you can ask for
+ multiple dispatch objects at the same time.
+
+ @param lDescriptor
+ a list of queryDispatch() parameter
+
+ @return A list of corresponding dispatch objects.
+ NULL references are not skipped. Every result
+ match to one given descriptor item.
+*/
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL JobDispatch::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor )
+{
+ // don't pack resulting list!
+ sal_Int32 nCount = lDescriptor.getLength();
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatches(nCount);
+ auto lDispatchesRange = asNonConstRange(lDispatches);
+ for (sal_Int32 i=0; i<nCount; ++i)
+ lDispatchesRange[i] = queryDispatch( lDescriptor[i].FeatureURL ,
+ lDescriptor[i].FrameName ,
+ lDescriptor[i].SearchFlags );
+ return lDispatches;
+}
+
+/**
+ @short implementation of XNotifyingDispatch::dispatchWithNotification()
+ @descr It creates the job service implementation and call execute on it.
+ Further it starts the life time control of it. (important for async job)
+ For synchronous job we react for the returned result directly ... for asynchronous
+ ones we do it later inside our callback method. But we use the same impl method
+ doing that to share the code. (see impl_finishJob())
+
+ If a job is already running, (it can only occur for asynchronous jobs)
+ don't start the same job a second time. Queue in the given dispatch parameter
+ and return immediately. If the current running job call us back, we will start this
+ new dispatch request.
+ If no job is running - queue the parameter too! But then start the new job immediately.
+ We have to queue it every time - because it hold us alive by ref count!
+
+ @param aURL
+ describe the job(s), which should be started
+
+ @param lArgs
+ optional arguments for this request
+
+ @param xListener
+ an interested listener for possible results of this operation
+*/
+void SAL_CALL JobDispatch::dispatchWithNotification( /*IN*/ const css::util::URL& aURL ,
+ /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs ,
+ /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ JobURL aAnalyzedURL(aURL.Complete);
+ if (aAnalyzedURL.isValid())
+ {
+ OUString sRequest;
+ if (aAnalyzedURL.getEvent(sRequest))
+ impl_dispatchEvent(sRequest, lArgs, xListener);
+ else
+ if (aAnalyzedURL.getService(sRequest))
+ impl_dispatchService(sRequest, lArgs, xListener);
+ else
+ if (aAnalyzedURL.getAlias(sRequest))
+ impl_dispatchAlias(sRequest, lArgs, xListener);
+ }
+}
+
+/**
+ @short dispatch an event
+ @descr We search all registered jobs for this event and execute it.
+ After doing so, we inform the given listener about the results.
+ (There will be one notify for every executed job!)
+
+ @param sEvent
+ the event, for which jobs can be registered
+
+ @param lArgs
+ optional arguments for this request
+ Currently not used!
+
+ @param xListener
+ an interested listener for possible results of this operation
+*/
+void JobDispatch::impl_dispatchEvent( /*IN*/ const OUString& sEvent ,
+ /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs ,
+ /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ // get list of all enabled jobs
+ // The called static helper methods read it from the configuration and
+ // filter disabled jobs using it's time stamp values.
+ /* SAFE { */
+ SolarMutexResettableGuard aReadLock;
+ std::vector< OUString > lJobs = JobData::getEnabledJobsForEvent(m_xContext, sEvent);
+ aReadLock.clear();
+ /* } SAFE */
+
+ css::uno::Reference< css::frame::XDispatchResultListener > xThis( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY );
+
+ // no jobs... no execution
+ // But a may given listener will know something...
+ // I think this operation was finished successfully.
+ // It's not really an error, if no registered jobs could be located.
+ // Step over all found jobs and execute it
+ int nExecutedJobs=0;
+ for (const OUString & lJob : lJobs)
+ {
+ /* SAFE { */
+ aReadLock.reset();
+
+ JobData aCfg(m_xContext);
+ aCfg.setEvent(sEvent, lJob);
+ aCfg.setEnvironment(JobData::E_DISPATCH);
+ const bool bIsEnabled=aCfg.hasCorrectContext(m_sModuleIdentifier);
+
+ rtl::Reference<Job> pJob = new Job(m_xContext, m_xFrame);
+ pJob->setJobData(aCfg);
+
+ aReadLock.clear();
+ /* } SAFE */
+
+ if (!bIsEnabled)
+ continue;
+
+ // Special mode for listener.
+ // We don't notify it directly here. We delegate that
+ // to the job implementation. But we must set ourself there too.
+ // Because this job must fake the source address of the event.
+ // Otherwise the listener may ignore it.
+ if (xListener.is())
+ pJob->setDispatchResultFake(xListener, xThis);
+ pJob->execute(Converter::convert_seqPropVal2seqNamedVal(lArgs));
+ ++nExecutedJobs;
+ }
+
+ if (nExecutedJobs<1 && xListener.is())
+ {
+ css::frame::DispatchResultEvent aEvent;
+ aEvent.Source = xThis;
+ aEvent.State = css::frame::DispatchResultState::SUCCESS;
+ xListener->dispatchFinished(aEvent);
+ }
+}
+
+/**
+ @short dispatch a service
+ @descr We use the given name only to create and if possible to initialize
+ it as a uno service. It can be useful for creating (caching?)
+ of e.g. one instance services.
+
+ @param sService
+ the uno implementation or service name of the job, which should be instantiated
+
+ @param lArgs
+ optional arguments for this request
+ Currently not used!
+
+ @param xListener
+ an interested listener for possible results of this operation
+*/
+void JobDispatch::impl_dispatchService( /*IN*/ const OUString& sService ,
+ /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs ,
+ /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ /* SAFE { */
+ SolarMutexClearableGuard aReadLock;
+
+ JobData aCfg(m_xContext);
+ aCfg.setService(sService);
+ aCfg.setEnvironment(JobData::E_DISPATCH);
+
+ /*Attention!
+ Jobs implements interfaces and dies by ref count!
+ And freeing of such uno object is done by uno itself.
+ So we have to use dynamic memory everytimes.
+ */
+ rtl::Reference<Job> pJob = new Job(m_xContext, m_xFrame);
+ pJob->setJobData(aCfg);
+
+ aReadLock.clear();
+ /* } SAFE */
+
+ css::uno::Reference< css::frame::XDispatchResultListener > xThis( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY );
+
+ // Special mode for listener.
+ // We don't notify it directly here. We delegate that
+ // to the job implementation. But we must set ourself there too.
+ // Because this job must fake the source address of the event.
+ // Otherwise the listener may ignore it.
+ if (xListener.is())
+ pJob->setDispatchResultFake(xListener, xThis);
+ pJob->execute(Converter::convert_seqPropVal2seqNamedVal(lArgs));
+}
+
+/**
+ @short dispatch an alias
+ @descr We use this alias to locate a job inside the configuration
+ and execute it. Further we inform the given listener about the results.
+
+ @param sAlias
+ the alias name of the configured job
+
+ @param lArgs
+ optional arguments for this request
+ Currently not used!
+
+ @param xListener
+ an interested listener for possible results of this operation
+*/
+void JobDispatch::impl_dispatchAlias( /*IN*/ const OUString& sAlias ,
+ /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs ,
+ /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ /* SAFE { */
+ SolarMutexClearableGuard aReadLock;
+
+ JobData aCfg(m_xContext);
+ aCfg.setAlias(sAlias);
+ aCfg.setEnvironment(JobData::E_DISPATCH);
+
+ rtl::Reference<Job> pJob = new Job(m_xContext, m_xFrame);
+ pJob->setJobData(aCfg);
+
+ aReadLock.clear();
+ /* } SAFE */
+
+ css::uno::Reference< css::frame::XDispatchResultListener > xThis( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY );
+
+ // Special mode for listener.
+ // We don't notify it directly here. We delegate that
+ // to the job implementation. But we must set ourself there too.
+ // Because this job must fake the source address of the event.
+ // Otherwise the listener may ignore it.
+ if (xListener.is())
+ pJob->setDispatchResultFake(xListener, xThis);
+ pJob->execute(Converter::convert_seqPropVal2seqNamedVal(lArgs));
+}
+
+/**
+ @short implementation of XDispatch::dispatch()
+ @descr Because the methods dispatch() and dispatchWithNotification() are different in her parameters
+ only, we can forward this request to dispatchWithNotification() by using an empty listener!
+
+ @param aURL
+ describe the job(s), which should be started
+
+ @param lArgs
+ optional arguments for this request
+
+ @see dispatchWithNotification()
+*/
+void SAL_CALL JobDispatch::dispatch( /*IN*/ const css::util::URL& aURL ,
+ /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs )
+{
+ dispatchWithNotification(aURL, lArgs, css::uno::Reference< css::frame::XDispatchResultListener >());
+}
+
+/**
+ @short not supported
+*/
+void SAL_CALL JobDispatch::addStatusListener( /*IN*/ const css::uno::Reference< css::frame::XStatusListener >&,
+ /*IN*/ const css::util::URL& )
+{
+}
+
+/**
+ @short not supported
+*/
+void SAL_CALL JobDispatch::removeStatusListener( /*IN*/ const css::uno::Reference< css::frame::XStatusListener >&,
+ /*IN*/ const css::util::URL& )
+{
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_jobs_JobDispatch_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new JobDispatch(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/jobs/jobexecutor.cxx b/framework/source/jobs/jobexecutor.cxx
new file mode 100644
index 000000000..5578b877b
--- /dev/null
+++ b/framework/source/jobs/jobexecutor.cxx
@@ -0,0 +1,399 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <jobs/job.hxx>
+#include <jobs/configaccess.hxx>
+#include <classes/converter.hxx>
+
+#include <helper/mischelper.hxx>
+
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/task/XJobExecutor.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/document/XEventListener.hpp>
+
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/configpaths.hxx>
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace framework;
+
+namespace {
+
+typedef cppu::WeakComponentImplHelper<
+ css::lang::XServiceInfo
+ , css::task::XJobExecutor
+ , css::container::XContainerListener // => lang.XEventListener
+ , css::document::XEventListener >
+ Base;
+
+/**
+ @short implements a job executor, which can be triggered from any code
+ @descr It uses the given trigger event to locate any registered job service
+ inside the configuration and execute it. Of course it controls the
+ lifetime of such jobs too.
+ */
+class JobExecutor : private cppu::BaseMutex, public Base
+{
+private:
+
+ /** reference to the uno service manager */
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ /** cached list of all registered event names of cfg for call optimization. */
+ std::vector<OUString> m_lEvents;
+
+ /** we listen at the configuration for changes at the event list. */
+ ConfigAccess m_aConfig;
+
+ /** helper to allow us listen to the configuration without a cyclic dependency */
+ css::uno::Reference<css::container::XContainerListener> m_xConfigListener;
+
+ virtual void SAL_CALL disposing() final override;
+
+public:
+
+ explicit JobExecutor(const css::uno::Reference< css::uno::XComponentContext >& xContext);
+ virtual ~JobExecutor() override;
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.JobExecutor";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.task.JobExecutor"};
+ }
+
+ // task.XJobExecutor
+ virtual void SAL_CALL trigger( const OUString& sEvent ) override;
+
+ /// Initialization function after having acquire()'d.
+ void initListeners();
+
+ // document.XEventListener
+ virtual void SAL_CALL notifyEvent( const css::document::EventObject& aEvent ) override;
+
+ // container.XContainerListener
+ virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& aEvent ) override;
+ virtual void SAL_CALL elementRemoved ( const css::container::ContainerEvent& aEvent ) override;
+ virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& aEvent ) override;
+
+ // lang.XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override;
+};
+
+/**
+ @short standard ctor
+ @descr It initialize this new instance.
+
+ @param xContext
+ reference to the uno service manager
+ */
+JobExecutor::JobExecutor( /*IN*/ const css::uno::Reference< css::uno::XComponentContext >& xContext )
+ : Base (m_aMutex)
+ , m_xContext (xContext )
+ , m_aConfig (xContext, "/org.openoffice.Office.Jobs/Events")
+{
+}
+
+void JobExecutor::initListeners()
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+
+ // read the list of all currently registered events inside configuration.
+ // e.g. "/org.openoffice.Office.Jobs/Events/<event name>"
+ // We need it later to check if an incoming event request can be executed successfully
+ // or must be rejected. It's an optimization! Of course we must implement updating of this
+ // list too ... Be listener at the configuration.
+
+ m_aConfig.open(ConfigAccess::E_READONLY);
+ if (m_aConfig.getMode() != ConfigAccess::E_READONLY)
+ return;
+
+ css::uno::Reference< css::container::XNameAccess > xRegistry(
+ m_aConfig.cfg(), css::uno::UNO_QUERY);
+ if (xRegistry.is())
+ m_lEvents = Converter::convert_seqOUString2OUStringList(
+ xRegistry->getElementNames());
+
+ css::uno::Reference< css::container::XContainer > xNotifier(
+ m_aConfig.cfg(), css::uno::UNO_QUERY);
+ if (xNotifier.is())
+ {
+ m_xConfigListener = new WeakContainerListener(this);
+ xNotifier->addContainerListener(m_xConfigListener);
+ }
+
+ // don't close cfg here!
+ // It will be done inside disposing ...
+}
+
+JobExecutor::~JobExecutor()
+{
+ disposing();
+}
+
+void JobExecutor::disposing() {
+ css::uno::Reference<css::container::XContainer> notifier;
+ css::uno::Reference<css::container::XContainerListener> listener;
+ {
+ osl::MutexGuard g(rBHelper.rMutex);
+ if (m_aConfig.getMode() != ConfigAccess::E_CLOSED) {
+ notifier.set(m_aConfig.cfg(), css::uno::UNO_QUERY);
+ listener = m_xConfigListener;
+ m_aConfig.close();
+ }
+ m_xConfigListener.clear();
+ }
+ if (notifier.is()) {
+ notifier->removeContainerListener(listener);
+ }
+}
+
+/**
+ @short implementation of XJobExecutor interface
+ @descr We use the given event to locate any registered job inside our configuration
+ and execute it. Further we control the lifetime of it and suppress
+ shutdown of the office till all jobs was finished.
+
+ @param sEvent
+ is used to locate registered jobs
+ */
+void SAL_CALL JobExecutor::trigger( const OUString& sEvent )
+{
+ SAL_INFO( "fwk", "JobExecutor::trigger()");
+
+ std::vector< OUString > lJobs;
+
+ /* SAFE */ {
+ osl::MutexGuard g(rBHelper.rMutex);
+
+ // Optimization!
+ // Check if the given event name exist inside configuration and reject wrong requests.
+ // This optimization suppress using of the cfg api for getting event and job descriptions ...
+ if (std::find(m_lEvents.begin(), m_lEvents.end(), sEvent) == m_lEvents.end())
+ return;
+
+ // get list of all enabled jobs
+ // The called static helper methods read it from the configuration and
+ // filter disabled jobs using it's time stamp values.
+ lJobs = JobData::getEnabledJobsForEvent(m_xContext, sEvent);
+ } /* SAFE */
+
+ // step over all enabled jobs and execute it
+ size_t c = lJobs.size();
+ for (size_t j=0; j<c; ++j)
+ {
+ rtl::Reference<Job> pJob;
+
+ /* SAFE */
+ {
+ SolarMutexGuard g2;
+
+ JobData aCfg(m_xContext);
+ aCfg.setEvent(sEvent, lJobs[j]);
+ aCfg.setEnvironment(JobData::E_EXECUTION);
+
+ /*Attention!
+ Jobs implements interfaces and dies by ref count!
+ And freeing of such uno object is done by uno itself.
+ So we have to use dynamic memory everytimes.
+ */
+ pJob = new Job(m_xContext, css::uno::Reference< css::frame::XFrame >());
+ pJob->setJobData(aCfg);
+ } /* SAFE */
+
+ pJob->execute(css::uno::Sequence< css::beans::NamedValue >());
+ }
+}
+
+void SAL_CALL JobExecutor::notifyEvent( const css::document::EventObject& aEvent )
+{
+ static constexpr OUStringLiteral EVENT_ON_DOCUMENT_OPENED(u"onDocumentOpened"); // Job UI event : OnNew or OnLoad
+ static constexpr OUStringLiteral EVENT_ON_DOCUMENT_ADDED(u"onDocumentAdded"); // Job API event : OnCreate or OnLoadFinished
+
+ OUString aModuleIdentifier;
+ ::std::vector< JobData::TJob2DocEventBinding > lJobs;
+
+ /* SAFE */ {
+ osl::MutexGuard g(rBHelper.rMutex);
+
+ // Optimization!
+ // Check if the given event name exist inside configuration and reject wrong requests.
+ // This optimization suppress using of the cfg api for getting event and job descriptions.
+ // see using of m_lEvents.find() below ...
+
+ // retrieve event context from event source
+ try
+ {
+ aModuleIdentifier = css::frame::ModuleManager::create( m_xContext )->identify( aEvent.Source );
+ }
+ catch( const css::uno::Exception& )
+ {}
+
+ // Special feature: If the events "OnNew" or "OnLoad" occurs - we generate our own event "onDocumentOpened".
+ if (
+ (aEvent.EventName == "OnNew") ||
+ (aEvent.EventName == "OnLoad")
+ )
+ {
+ if (std::find(m_lEvents.begin(), m_lEvents.end(), EVENT_ON_DOCUMENT_OPENED) != m_lEvents.end())
+ JobData::appendEnabledJobsForEvent(m_xContext, EVENT_ON_DOCUMENT_OPENED, lJobs);
+ }
+
+ // Special feature: If the events "OnCreate" or "OnLoadFinished" occurs - we generate our own event "onDocumentAdded".
+ if (
+ (aEvent.EventName == "OnCreate") ||
+ (aEvent.EventName == "OnLoadFinished")
+ )
+ {
+ if (std::find(m_lEvents.begin(), m_lEvents.end(), EVENT_ON_DOCUMENT_ADDED) != m_lEvents.end())
+ JobData::appendEnabledJobsForEvent(m_xContext, EVENT_ON_DOCUMENT_ADDED, lJobs);
+ }
+
+ // Add all jobs for "real" notified event too .-)
+ if (std::find(m_lEvents.begin(), m_lEvents.end(), aEvent.EventName) != m_lEvents.end())
+ JobData::appendEnabledJobsForEvent(m_xContext, aEvent.EventName, lJobs);
+ } /* SAFE */
+
+ // step over all enabled jobs and execute it
+ for (auto const& lJob : lJobs)
+ {
+ rtl::Reference<Job> pJob;
+
+ /* SAFE */ {
+ SolarMutexGuard g2;
+
+ const JobData::TJob2DocEventBinding& rBinding = lJob;
+
+ JobData aCfg(m_xContext);
+ aCfg.setEvent(rBinding.m_sDocEvent, rBinding.m_sJobName);
+ aCfg.setEnvironment(JobData::E_DOCUMENTEVENT);
+
+ if (!aCfg.hasCorrectContext(aModuleIdentifier))
+ continue;
+
+ /*Attention!
+ Jobs implements interfaces and dies by ref count!
+ And freeing of such uno object is done by uno itself.
+ So we have to use dynamic memory everytimes.
+ */
+ css::uno::Reference< css::frame::XModel > xModel(aEvent.Source, css::uno::UNO_QUERY);
+ pJob = new Job(m_xContext, xModel);
+ pJob->setJobData(aCfg);
+ } /* SAFE */
+
+ pJob->execute(css::uno::Sequence< css::beans::NamedValue >());
+ }
+}
+
+void SAL_CALL JobExecutor::elementInserted( const css::container::ContainerEvent& aEvent )
+{
+ OUString sValue;
+ if (aEvent.Accessor >>= sValue)
+ {
+ OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue);
+ if (!sEvent.isEmpty())
+ {
+ std::vector<OUString>::iterator pEvent = std::find(m_lEvents.begin(), m_lEvents.end(), sEvent);
+ if (pEvent == m_lEvents.end())
+ m_lEvents.push_back(sEvent);
+ }
+ }
+}
+
+void SAL_CALL JobExecutor::elementRemoved ( const css::container::ContainerEvent& aEvent )
+{
+ OUString sValue;
+ if (aEvent.Accessor >>= sValue)
+ {
+ OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue);
+ if (!sEvent.isEmpty())
+ {
+ std::vector<OUString>::iterator pEvent = std::find(m_lEvents.begin(), m_lEvents.end(), sEvent);
+ if (pEvent != m_lEvents.end())
+ m_lEvents.erase(pEvent);
+ }
+ }
+}
+
+void SAL_CALL JobExecutor::elementReplaced( const css::container::ContainerEvent& )
+{
+ // I'm not interested on changed items :-)
+}
+
+/** @short the used cfg changes notifier wish to be released in its reference.
+
+ @descr We close our internal used configuration instance to
+ free this reference.
+
+ @attention For the special feature "bind global document event broadcaster to job execution"
+ this job executor instance was registered from outside code as
+ css.document.XEventListener. So it can be, that this disposing call comes from
+ the global event broadcaster service. But we don't hold any reference to this service
+ which can or must be released. Because this broadcaster itself is a one instance service
+ too, we can ignore this request. On the other side we must release our internal CFG
+ reference... SOLUTION => check the given event source and react only, if it's our internal
+ hold configuration object!
+ */
+void SAL_CALL JobExecutor::disposing( const css::lang::EventObject& aEvent )
+{
+ /* SAFE { */
+ osl::MutexGuard g(rBHelper.rMutex);
+ css::uno::Reference< css::uno::XInterface > xCFG(m_aConfig.cfg(), css::uno::UNO_QUERY);
+ if (
+ (xCFG == aEvent.Source ) &&
+ (m_aConfig.getMode() != ConfigAccess::E_CLOSED)
+ )
+ {
+ m_aConfig.close();
+ }
+ /* } SAFE */
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_JobExecutor_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ rtl::Reference<JobExecutor> xJobExec = new JobExecutor(context);
+ // 2nd phase initialization needed
+ xJobExec->initListeners();
+ return cppu::acquire(xJobExec.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/jobs/jobresult.cxx b/framework/source/jobs/jobresult.cxx
new file mode 100644
index 000000000..58cd2b594
--- /dev/null
+++ b/framework/source/jobs/jobresult.cxx
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <jobs/jobresult.hxx>
+#include <jobs/jobconst.hxx>
+
+#include <vcl/svapp.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/sequence.hxx>
+
+namespace framework
+{
+/**
+ @short special ctor
+ @descr It initialize this new instance with a pure job execution result
+ and analyze it. Doing so, we update our other members.
+
+ <p>
+ It's a list of named values, packed inside this any.
+ Following protocol is used:
+ <p>
+ <ul>
+ <li>
+ "SaveArguments" [sequence< css.beans.NamedValue >]
+ <br>
+ The returned list of (for this generic implementation unknown!)
+ properties, will be written directly to the configuration and replace
+ any old values there. There will no check for changes and we don't
+ support any merge feature here. They are written only. The job has
+ to modify this list.
+ </li>
+ <li>
+ "SendDispatchResult" [css.frame.DispatchResultEvent]
+ <br>
+ The given event is send to all current registered listener.
+ But it's not guaranteed. In case no listener are available or
+ this job isn't part of the dispatch environment (because it was used
+ by the css..task.XJobExecutor->trigger() implementation) this option
+ will be ignored.
+ </li>
+ <li>
+ "Deactivate" [boolean]
+ <br>
+ The job wish to be disabled. But note: There is no way, to enable it later
+ again by using this implementation. It can be done by using the configuration
+ only. (Means to register this job again.)
+ If a job knows, that there exist some status or result listener, it must use
+ the options "SendDispatchStatus" and "SendDispatchResult" (see before) too, to
+ inform it about the deactivation of this service.
+ </li>
+ </ul>
+
+ @param aResult
+ the job result
+*/
+JobResult::JobResult(/*IN*/ const css::uno::Any& aResult)
+{
+ // reset the flag mask!
+ // It will reset the accessible state of this object.
+ // That can be useful if something will fail here ...
+ m_eParts = E_NOPART;
+
+ // analyze the result and update our other members
+ ::comphelper::SequenceAsHashMap aProtocol(aResult);
+ if (aProtocol.empty())
+ return;
+
+ ::comphelper::SequenceAsHashMap::const_iterator pIt
+ = aProtocol.find(JobConst::ANSWER_DEACTIVATE_JOB);
+ if (pIt != aProtocol.end())
+ {
+ /**
+ an executed job can force his deactivation
+ But we provide this information here only.
+ Doing so is part of any user of us.
+ */
+ bool bDeactivate(false);
+ pIt->second >>= bDeactivate;
+ if (bDeactivate)
+ m_eParts |= E_DEACTIVATE;
+ }
+
+ pIt = aProtocol.find(JobConst::ANSWER_SAVE_ARGUMENTS);
+ if (pIt != aProtocol.end())
+ {
+ css::uno::Sequence<css::beans::NamedValue> aTmp;
+ pIt->second >>= aTmp;
+ comphelper::sequenceToContainer(m_lArguments, aTmp);
+ if (m_lArguments.empty())
+ m_eParts |= E_ARGUMENTS;
+ }
+
+ pIt = aProtocol.find(JobConst::ANSWER_SEND_DISPATCHRESULT);
+ if (pIt != aProtocol.end())
+ {
+ if (pIt->second >>= m_aDispatchResult)
+ m_eParts |= E_DISPATCHRESULT;
+ }
+}
+
+/**
+ @short copy dtor
+*/
+JobResult::JobResult(const JobResult& rCopy)
+{
+ m_eParts = rCopy.m_eParts;
+ m_lArguments = rCopy.m_lArguments;
+ m_aDispatchResult = rCopy.m_aDispatchResult;
+}
+
+/**
+ @short standard dtor
+ @descr Free all internally used resources at the end of living.
+*/
+JobResult::~JobResult()
+{
+ // Nothing really to do here.
+}
+
+/**
+ @short =operator
+ @descr Must be implemented to overwrite this instance with another one.
+
+ @param rCopy
+ reference to the other instance, which should be used for copying.
+*/
+JobResult& JobResult::operator=(const JobResult& rCopy)
+{
+ SolarMutexGuard g;
+ m_eParts = rCopy.m_eParts;
+ m_lArguments = rCopy.m_lArguments;
+ m_aDispatchResult = rCopy.m_aDispatchResult;
+ return *this;
+}
+
+/**
+ @short checks for existing parts of the analyzed result
+ @descr The internal flag mask was set after analyzing of the pure result.
+ An user of us can check here, if the required part was really part
+ of this result. Otherwise it would use invalid information ...
+ by using our other members!
+
+ @param eParts
+ a flag mask too, which will be compared with our internally set one.
+
+ @return We return true only, if any set flag of the given mask match.
+*/
+bool JobResult::existPart(sal_uInt32 eParts) const
+{
+ SolarMutexGuard g;
+ return ((m_eParts & eParts) == eParts);
+}
+
+/**
+ @short provides access to our internal members
+ @descr The return value will be valid only in case a call of
+ existPart(E_...) before returned true!
+
+ @return It returns the state of the internal member
+ without any checks!
+*/
+std::vector<css::beans::NamedValue> JobResult::getArguments() const
+{
+ SolarMutexGuard g;
+ return m_lArguments;
+}
+
+css::frame::DispatchResultEvent JobResult::getDispatchResult() const
+{
+ SolarMutexGuard g;
+ return m_aDispatchResult;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/jobs/joburl.cxx b/framework/source/jobs/joburl.cxx
new file mode 100644
index 000000000..efff201a8
--- /dev/null
+++ b/framework/source/jobs/joburl.cxx
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cstring>
+
+#include <jobs/joburl.hxx>
+
+#include <vcl/svapp.hxx>
+
+namespace framework{
+
+/**
+ @short special ctor
+ @descr It initialize this new instance with a (hopefully) valid job URL.
+ This URL will be parsed. After that we set our members right,
+ so other interface methods of this class can be used to get
+ all items of this URL. Of course it will be possible to know,
+ if this URL was valid too.
+
+ @param sURL
+ the job URL for parsing
+*/
+JobURL::JobURL( /*IN*/ const OUString& sURL )
+{
+ m_eRequest = E_UNKNOWN;
+
+ // syntax: vnd.sun.star.job:{[event=<name>],[alias=<name>],[service=<name>]}
+
+ // check for "vnd.sun.star.job:"
+ if (!sURL.startsWithIgnoreAsciiCase("vnd.sun.star.job:"))
+ return;
+
+ sal_Int32 t = std::strlen("vnd.sun.star.job:");
+ do
+ {
+ // separate all token of "{[event=<name>],[alias=<name>],[service=<name>]}"
+ OUString sToken = sURL.getToken(0, JOBURL_PART_SEPARATOR, t);
+ OUString sPartValue;
+ OUString sPartArguments;
+
+ // check for "event="
+ if (
+ (JobURL::implst_split(sToken,JOBURL_EVENT_STR,JOBURL_EVENT_LEN,sPartValue,sPartArguments)) &&
+ (!sPartValue.isEmpty())
+ )
+ {
+ // set the part value
+ m_sEvent = sPartValue;
+ m_eRequest |= E_EVENT;
+ }
+ else
+ // check for "alias="
+ if (
+ (JobURL::implst_split(sToken,JOBURL_ALIAS_STR,JOBURL_ALIAS_LEN,sPartValue,sPartArguments)) &&
+ (!sPartValue.isEmpty())
+ )
+ {
+ // set the part value
+ m_sAlias = sPartValue;
+ m_eRequest |= E_ALIAS;
+ }
+ else
+ // check for "service="
+ if (
+ (JobURL::implst_split(sToken,JOBURL_SERVICE_STR,JOBURL_SERVICE_LEN,sPartValue,sPartArguments)) &&
+ (!sPartValue.isEmpty())
+ )
+ {
+ // set the part value
+ m_sService = sPartValue;
+ m_eRequest |= E_SERVICE;
+ }
+ }
+ while(t!=-1);
+}
+
+/**
+ @short knows, if this job URL object hold a valid URL inside
+
+ @return <TRUE/> if it represent a valid job URL.
+*/
+bool JobURL::isValid() const
+{
+ SolarMutexGuard g;
+ return (m_eRequest!=E_UNKNOWN);
+}
+
+/**
+ @short get the event item of this job URL
+ @descr Because the three possible parts of such URL (event, alias, service)
+ can't be combined, this method can(!) return a valid value - but it's
+ not a must. That's why the return value must be used too, to detect a missing
+ event value.
+
+ @param sEvent
+ returns the possible existing event value
+ e.g. "vnd.sun.star.job:event=myEvent" returns "myEvent"
+
+ @return <TRUE/> if an event part of the job URL exist and the out parameter
+ sEvent was filled.
+
+ @attention The out parameter will be reset every time. Don't use it if method returns <FALSE/>!
+*/
+bool JobURL::getEvent( /*OUT*/ OUString& sEvent ) const
+{
+ SolarMutexGuard g;
+
+ sEvent.clear();
+ bool bSet = ((m_eRequest & E_EVENT) == E_EVENT);
+ if (bSet)
+ sEvent = m_sEvent;
+
+ return bSet;
+}
+
+/**
+ @short get the alias item of this job URL
+ @descr Because the three possible parts of such URL (event, alias, service)
+ can't be combined, this method can(!) return a valid value - but it's
+ not a must. that's why the return value must be used too, to detect a missing
+ alias value.
+
+ @param sAlias
+ returns the possible existing alias value
+ e.g. "vnd.sun.star.job:alias=myAlias" returns "myAlias"
+
+ @return <TRUE/> if an alias part of the job URL exist and the out parameter
+ sAlias was filled.
+
+ @attention The out parameter will be reset every time. Don't use it if method returns <FALSE/>!
+*/
+bool JobURL::getAlias( /*OUT*/ OUString& sAlias ) const
+{
+ SolarMutexGuard g;
+
+ sAlias.clear();
+ bool bSet = ((m_eRequest & E_ALIAS) == E_ALIAS);
+ if (bSet)
+ sAlias = m_sAlias;
+
+ return bSet;
+}
+
+/**
+ @short get the service item of this job URL
+ @descr Because the three possible parts of such URL (event, service, service)
+ can't be combined, this method can(!) return a valid value - but it's
+ not a must. That's why the return value must be used too, to detect a missing
+ service value.
+
+ @param sAlias
+ returns the possible existing service value
+ e.g. "vnd.sun.star.job:service=com.sun.star.Service" returns "com.sun.star.Service"
+
+ @return <TRUE/> if a service part of the job URL exist and the out parameter
+ sService was filled.
+
+ @attention The out parameter will be reset every time. Don't use it if method returns <FALSE/>!
+*/
+bool JobURL::getService( /*OUT*/ OUString& sService ) const
+{
+ SolarMutexGuard g;
+
+ sService.clear();
+ bool bSet = ((m_eRequest & E_SERVICE) == E_SERVICE);
+ if (bSet)
+ sService = m_sService;
+
+ return bSet;
+}
+
+/**
+ @short searches for a special identifier in the given string and split it
+ @descr If the given identifier could be found at the beginning of the given string,
+ this method split it into different parts and return it.
+ Following schema is used: <partidentifier>=<partvalue>[?<partarguments>]
+
+ @param sPart
+ the string, which should be analyzed
+
+ @param pPartIdentifier
+ the part identifier value, which must be found at the beginning of the
+ parameter <var>sPart</var>
+
+ @param nPartLength
+ the length of the ascii value <var>pPartIdentifier</var>
+
+ @param rPartValue
+ returns the part value if <var>sPart</var> was split successfully
+
+ @param rPartArguments
+ returns the part arguments if <var>sPart</var> was split successfully
+
+ @return <TRUE/> if the identifier could be found and the string was split.
+ <FALSE/> otherwise.
+*/
+bool JobURL::implst_split( /*IN*/ const OUString& sPart ,
+ /*IN*/ const char* pPartIdentifier ,
+ /*IN*/ sal_Int32 nPartLength ,
+ /*OUT*/ OUString& rPartValue ,
+ /*OUT*/ OUString& rPartArguments )
+{
+ // first search for the given identifier
+ bool bPartFound = sPart.matchIgnoreAsciiCaseAsciiL(pPartIdentifier,nPartLength);
+
+ // If it exist - we can split the part and return sal_True.
+ // Otherwise we do nothing and return sal_False.
+ if (bPartFound)
+ {
+ // But may the part has optional arguments - separated by a "?".
+ // Do so - we set the return value with the whole part string.
+ // Arguments will be set to an empty string as default.
+ // If we detect the right sign - we split the arguments and overwrite the default.
+ OUString sValueAndArguments = sPart.copy(nPartLength);
+ OUString sValue = sValueAndArguments;
+ OUString sArguments;
+
+ sal_Int32 nArgStart = sValueAndArguments.indexOf('?');
+ if (nArgStart!=-1)
+ {
+ sValue = sValueAndArguments.copy(0,nArgStart);
+ ++nArgStart; // ignore '?'!
+ sArguments = sValueAndArguments.copy(nArgStart);
+ }
+
+ rPartValue = sValue;
+ rPartArguments = sArguments;
+ }
+
+ return bPartFound;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/jobs/shelljob.cxx b/framework/source/jobs/shelljob.cxx
new file mode 100644
index 000000000..0c895db33
--- /dev/null
+++ b/framework/source/jobs/shelljob.cxx
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// include own header
+
+#include <jobs/shelljob.hxx>
+#include <jobs/jobconst.hxx>
+#include <services.h>
+
+// include others
+
+#include <osl/process.h>
+#include <comphelper/sequenceashashmap.hxx>
+
+// include interfaces
+
+#include <com/sun/star/util/PathSubstitution.hpp>
+#include <com/sun/star/util/XStringSubstitution.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <utility>
+
+namespace framework{
+
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL ShellJob::getImplementationName()
+{
+ return "com.sun.star.comp.framework.ShellJob";
+}
+
+sal_Bool SAL_CALL ShellJob::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL ShellJob::getSupportedServiceNames()
+{
+ return { SERVICENAME_JOB };
+}
+
+
+ShellJob::ShellJob(css::uno::Reference< css::uno::XComponentContext > xContext)
+ : m_xContext (std::move(xContext))
+{
+}
+
+ShellJob::~ShellJob()
+{
+}
+
+css::uno::Any SAL_CALL ShellJob::execute(const css::uno::Sequence< css::beans::NamedValue >& lJobArguments)
+{
+ ::comphelper::SequenceAsHashMap lArgs (lJobArguments);
+ /** address job configuration inside argument set provided on method execute(). */
+ ::comphelper::SequenceAsHashMap lOwnCfg(lArgs.getUnpackedValueOrDefault("JobConfig", css::uno::Sequence< css::beans::NamedValue >()));
+
+ const OUString sCommand = lOwnCfg.getUnpackedValueOrDefault("Command" , OUString());
+ const css::uno::Sequence< OUString > lCommandArguments = lOwnCfg.getUnpackedValueOrDefault("Arguments" , css::uno::Sequence< OUString >());
+ const bool bDeactivateJobIfDone = lOwnCfg.getUnpackedValueOrDefault("DeactivateJobIfDone" , true );
+ const bool bCheckExitCode = lOwnCfg.getUnpackedValueOrDefault("CheckExitCode" , true );
+
+ // replace all might existing place holder.
+ OUString sRealCommand = impl_substituteCommandVariables(sCommand);
+
+ // Command is required as minimum.
+ // If it does not exists ... we can't do our job.
+ // Deactivate such miss configured job silently .-)
+ if (sRealCommand.isEmpty())
+ return ShellJob::impl_generateAnswer4Deactivation();
+
+ // do it
+ bool bDone = impl_execute(sRealCommand, lCommandArguments, bCheckExitCode);
+ if (! bDone)
+ return css::uno::Any();
+
+ // Job was done ... user configured deactivation of this job
+ // in such case.
+ if (bDeactivateJobIfDone)
+ return ShellJob::impl_generateAnswer4Deactivation();
+
+ // There was no decision about deactivation of this job.
+ // So we have to return nothing here !
+ return css::uno::Any();
+}
+
+css::uno::Any ShellJob::impl_generateAnswer4Deactivation()
+{
+ css::uno::Sequence< css::beans::NamedValue > aAnswer { { JobConst::ANSWER_DEACTIVATE_JOB, css::uno::Any(true) } };
+ return css::uno::Any(aAnswer);
+}
+
+OUString ShellJob::impl_substituteCommandVariables(const OUString& sCommand)
+{
+ try
+ {
+ css::uno::Reference< css::util::XStringSubstitution > xSubst( css::util::PathSubstitution::create(m_xContext) );
+ const bool bSubstRequired = true;
+ const OUString sCompleteCommand = xSubst->substituteVariables(sCommand, bSubstRequired);
+
+ return sCompleteCommand;
+ }
+ catch(const css::uno::Exception&)
+ {}
+
+ return OUString();
+}
+
+bool ShellJob::impl_execute(const OUString& sCommand ,
+ const css::uno::Sequence< OUString >& lArguments ,
+ bool bCheckExitCode)
+{
+ ::rtl_uString** pArgs = nullptr;
+ const ::sal_Int32 nArgs = lArguments.getLength ();
+ oslProcess hProcess(nullptr);
+
+ if (nArgs > 0)
+ pArgs = reinterpret_cast< ::rtl_uString** >(const_cast< OUString* >(lArguments.getConstArray()));
+
+ oslProcessError eError = osl_executeProcess(sCommand.pData, pArgs, nArgs, osl_Process_WAIT, nullptr, nullptr, nullptr, 0, &hProcess);
+
+ // executable not found or couldn't be started
+ if (eError != osl_Process_E_None)
+ return false;
+
+ bool bRet = true;
+ if (bCheckExitCode)
+ {
+ // check its return codes ...
+ oslProcessInfo aInfo;
+ aInfo.Size = sizeof (oslProcessInfo);
+ eError = osl_getProcessInfo(hProcess, osl_Process_EXITCODE, &aInfo);
+
+ if (eError != osl_Process_E_None)
+ bRet = false;
+ else
+ bRet = (aInfo.Code == 0);
+ }
+ osl_freeProcessHandle(hProcess);
+ return bRet;
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_ShellJob_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::ShellJob(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/layoutmanager/helpers.cxx b/framework/source/layoutmanager/helpers.cxx
new file mode 100644
index 000000000..7508d58cb
--- /dev/null
+++ b/framework/source/layoutmanager/helpers.cxx
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "helpers.hxx"
+
+#include <com/sun/star/ui/DockingArea.hpp>
+#include <com/sun/star/awt/Toolkit.hpp>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/frame/DispatchHelper.hpp>
+#include <com/sun/star/awt/XDockableWindow.hpp>
+#include <com/sun/star/awt/XDockableWindowListener.hpp>
+#include <com/sun/star/awt/XWindowListener.hpp>
+#include <com/sun/star/ui/XUIElement.hpp>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <vcl/svapp.hxx>
+#include <o3tl/string_view.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+using namespace com::sun::star;
+
+namespace framework
+{
+
+bool hasEmptySize( const css::awt::Size& rSize )
+{
+ return ( rSize.Width == 0 ) && ( rSize.Height == 0 );
+}
+
+bool hasDefaultPosValue( const css::awt::Point& rPos )
+{
+ return (( rPos.X == SAL_MAX_INT32 ) || ( rPos.Y == SAL_MAX_INT32 ));
+}
+
+bool isDefaultPos( const css::awt::Point& rPos )
+{
+ return (( rPos.X == SAL_MAX_INT32 ) && ( rPos.Y == SAL_MAX_INT32 ));
+}
+
+bool isReverseOrderDockingArea( const sal_Int32 nDockArea )
+{
+ ui::DockingArea eDockArea = static_cast< ui::DockingArea >( nDockArea );
+ return (( eDockArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) ||
+ ( eDockArea == ui::DockingArea_DOCKINGAREA_RIGHT ));
+}
+
+bool isToolboxHorizontalAligned( ToolBox const * pToolBox )
+{
+ if ( pToolBox )
+ return (( pToolBox->GetAlign() == WindowAlign::Top ) || ( pToolBox->GetAlign() == WindowAlign::Bottom ));
+ return false;
+}
+
+bool isHorizontalDockingArea( const ui::DockingArea& nDockingArea )
+{
+ return (( nDockingArea == ui::DockingArea_DOCKINGAREA_TOP ) ||
+ ( nDockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM ));
+}
+
+bool isHorizontalDockingArea( const sal_Int32 nDockArea )
+{
+ return isHorizontalDockingArea(static_cast< ui::DockingArea >( nDockArea ));
+}
+
+OUString retrieveToolbarNameFromHelpURL( vcl::Window* pWindow )
+{
+ OUString aToolbarName;
+
+ if ( pWindow->GetType() == WindowType::TOOLBOX )
+ {
+ ToolBox* pToolBox = dynamic_cast<ToolBox *>( pWindow );
+ if ( pToolBox )
+ {
+ aToolbarName = OStringToOUString( pToolBox->GetHelpId(), RTL_TEXTENCODING_UTF8 );
+ sal_Int32 i = aToolbarName.lastIndexOf( ':' );
+ if ( !aToolbarName.isEmpty() && ( i > 0 ) && (( i + 1 ) < aToolbarName.getLength() ))
+ aToolbarName = aToolbarName.copy( i+1 ); // Remove ".HelpId:" protocol from toolbar name
+ else
+ aToolbarName.clear();
+ }
+ }
+ return aToolbarName;
+}
+
+ToolBox* getToolboxPtr( vcl::Window* pWindow )
+{
+ ToolBox* pToolbox(nullptr);
+ if ( pWindow->GetType() == WindowType::TOOLBOX )
+ pToolbox = dynamic_cast<ToolBox*>( pWindow );
+ return pToolbox;
+}
+
+vcl::Window* getWindowFromXUIElement( const uno::Reference< ui::XUIElement >& xUIElement )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< awt::XWindow > xWindow;
+ if ( xUIElement.is() )
+ xWindow.set( xUIElement->getRealInterface(), uno::UNO_QUERY );
+ return VCLUnoHelper::GetWindow( xWindow );
+}
+
+SystemWindow* getTopSystemWindow( const uno::Reference< awt::XWindow >& xWindow )
+{
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ while ( pWindow && !pWindow->IsSystemWindow() )
+ pWindow = pWindow->GetParent();
+
+ if ( pWindow )
+ return static_cast<SystemWindow *>(pWindow.get());
+ else
+ return nullptr;
+}
+
+// ATTENTION!
+// This value is directly copied from the sfx2 project.
+// You have to change BOTH values, see sfx2/inc/sfx2/sfxsids.hrc (SID_DOCKWIN_START)
+const sal_Int32 DOCKWIN_ID_BASE = 9800;
+
+bool lcl_checkUIElement(const uno::Reference< ui::XUIElement >& xUIElement, awt::Rectangle& _rPosSize, uno::Reference< awt::XWindow >& _xWindow)
+{
+ bool bRet = xUIElement.is();
+ if ( bRet )
+ {
+ SolarMutexGuard aGuard;
+ _xWindow.set( xUIElement->getRealInterface(), uno::UNO_QUERY );
+ _rPosSize = _xWindow->getPosSize();
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( _xWindow );
+ if ( pWindow->GetType() == WindowType::TOOLBOX )
+ {
+ ::Size aSize = static_cast<ToolBox*>(pWindow.get())->CalcWindowSizePixel( 1 );
+ _rPosSize.Width = aSize.Width();
+ _rPosSize.Height = aSize.Height();
+ }
+ } // if ( xUIElement.is() )
+ return bRet;
+}
+
+uno::Reference< awt::XWindowPeer > createToolkitWindow( const uno::Reference< uno::XComponentContext >& rxContext, const uno::Reference< awt::XWindowPeer >& rParent, const char* pService )
+{
+ uno::Reference< awt::XToolkit2 > xToolkit = awt::Toolkit::create( rxContext );
+
+ // describe window properties.
+ css::awt::WindowDescriptor aDescriptor;
+ aDescriptor.Type = awt::WindowClass_SIMPLE;
+ aDescriptor.WindowServiceName = OUString::createFromAscii( pService );
+ aDescriptor.ParentIndex = -1;
+ aDescriptor.Parent = rParent;
+ aDescriptor.Bounds = awt::Rectangle(0,0,0,0);
+ aDescriptor.WindowAttributes = 0;
+
+ // create an awt window
+ uno::Reference< awt::XWindowPeer > xPeer = xToolkit->createWindow( aDescriptor );
+
+ return xPeer;
+}
+
+// convert alignment constant to vcl's WindowAlign type
+WindowAlign ImplConvertAlignment( ui::DockingArea aAlignment )
+{
+ if ( aAlignment == ui::DockingArea_DOCKINGAREA_LEFT )
+ return WindowAlign::Left;
+ else if ( aAlignment == ui::DockingArea_DOCKINGAREA_RIGHT )
+ return WindowAlign::Right;
+ else if ( aAlignment == ui::DockingArea_DOCKINGAREA_TOP )
+ return WindowAlign::Top;
+ else
+ return WindowAlign::Bottom;
+}
+
+std::u16string_view getElementTypeFromResourceURL( std::u16string_view aResourceURL )
+{
+ if ( o3tl::starts_with(aResourceURL, UIRESOURCE_URL ) )
+ {
+ sal_Int32 nIndex{ UIRESOURCE_URL.getLength() };
+ return o3tl::getToken(aResourceURL, 1, '/', nIndex );
+ }
+
+ return std::u16string_view();
+}
+
+void parseResourceURL( std::u16string_view aResourceURL, OUString& aElementType, OUString& aElementName )
+{
+ if ( o3tl::starts_with(aResourceURL, UIRESOURCE_URL) )
+ {
+ sal_Int32 nIndex{ UIRESOURCE_URL.getLength() };
+ aElementType = o3tl::getToken(aResourceURL, 1, '/', nIndex );
+ aElementName = o3tl::getToken(aResourceURL, 0, '/', nIndex );
+ }
+}
+
+css::awt::Rectangle putRectangleValueToAWT( const ::tools::Rectangle& rRect )
+{
+ css::awt::Rectangle aRect;
+ aRect.X = rRect.Left();
+ aRect.Y = rRect.Top();
+ aRect.Width = rRect.Right();
+ aRect.Height = rRect.Bottom();
+
+ return aRect;
+}
+
+::tools::Rectangle putAWTToRectangle( const css::awt::Rectangle& rRect )
+{
+ ::tools::Rectangle aRect;
+ aRect.SetLeft( rRect.X );
+ aRect.SetTop( rRect.Y );
+ aRect.SetRight( rRect.Width );
+ aRect.SetBottom( rRect.Height );
+
+ return aRect;
+}
+
+bool equalRectangles( const css::awt::Rectangle& rRect1,
+ const css::awt::Rectangle& rRect2 )
+{
+ return (( rRect1.X == rRect2.X ) &&
+ ( rRect1.Y == rRect2.Y ) &&
+ ( rRect1.Width == rRect2.Width ) &&
+ ( rRect1.Height == rRect2.Height ));
+}
+
+uno::Reference< frame::XModel > impl_getModelFromFrame( const uno::Reference< frame::XFrame >& rFrame )
+{
+ // Query for the model to get check the context information
+ uno::Reference< frame::XModel > xModel;
+ if ( rFrame.is() )
+ {
+ uno::Reference< frame::XController > xController = rFrame->getController();
+ if ( xController.is() )
+ xModel = xController->getModel();
+ }
+
+ return xModel;
+}
+
+bool implts_isPreviewModel( const uno::Reference< frame::XModel >& xModel )
+{
+ // the cost in calc of calling getArgs for this property
+ // includes measuring the entire sheet - which is extremely slow.
+ if (comphelper::LibreOfficeKit::isActive())
+ return false;
+
+ if ( xModel.is() )
+ {
+ utl::MediaDescriptor aDesc( xModel->getArgs() );
+ return aDesc.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW, false);
+ }
+ else
+ return false;
+}
+
+bool implts_isFrameOrWindowTop( const uno::Reference< frame::XFrame >& xFrame )
+{
+ if (xFrame->isTop())
+ return true;
+
+ uno::Reference< awt::XTopWindow > xWindowCheck(xFrame->getContainerWindow(), uno::UNO_QUERY); // don't use _THROW here ... it's a check only
+ if (xWindowCheck.is())
+ {
+ // #i76867# top and system window is required.
+ SolarMutexGuard aGuard;
+ uno::Reference< awt::XWindow > xWindow( xWindowCheck, uno::UNO_QUERY );
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ return pWindow && pWindow->IsSystemWindow();
+ }
+
+ return false;
+}
+
+void impl_setDockingWindowVisibility( const css::uno::Reference< css::uno::XComponentContext>& rxContext, const css::uno::Reference< css::frame::XFrame >& rFrame, std::u16string_view rDockingWindowName, bool bVisible )
+{
+ sal_Int32 nID = o3tl::toInt32(rDockingWindowName);
+ sal_Int32 nIndex = nID - DOCKWIN_ID_BASE;
+
+ css::uno::Reference< css::frame::XDispatchProvider > xProvider(rFrame, css::uno::UNO_QUERY);
+ if ( !(nIndex >= 0 && xProvider.is()) )
+ return;
+
+ OUString aDockWinArgName = "DockingWindow" + OUString::number( nIndex );
+
+ css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
+ aDockWinArgName, bVisible) };
+
+ css::uno::Reference< css::frame::XDispatchHelper > xDispatcher = css::frame::DispatchHelper::create( rxContext );
+
+ OUString aDockWinCommand = ".uno:" + aDockWinArgName;
+ xDispatcher->executeDispatch(
+ xProvider,
+ aDockWinCommand,
+ "_self",
+ 0,
+ aArgs);
+}
+
+void impl_addWindowListeners(
+ const css::uno::Reference< css::uno::XInterface >& xThis,
+ const css::uno::Reference< css::ui::XUIElement >& xUIElement )
+{
+ css::uno::Reference< css::awt::XWindow > xWindow( xUIElement->getRealInterface(), css::uno::UNO_QUERY );
+ css::uno::Reference< css::awt::XDockableWindow > xDockWindow( xUIElement->getRealInterface(), css::uno::UNO_QUERY );
+ if ( !(xDockWindow.is() && xWindow.is()) )
+ return;
+
+ try
+ {
+ xDockWindow->addDockableWindowListener(
+ css::uno::Reference< css::awt::XDockableWindowListener >(
+ xThis, css::uno::UNO_QUERY ));
+ xWindow->addWindowListener(
+ css::uno::Reference< css::awt::XWindowListener >(
+ xThis, css::uno::UNO_QUERY ));
+ xDockWindow->enableDocking( true );
+ }
+ catch ( const css::uno::Exception& )
+ {
+ }
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/layoutmanager/helpers.hxx b/framework/source/layoutmanager/helpers.hxx
new file mode 100644
index 000000000..14f9642a3
--- /dev/null
+++ b/framework/source/layoutmanager/helpers.hxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/ui/XUIElement.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/ui/DockingArea.hpp>
+#include <com/sun/star/awt/Point.hpp>
+
+#include <vcl/window.hxx>
+#include <vcl/toolbox.hxx>
+
+inline constexpr OUStringLiteral UIRESOURCE_URL = u"private:resource";
+inline constexpr OUStringLiteral UIRESOURCETYPE_TOOLBAR = u"toolbar";
+#define UIRESOURCETYPE_MENUBAR "menubar"
+
+namespace framework
+{
+
+bool hasEmptySize( const css::awt::Size& rSize );
+bool hasDefaultPosValue( const css::awt::Point& rPos );
+bool isDefaultPos( const css::awt::Point& rPos );
+bool isToolboxHorizontalAligned( ToolBox const * pToolBox );
+bool isReverseOrderDockingArea( const sal_Int32 nDockArea );
+bool isHorizontalDockingArea( const sal_Int32 nDockArea );
+bool isHorizontalDockingArea( const css::ui::DockingArea& nDockArea );
+OUString retrieveToolbarNameFromHelpURL( vcl::Window* pWindow );
+ToolBox* getToolboxPtr( vcl::Window* pWindow );
+vcl::Window* getWindowFromXUIElement( const css::uno::Reference< css::ui::XUIElement >& xUIElement );
+SystemWindow* getTopSystemWindow( const css::uno::Reference< css::awt::XWindow >& xWindow );
+bool equalRectangles( const css::awt::Rectangle& rRect1, const css::awt::Rectangle& rRect2 );
+bool lcl_checkUIElement(const css::uno::Reference< css::ui::XUIElement >& xUIElement,css::awt::Rectangle& _rPosSize, css::uno::Reference< css::awt::XWindow >& _xWindow);
+css::uno::Reference< css::awt::XWindowPeer > createToolkitWindow( const css::uno::Reference< css::uno::XComponentContext >& rxContext, const css::uno::Reference< css::awt::XWindowPeer >& rParent, const char* pService );
+WindowAlign ImplConvertAlignment( css::ui::DockingArea aAlignment );
+std::u16string_view getElementTypeFromResourceURL( std::u16string_view aResourceURL );
+void parseResourceURL( std::u16string_view aResourceURL, OUString& aElementType, OUString& aElementName );
+::tools::Rectangle putAWTToRectangle( const css::awt::Rectangle& rRect );
+css::awt::Rectangle putRectangleValueToAWT( const ::tools::Rectangle& rRect );
+css::uno::Reference< css::frame::XModel > impl_getModelFromFrame( const css::uno::Reference< css::frame::XFrame >& rFrame );
+bool implts_isPreviewModel( const css::uno::Reference< css::frame::XModel >& xModel );
+bool implts_isFrameOrWindowTop( const css::uno::Reference< css::frame::XFrame >& xFrame );
+void impl_setDockingWindowVisibility( const css::uno::Reference< css::uno::XComponentContext>& rxContext, const css::uno::Reference< css::frame::XFrame >& rFrame, std::u16string_view rDockingWindowName, bool bVisible );
+void impl_addWindowListeners( const css::uno::Reference< css::uno::XInterface >& xThis, const css::uno::Reference< css::ui::XUIElement >& xUIElement );
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/layoutmanager/layoutmanager.cxx b/framework/source/layoutmanager/layoutmanager.cxx
new file mode 100644
index 000000000..9220b805c
--- /dev/null
+++ b/framework/source/layoutmanager/layoutmanager.cxx
@@ -0,0 +1,3079 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <config_feature_desktop.h>
+
+#include <properties.h>
+#include <services/layoutmanager.hxx>
+#include "helpers.hxx"
+
+#include <framework/sfxhelperfunctions.hxx>
+#include <uielement/menubarwrapper.hxx>
+#include <uielement/progressbarwrapper.hxx>
+#include <uiconfiguration/globalsettings.hxx>
+#include <uiconfiguration/windowstateproperties.hxx>
+#include "toolbarlayoutmanager.hxx"
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/FrameAction.hpp>
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/awt/XDevice.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/theWindowStateConfiguration.hpp>
+#include <com/sun/star/ui/theUIElementFactoryManager.hpp>
+#include <com/sun/star/container/XNameReplace.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/frame/LayoutManagerEvents.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/DispatchHelper.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/status.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <comphelper/uno3.hxx>
+#include <officecfg/Office/Compatibility.hxx>
+
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <algorithm>
+
+// using namespace
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::ui;
+using namespace ::com::sun::star::frame;
+
+constexpr OUStringLiteral STATUS_BAR_ALIAS = u"private:resource/statusbar/statusbar";
+
+namespace framework
+{
+
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( LayoutManager, LayoutManager_Base, LayoutManager_PBase )
+IMPLEMENT_FORWARD_XINTERFACE2( LayoutManager, LayoutManager_Base, LayoutManager_PBase )
+
+LayoutManager::LayoutManager( const Reference< XComponentContext >& xContext ) :
+ ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType >(m_aMutex)
+ , LayoutManager_PBase( *static_cast< ::cppu::OBroadcastHelper* >(this) )
+ , m_xContext( xContext )
+ , m_xURLTransformer( URLTransformer::create(xContext) )
+ , m_nLockCount( 0 )
+ , m_bInplaceMenuSet( false )
+ , m_bMenuVisible( true )
+ , m_bVisible( true )
+ , m_bParentWindowVisible( false )
+ , m_bMustDoLayout( true )
+#if HAVE_FEATURE_DESKTOP
+ , m_bAutomaticToolbars( true )
+#else
+ , m_bAutomaticToolbars( false )
+#endif
+ , m_bHideCurrentUI( false )
+ , m_bGlobalSettings( false )
+ , m_bPreserveContentSize( false )
+ , m_bMenuBarCloseButton( false )
+ , m_xModuleManager( ModuleManager::create( xContext ))
+ , m_xUIElementFactoryManager( ui::theUIElementFactoryManager::get(xContext) )
+ , m_xPersistentWindowStateSupplier( ui::theWindowStateConfiguration::get( xContext ) )
+ , m_aAsyncLayoutTimer( "framework::LayoutManager m_aAsyncLayoutTimer" )
+ , m_aListenerContainer( m_aMutex )
+{
+ // Initialize statusbar member
+ m_aStatusBarElement.m_aType = "statusbar";
+ m_aStatusBarElement.m_aName = STATUS_BAR_ALIAS;
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ m_xToolbarManager = new ToolbarLayoutManager( xContext, Reference<XUIElementFactory>(m_xUIElementFactoryManager, UNO_QUERY_THROW), this );
+ }
+
+ m_aAsyncLayoutTimer.SetPriority( TaskPriority::HIGH_IDLE );
+ m_aAsyncLayoutTimer.SetTimeout( 50 );
+ m_aAsyncLayoutTimer.SetInvokeHandler( LINK( this, LayoutManager, AsyncLayoutHdl ) );
+
+ registerProperty( LAYOUTMANAGER_PROPNAME_ASCII_AUTOMATICTOOLBARS, LAYOUTMANAGER_PROPHANDLE_AUTOMATICTOOLBARS, css::beans::PropertyAttribute::TRANSIENT, &m_bAutomaticToolbars, cppu::UnoType<decltype(m_bAutomaticToolbars)>::get() );
+ registerProperty( LAYOUTMANAGER_PROPNAME_ASCII_HIDECURRENTUI, LAYOUTMANAGER_PROPHANDLE_HIDECURRENTUI, beans::PropertyAttribute::TRANSIENT, &m_bHideCurrentUI, cppu::UnoType<decltype(m_bHideCurrentUI)>::get() );
+ registerProperty( LAYOUTMANAGER_PROPNAME_ASCII_LOCKCOUNT, LAYOUTMANAGER_PROPHANDLE_LOCKCOUNT, beans::PropertyAttribute::TRANSIENT | beans::PropertyAttribute::READONLY, &m_nLockCount, cppu::UnoType<decltype(m_nLockCount)>::get() );
+ registerProperty( LAYOUTMANAGER_PROPNAME_MENUBARCLOSER, LAYOUTMANAGER_PROPHANDLE_MENUBARCLOSER, beans::PropertyAttribute::TRANSIENT, &m_bMenuBarCloseButton, cppu::UnoType<decltype(m_bMenuBarCloseButton)>::get() );
+ registerPropertyNoMember( LAYOUTMANAGER_PROPNAME_ASCII_REFRESHVISIBILITY, LAYOUTMANAGER_PROPHANDLE_REFRESHVISIBILITY, beans::PropertyAttribute::TRANSIENT, cppu::UnoType<bool>::get(), css::uno::Any(false) );
+ registerProperty( LAYOUTMANAGER_PROPNAME_ASCII_PRESERVE_CONTENT_SIZE, LAYOUTMANAGER_PROPHANDLE_PRESERVE_CONTENT_SIZE, beans::PropertyAttribute::TRANSIENT, &m_bPreserveContentSize, cppu::UnoType<decltype(m_bPreserveContentSize)>::get() );
+}
+
+LayoutManager::~LayoutManager()
+{
+ m_aAsyncLayoutTimer.Stop();
+ setDockingAreaAcceptor(nullptr);
+ m_pGlobalSettings.reset();
+}
+
+void LayoutManager::implts_createMenuBar(const OUString& rMenuBarName)
+{
+ SolarMutexGuard aWriteLock;
+
+ // Create a customized menu if compatibility mode is on
+ if (m_aModuleIdentifier == "com.sun.star.text.TextDocument" && officecfg::Office::Compatibility::View::MSCompatibleFormsMenu::get())
+ {
+ implts_createMSCompatibleMenuBar(rMenuBarName);
+ }
+
+ // Create the default menubar otherwise
+ if (m_bInplaceMenuSet || m_xMenuBar.is())
+ return;
+
+ m_xMenuBar = implts_createElement( rMenuBarName );
+ if ( !m_xMenuBar.is() )
+ return;
+
+ SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow );
+ if ( !pSysWindow )
+ return;
+
+ Reference< awt::XMenuBar > xMenuBar;
+
+ Reference< XPropertySet > xPropSet( m_xMenuBar, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ xPropSet->getPropertyValue("XMenuBar") >>= xMenuBar;
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ }
+ catch (const lang::WrappedTargetException&)
+ {
+ }
+ }
+
+ if ( !xMenuBar.is() )
+ return;
+
+ VCLXMenu* pAwtMenuBar = comphelper::getFromUnoTunnel<VCLXMenu>( xMenuBar );
+ if ( pAwtMenuBar )
+ {
+ MenuBar* pMenuBar = static_cast<MenuBar*>(pAwtMenuBar->GetMenu());
+ if ( pMenuBar )
+ {
+ pSysWindow->SetMenuBar(pMenuBar);
+ pMenuBar->SetDisplayable( m_bMenuVisible );
+ implts_updateMenuBarClose();
+ }
+ }
+}
+
+// Internal helper function
+void LayoutManager::impl_clearUpMenuBar()
+{
+ implts_lock();
+
+ // Clear up VCL menu bar to prepare shutdown
+ if ( m_xContainerWindow.is() )
+ {
+ SolarMutexGuard aGuard;
+
+ SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow );
+ if ( pSysWindow )
+ {
+ MenuBar* pSetMenuBar = nullptr;
+ if ( m_xInplaceMenuBar.is() )
+ pSetMenuBar = static_cast<MenuBar *>(m_xInplaceMenuBar->GetMenuBar());
+ else
+ {
+ Reference< awt::XMenuBar > xMenuBar;
+
+ Reference< XPropertySet > xPropSet( m_xMenuBar, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ xPropSet->getPropertyValue("XMenuBar") >>= xMenuBar;
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ }
+ catch (const lang::WrappedTargetException&)
+ {
+ }
+ }
+
+ VCLXMenu* pAwtMenuBar = comphelper::getFromUnoTunnel<VCLXMenu>( xMenuBar );
+ if ( pAwtMenuBar )
+ pSetMenuBar = static_cast<MenuBar*>(pAwtMenuBar->GetMenu());
+ }
+
+ MenuBar* pTopMenuBar = pSysWindow->GetMenuBar();
+ if ( pSetMenuBar == pTopMenuBar )
+ pSysWindow->SetMenuBar( nullptr );
+ }
+ }
+
+ // reset inplace menubar manager
+ VclPtr<Menu> pMenuBar;
+ if (m_xInplaceMenuBar.is())
+ {
+ pMenuBar = m_xInplaceMenuBar->GetMenuBar();
+ m_xInplaceMenuBar->dispose();
+ m_xInplaceMenuBar.clear();
+ }
+ pMenuBar.disposeAndClear();
+ m_bInplaceMenuSet = false;
+
+ Reference< XComponent > xComp( m_xMenuBar, UNO_QUERY );
+ if ( xComp.is() )
+ xComp->dispose();
+ m_xMenuBar.clear();
+ implts_unlock();
+}
+
+void LayoutManager::implts_lock()
+{
+ SolarMutexGuard g;
+ ++m_nLockCount;
+}
+
+bool LayoutManager::implts_unlock()
+{
+ SolarMutexGuard g;
+ m_nLockCount = std::max( m_nLockCount-1, static_cast<sal_Int32>(0) );
+ return ( m_nLockCount == 0 );
+}
+
+void LayoutManager::implts_reset( bool bAttached )
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexClearableGuard aReadLock;
+ Reference< XFrame > xFrame = m_xFrame;
+ Reference< awt::XWindow > xContainerWindow( m_xContainerWindow );
+ Reference< XUIConfiguration > xModuleCfgMgr( m_xModuleCfgMgr, UNO_QUERY );
+ Reference< XUIConfiguration > xDocCfgMgr( m_xDocCfgMgr, UNO_QUERY );
+ Reference< XNameAccess > xPersistentWindowState( m_xPersistentWindowState );
+ Reference< XComponentContext > xContext( m_xContext );
+ Reference< XNameAccess > xPersistentWindowStateSupplier( m_xPersistentWindowStateSupplier );
+ rtl::Reference<ToolbarLayoutManager> xToolbarManager( m_xToolbarManager );
+ OUString aModuleIdentifier( m_aModuleIdentifier );
+ bool bAutomaticToolbars( m_bAutomaticToolbars );
+ aReadLock.clear();
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ implts_lock();
+
+ Reference< XModel > xModel;
+ if ( xFrame.is() )
+ {
+ if ( bAttached )
+ {
+ OUString aOldModuleIdentifier( aModuleIdentifier );
+ try
+ {
+ aModuleIdentifier = m_xModuleManager->identify( xFrame );
+ }
+ catch( const Exception& ) {}
+
+ if ( !aModuleIdentifier.isEmpty() && aOldModuleIdentifier != aModuleIdentifier )
+ {
+ Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgSupplier;
+ if ( xContext.is() )
+ xModuleCfgSupplier = theModuleUIConfigurationManagerSupplier::get( xContext );
+
+ if ( xModuleCfgMgr.is() )
+ {
+ try
+ {
+ // Remove listener to old module ui configuration manager
+ xModuleCfgMgr->removeConfigurationListener( Reference< XUIConfigurationListener >(this) );
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ try
+ {
+ // Add listener to new module ui configuration manager
+ xModuleCfgMgr.set( xModuleCfgSupplier->getUIConfigurationManager( aModuleIdentifier ), UNO_QUERY );
+ if ( xModuleCfgMgr.is() )
+ xModuleCfgMgr->addConfigurationListener( Reference< XUIConfigurationListener >(this) );
+ }
+ catch (const Exception&)
+ {
+ }
+
+ try
+ {
+ // Retrieve persistent window state reference for our new module
+ if ( xPersistentWindowStateSupplier.is() )
+ xPersistentWindowStateSupplier->getByName( aModuleIdentifier ) >>= xPersistentWindowState;
+ }
+ catch (const NoSuchElementException&)
+ {
+ }
+ catch (const WrappedTargetException&)
+ {
+ }
+ }
+
+ xModel = impl_getModelFromFrame( xFrame );
+ if ( xModel.is() )
+ {
+ Reference< XUIConfigurationManagerSupplier > xUIConfigurationManagerSupplier( xModel, UNO_QUERY );
+ if ( xUIConfigurationManagerSupplier.is() )
+ {
+ if ( xDocCfgMgr.is() )
+ {
+ try
+ {
+ // Remove listener to old ui configuration manager
+ xDocCfgMgr->removeConfigurationListener( Reference< XUIConfigurationListener >(this) );
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ try
+ {
+ xDocCfgMgr.set( xUIConfigurationManagerSupplier->getUIConfigurationManager(), UNO_QUERY );
+ if ( xDocCfgMgr.is() )
+ xDocCfgMgr->addConfigurationListener( Reference< XUIConfigurationListener >(this) );
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+ }
+ }
+ else
+ {
+ // Remove configuration listeners before we can release our references
+ if ( xModuleCfgMgr.is() )
+ {
+ try
+ {
+ xModuleCfgMgr->removeConfigurationListener(
+ Reference< XUIConfigurationListener >(this) );
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ if ( xDocCfgMgr.is() )
+ {
+ try
+ {
+ xDocCfgMgr->removeConfigurationListener(
+ Reference< XUIConfigurationListener >(this) );
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ // Release references to our configuration managers as we currently don't have
+ // an attached module.
+ xModuleCfgMgr.clear();
+ xDocCfgMgr.clear();
+ xPersistentWindowState.clear();
+ aModuleIdentifier.clear();
+ }
+
+ Reference< XUIConfigurationManager > xModCfgMgr( xModuleCfgMgr, UNO_QUERY );
+ Reference< XUIConfigurationManager > xDokCfgMgr( xDocCfgMgr, UNO_QUERY );
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexClearableGuard aWriteLock;
+ m_aDockingArea = awt::Rectangle();
+ m_aModuleIdentifier = aModuleIdentifier;
+ m_xModuleCfgMgr = xModCfgMgr;
+ m_xDocCfgMgr = xDokCfgMgr;
+ m_xPersistentWindowState = xPersistentWindowState;
+ m_aStatusBarElement.m_bStateRead = false; // reset state to read data again!
+ aWriteLock.clear();
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ // reset/notify toolbar layout manager
+ if ( xToolbarManager.is() )
+ {
+ if ( bAttached )
+ {
+ xToolbarManager->attach( xFrame, xModCfgMgr, xDokCfgMgr, xPersistentWindowState );
+ uno::Reference< awt::XWindowPeer > xParent( xContainerWindow, UNO_QUERY );
+ xToolbarManager->setParentWindow( xParent );
+ if ( bAutomaticToolbars )
+ xToolbarManager->createStaticToolbars();
+ }
+ else
+ {
+ xToolbarManager->reset();
+ implts_destroyElements();
+ }
+ }
+ }
+
+ implts_unlock();
+}
+
+bool LayoutManager::implts_isEmbeddedLayoutManager() const
+{
+ SolarMutexClearableGuard aReadLock;
+ Reference< XFrame > xFrame = m_xFrame;
+ Reference< awt::XWindow > xContainerWindow( m_xContainerWindow );
+ aReadLock.clear();
+
+ Reference< awt::XWindow > xFrameContainerWindow = xFrame->getContainerWindow();
+ return xFrameContainerWindow != xContainerWindow;
+}
+
+void LayoutManager::implts_destroyElements()
+{
+ SolarMutexResettableGuard aWriteLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aWriteLock.clear();
+
+ if ( pToolbarManager )
+ pToolbarManager->destroyToolbars();
+
+ implts_destroyStatusBar();
+
+ aWriteLock.reset();
+ impl_clearUpMenuBar();
+ aWriteLock.clear();
+}
+
+void LayoutManager::implts_toggleFloatingUIElementsVisibility( bool bActive )
+{
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ pToolbarManager->setFloatingToolbarsVisibility( bActive );
+}
+
+uno::Reference< ui::XUIElement > LayoutManager::implts_findElement( std::u16string_view aName )
+{
+ OUString aElementType;
+ OUString aElementName;
+
+ parseResourceURL( aName, aElementType, aElementName );
+ if ( aElementType.equalsIgnoreAsciiCase("menubar") &&
+ aElementName.equalsIgnoreAsciiCase("menubar") )
+ return m_xMenuBar;
+ else if (( aElementType.equalsIgnoreAsciiCase("statusbar") &&
+ aElementName.equalsIgnoreAsciiCase("statusbar") ) ||
+ ( m_aStatusBarElement.m_aName == aName ))
+ return m_aStatusBarElement.m_xUIElement;
+ else if ( aElementType.equalsIgnoreAsciiCase("progressbar") &&
+ aElementName.equalsIgnoreAsciiCase("progressbar") )
+ return m_aProgressBarElement.m_xUIElement;
+
+ return uno::Reference< ui::XUIElement >();
+}
+
+bool LayoutManager::implts_readWindowStateData( const OUString& aName, UIElement& rElementData )
+{
+ return readWindowStateData( aName, rElementData, m_xPersistentWindowState,
+ m_pGlobalSettings, m_bGlobalSettings, m_xContext );
+}
+
+bool LayoutManager::readWindowStateData( const OUString& aName, UIElement& rElementData,
+ const Reference< XNameAccess > &rPersistentWindowState,
+ std::unique_ptr<GlobalSettings> &rGlobalSettings, bool &bInGlobalSettings,
+ const Reference< XComponentContext > &rComponentContext )
+{
+ if ( !rPersistentWindowState.is() )
+ return false;
+
+ bool bGetSettingsState( false );
+
+ SolarMutexClearableGuard aWriteLock;
+ bool bGlobalSettings( bInGlobalSettings );
+ if ( rGlobalSettings == nullptr )
+ {
+ rGlobalSettings.reset( new GlobalSettings( rComponentContext ) );
+ bGetSettingsState = true;
+ }
+ GlobalSettings* pGlobalSettings = rGlobalSettings.get();
+ aWriteLock.clear();
+
+ try
+ {
+ Sequence< PropertyValue > aWindowState;
+ if ( rPersistentWindowState->hasByName( aName ) && (rPersistentWindowState->getByName( aName ) >>= aWindowState) )
+ {
+ bool bValue( false );
+ for ( PropertyValue const & rProp : std::as_const(aWindowState) )
+ {
+ if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKED )
+ {
+ if ( rProp.Value >>= bValue )
+ rElementData.m_bFloating = !bValue;
+ }
+ else if ( rProp.Name == WINDOWSTATE_PROPERTY_VISIBLE )
+ {
+ if ( rProp.Value >>= bValue )
+ rElementData.m_bVisible = bValue;
+ }
+ else if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKINGAREA )
+ {
+ ui::DockingArea eDockingArea;
+ if ( rProp.Value >>= eDockingArea )
+ rElementData.m_aDockedData.m_nDockedArea = eDockingArea;
+ }
+ else if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKPOS )
+ {
+ awt::Point aPoint;
+ if (rProp.Value >>= aPoint)
+ {
+ //tdf#90256 repair these broken Docking positions
+ if (aPoint.X < 0)
+ aPoint.X = SAL_MAX_INT32;
+ if (aPoint.Y < 0)
+ aPoint.Y = SAL_MAX_INT32;
+ rElementData.m_aDockedData.m_aPos = aPoint;
+ }
+ }
+ else if ( rProp.Name == WINDOWSTATE_PROPERTY_POS )
+ {
+ awt::Point aPoint;
+ if ( rProp.Value >>= aPoint )
+ rElementData.m_aFloatingData.m_aPos = aPoint;
+ }
+ else if ( rProp.Name == WINDOWSTATE_PROPERTY_SIZE )
+ {
+ awt::Size aSize;
+ if ( rProp.Value >>= aSize )
+ rElementData.m_aFloatingData.m_aSize = aSize;
+ }
+ else if ( rProp.Name == WINDOWSTATE_PROPERTY_UINAME )
+ rProp.Value >>= rElementData.m_aUIName;
+ else if ( rProp.Name == WINDOWSTATE_PROPERTY_STYLE )
+ {
+ sal_Int32 nStyle = 0;
+ if ( rProp.Value >>= nStyle )
+ rElementData.m_nStyle = static_cast<ButtonType>( nStyle );
+ }
+ else if ( rProp.Name == WINDOWSTATE_PROPERTY_LOCKED )
+ {
+ if ( rProp.Value >>= bValue )
+ rElementData.m_aDockedData.m_bLocked = bValue;
+ }
+ else if ( rProp.Name == WINDOWSTATE_PROPERTY_CONTEXT )
+ {
+ if ( rProp.Value >>= bValue )
+ rElementData.m_bContextSensitive = bValue;
+ }
+ else if ( rProp.Name == WINDOWSTATE_PROPERTY_NOCLOSE )
+ {
+ if ( rProp.Value >>= bValue )
+ rElementData.m_bNoClose = bValue;
+ }
+ }
+ }
+
+ // oversteer values with global settings
+ if (bGetSettingsState || bGlobalSettings)
+ {
+ if ( pGlobalSettings->HasToolbarStatesInfo())
+ {
+ {
+ SolarMutexGuard aWriteLock2;
+ bInGlobalSettings = true;
+ }
+
+ uno::Any aValue;
+ if ( pGlobalSettings->GetToolbarStateInfo(
+ GlobalSettings::STATEINFO_LOCKED,
+ aValue ))
+ aValue >>= rElementData.m_aDockedData.m_bLocked;
+ if ( pGlobalSettings->GetToolbarStateInfo(
+ GlobalSettings::STATEINFO_DOCKED,
+ aValue ))
+ {
+ bool bValue;
+ if ( aValue >>= bValue )
+ rElementData.m_bFloating = !bValue;
+ }
+ }
+ }
+
+ const bool bDockingSupportCrippled = !StyleSettings::GetDockingFloatsSupported();
+ if (bDockingSupportCrippled)
+ rElementData.m_bFloating = false;
+
+ return true;
+ }
+ catch (const NoSuchElementException&)
+ {
+ }
+
+ return false;
+}
+
+void LayoutManager::implts_writeWindowStateData( const OUString& aName, const UIElement& rElementData )
+{
+ SolarMutexClearableGuard aWriteLock;
+ Reference< XNameAccess > xPersistentWindowState( m_xPersistentWindowState );
+
+ aWriteLock.clear();
+
+ bool bPersistent( false );
+ Reference< XPropertySet > xPropSet( rElementData.m_xUIElement, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ // Check persistent flag of the user interface element
+ xPropSet->getPropertyValue("Persistent") >>= bPersistent;
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ // Non-configurable elements should at least store their dimension/position
+ bPersistent = true;
+ }
+ catch (const lang::WrappedTargetException&)
+ {
+ }
+ }
+
+ if ( !(bPersistent && xPersistentWindowState.is()) )
+ return;
+
+ try
+ {
+ Sequence< PropertyValue > aWindowState{
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKED, !rElementData.m_bFloating),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_VISIBLE, rElementData.m_bVisible),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKINGAREA,
+ rElementData.m_aDockedData.m_nDockedArea),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKPOS,
+ rElementData.m_aDockedData.m_aPos),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_POS,
+ rElementData.m_aFloatingData.m_aPos),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_SIZE,
+ rElementData.m_aFloatingData.m_aSize),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_UINAME, rElementData.m_aUIName),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_LOCKED,
+ rElementData.m_aDockedData.m_bLocked)
+ };
+
+ if ( xPersistentWindowState->hasByName( aName ))
+ {
+ Reference< XNameReplace > xReplace( xPersistentWindowState, uno::UNO_QUERY );
+ xReplace->replaceByName( aName, Any( aWindowState ));
+ }
+ else
+ {
+ Reference< XNameContainer > xInsert( xPersistentWindowState, uno::UNO_QUERY );
+ xInsert->insertByName( aName, Any( aWindowState ));
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+}
+
+::Size LayoutManager::implts_getContainerWindowOutputSize()
+{
+ ::Size aContainerWinSize;
+ vcl::Window* pContainerWindow( nullptr );
+
+ // Retrieve output size from container Window
+ SolarMutexGuard aGuard;
+ pContainerWindow = VCLUnoHelper::GetWindow( m_xContainerWindow );
+ if ( pContainerWindow )
+ aContainerWinSize = pContainerWindow->GetOutputSizePixel();
+
+ return aContainerWinSize;
+}
+
+Reference< XUIElement > LayoutManager::implts_createElement( const OUString& aName )
+{
+ Reference< ui::XUIElement > xUIElement;
+
+ SolarMutexGuard g;
+ Sequence< PropertyValue > aPropSeq{ comphelper::makePropertyValue("Frame", m_xFrame),
+ comphelper::makePropertyValue("Persistent", true) };
+
+ try
+ {
+ xUIElement = m_xUIElementFactoryManager->createUIElement( aName, aPropSeq );
+ }
+ catch (const NoSuchElementException&)
+ {
+ }
+ catch (const IllegalArgumentException&)
+ {
+ }
+
+ return xUIElement;
+}
+
+void LayoutManager::implts_setVisibleState( bool bShow )
+{
+ {
+ SolarMutexGuard aWriteLock;
+ m_aStatusBarElement.m_bMasterHide = !bShow;
+ }
+
+ implts_updateUIElementsVisibleState( bShow );
+}
+
+void LayoutManager::implts_updateUIElementsVisibleState( bool bSetVisible )
+{
+ // notify listeners
+ uno::Any a;
+ if ( bSetVisible )
+ implts_notifyListeners( frame::LayoutManagerEvents::VISIBLE, a );
+ else
+ implts_notifyListeners( frame::LayoutManagerEvents::INVISIBLE, a );
+
+ SolarMutexResettableGuard aWriteLock;
+ Reference< XUIElement > xMenuBar = m_xMenuBar;
+ Reference< awt::XWindow > xContainerWindow( m_xContainerWindow );
+ rtl::Reference< MenuBarManager > xInplaceMenuBar( m_xInplaceMenuBar );
+ aWriteLock.clear();
+
+ if (( xMenuBar.is() || xInplaceMenuBar.is() ) && xContainerWindow.is() )
+ {
+ SolarMutexGuard aGuard;
+
+ MenuBar* pMenuBar( nullptr );
+ if ( xInplaceMenuBar.is() )
+ pMenuBar = static_cast<MenuBar *>(xInplaceMenuBar->GetMenuBar());
+ else
+ {
+ MenuBarWrapper* pMenuBarWrapper = static_cast< MenuBarWrapper* >(xMenuBar.get());
+ pMenuBar = static_cast<MenuBar *>(pMenuBarWrapper->GetMenuBarManager()->GetMenuBar());
+ }
+
+ SystemWindow* pSysWindow = getTopSystemWindow( xContainerWindow );
+ if ( pSysWindow )
+ {
+ if ( bSetVisible )
+ {
+ pSysWindow->SetMenuBar(pMenuBar);
+ }
+ else
+ pSysWindow->SetMenuBar( nullptr );
+ }
+ }
+
+ bool bMustDoLayout;
+ // Hide/show the statusbar according to bSetVisible
+ if ( bSetVisible )
+ bMustDoLayout = !implts_showStatusBar();
+ else
+ bMustDoLayout = !implts_hideStatusBar();
+
+ aWriteLock.reset();
+ ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() );
+ aWriteLock.clear();
+
+ if ( pToolbarManager )
+ {
+ pToolbarManager->setVisible( bSetVisible );
+ bMustDoLayout = pToolbarManager->isLayoutDirty();
+ }
+
+ if ( bMustDoLayout )
+ implts_doLayout_notify( false );
+}
+
+void LayoutManager::implts_setCurrentUIVisibility( bool bShow )
+{
+ {
+ SolarMutexGuard aWriteLock;
+ if (!bShow && m_aStatusBarElement.m_bVisible && m_aStatusBarElement.m_xUIElement.is())
+ m_aStatusBarElement.m_bMasterHide = true;
+ else if (bShow && m_aStatusBarElement.m_bVisible)
+ m_aStatusBarElement.m_bMasterHide = false;
+ }
+
+ implts_updateUIElementsVisibleState( bShow );
+}
+
+void LayoutManager::implts_destroyStatusBar()
+{
+ Reference< XComponent > xCompStatusBar;
+
+ SolarMutexClearableGuard aWriteLock;
+ m_aStatusBarElement.m_aName.clear();
+ xCompStatusBar.set( m_aStatusBarElement.m_xUIElement, UNO_QUERY );
+ m_aStatusBarElement.m_xUIElement.clear();
+ aWriteLock.clear();
+
+ if ( xCompStatusBar.is() )
+ xCompStatusBar->dispose();
+
+ implts_destroyProgressBar();
+}
+
+void LayoutManager::implts_createStatusBar( const OUString& aStatusBarName )
+{
+ {
+ SolarMutexGuard aWriteLock;
+ if (!m_aStatusBarElement.m_xUIElement.is())
+ {
+ implts_readStatusBarState(aStatusBarName);
+ m_aStatusBarElement.m_aName = aStatusBarName;
+ m_aStatusBarElement.m_xUIElement = implts_createElement(aStatusBarName);
+ }
+ }
+
+ implts_createProgressBar();
+}
+
+void LayoutManager::implts_readStatusBarState( const OUString& rStatusBarName )
+{
+ SolarMutexGuard g;
+ if ( !m_aStatusBarElement.m_bStateRead )
+ {
+ // Read persistent data for status bar if not yet read!
+ if ( implts_readWindowStateData( rStatusBarName, m_aStatusBarElement ))
+ m_aStatusBarElement.m_bStateRead = true;
+ }
+}
+
+void LayoutManager::implts_createProgressBar()
+{
+ Reference< XUIElement > xStatusBar;
+ Reference< XUIElement > xProgressBar;
+ Reference< XUIElement > xProgressBarBackup;
+ Reference< awt::XWindow > xContainerWindow;
+
+ SolarMutexResettableGuard aWriteLock;
+ xStatusBar = m_aStatusBarElement.m_xUIElement;
+ xProgressBar = m_aProgressBarElement.m_xUIElement;
+ xProgressBarBackup = m_xProgressBarBackup;
+ m_xProgressBarBackup.clear();
+ xContainerWindow = m_xContainerWindow;
+ aWriteLock.clear();
+
+ bool bRecycled = xProgressBarBackup.is();
+ rtl::Reference<ProgressBarWrapper> pWrapper;
+ if ( bRecycled )
+ pWrapper = static_cast<ProgressBarWrapper*>(xProgressBarBackup.get());
+ else if ( xProgressBar.is() )
+ pWrapper = static_cast<ProgressBarWrapper*>(xProgressBar.get());
+ else
+ pWrapper = new ProgressBarWrapper();
+
+ if ( xStatusBar.is() )
+ {
+ Reference< awt::XWindow > xWindow( xStatusBar->getRealInterface(), UNO_QUERY );
+ pWrapper->setStatusBar( xWindow );
+ }
+ else
+ {
+ Reference< awt::XWindow > xStatusBarWindow = pWrapper->getStatusBar();
+
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pStatusBarWnd = VCLUnoHelper::GetWindow( xStatusBarWindow );
+ if ( !pStatusBarWnd )
+ {
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xContainerWindow );
+ if ( pWindow )
+ {
+ VclPtrInstance<StatusBar> pStatusBar( pWindow, WinBits( WB_LEFT | WB_3DLOOK ) );
+ Reference< awt::XWindow > xStatusBarWindow2( VCLUnoHelper::GetInterface( pStatusBar ));
+ pWrapper->setStatusBar( xStatusBarWindow2, true );
+ }
+ }
+ }
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ aWriteLock.reset();
+ m_aProgressBarElement.m_xUIElement = pWrapper;
+ aWriteLock.clear();
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ if ( bRecycled )
+ implts_showProgressBar();
+}
+
+void LayoutManager::implts_backupProgressBarWrapper()
+{
+ SolarMutexGuard g;
+
+ if (m_xProgressBarBackup.is())
+ return;
+
+ // safe a backup copy of the current progress!
+ // This copy will be used automatically inside createProgressBar() which is called
+ // implicitly from implts_doLayout() .-)
+ m_xProgressBarBackup = m_aProgressBarElement.m_xUIElement;
+
+ // remove the relation between this old progress bar and our old status bar.
+ // Otherwise we work on disposed items ...
+ // The internal used ProgressBarWrapper can handle a NULL reference.
+ if ( m_xProgressBarBackup.is() )
+ {
+ ProgressBarWrapper* pWrapper = static_cast<ProgressBarWrapper*>(m_xProgressBarBackup.get());
+ if ( pWrapper )
+ pWrapper->setStatusBar( Reference< awt::XWindow >() );
+ }
+
+ // prevent us from dispose() the m_aProgressBarElement.m_xUIElement inside implts_reset()
+ m_aProgressBarElement.m_xUIElement.clear();
+}
+
+void LayoutManager::implts_destroyProgressBar()
+{
+ // don't remove the progressbar in general
+ // We must reuse it if a new status bar is created later.
+ // Of course there exists one backup only.
+ // And further this backup will be released inside our dtor.
+ implts_backupProgressBarWrapper();
+}
+
+void LayoutManager::implts_setStatusBarPosSize( const ::Point& rPos, const ::Size& rSize )
+{
+ Reference< XUIElement > xStatusBar;
+ Reference< XUIElement > xProgressBar;
+ Reference< awt::XWindow > xContainerWindow;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexClearableGuard aReadLock;
+ xStatusBar = m_aStatusBarElement.m_xUIElement;
+ xProgressBar = m_aProgressBarElement.m_xUIElement;
+ xContainerWindow = m_xContainerWindow;
+
+ Reference< awt::XWindow > xWindow;
+ if ( xStatusBar.is() )
+ xWindow.set( xStatusBar->getRealInterface(), UNO_QUERY );
+ else if ( xProgressBar.is() )
+ {
+ ProgressBarWrapper* pWrapper = static_cast<ProgressBarWrapper*>(xProgressBar.get());
+ if ( pWrapper )
+ xWindow = pWrapper->getStatusBar();
+ }
+ aReadLock.clear();
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ if ( !xWindow.is() )
+ return;
+
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow( xContainerWindow );
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pParentWindow && ( pWindow && pWindow->GetType() == WindowType::STATUSBAR ))
+ {
+ vcl::Window* pOldParentWindow = pWindow->GetParent();
+ if ( pParentWindow != pOldParentWindow )
+ pWindow->SetParent( pParentWindow );
+ static_cast<StatusBar *>(pWindow.get())->SetPosSizePixel( rPos, rSize );
+ }
+}
+
+bool LayoutManager::implts_showProgressBar()
+{
+ Reference< XUIElement > xStatusBar;
+ Reference< XUIElement > xProgressBar;
+ Reference< awt::XWindow > xWindow;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexGuard aWriteLock;
+ xStatusBar = m_aStatusBarElement.m_xUIElement;
+ xProgressBar = m_aProgressBarElement.m_xUIElement;
+ bool bVisible( m_bVisible );
+
+ m_aProgressBarElement.m_bVisible = true;
+ if ( bVisible )
+ {
+ if ( xStatusBar.is() && !m_aStatusBarElement.m_bMasterHide )
+ {
+ xWindow.set( xStatusBar->getRealInterface(), UNO_QUERY );
+ }
+ else if ( xProgressBar.is() )
+ {
+ ProgressBarWrapper* pWrapper = static_cast<ProgressBarWrapper*>(xProgressBar.get());
+ if ( pWrapper )
+ xWindow = pWrapper->getStatusBar();
+ }
+ }
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow )
+ {
+ if ( !pWindow->IsVisible() )
+ {
+ implts_setOffset( pWindow->GetSizePixel().Height() );
+ pWindow->Show();
+ implts_doLayout_notify( false );
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool LayoutManager::implts_hideProgressBar()
+{
+ Reference< XUIElement > xProgressBar;
+ Reference< awt::XWindow > xWindow;
+ bool bHideStatusBar( false );
+
+ SolarMutexGuard g;
+ xProgressBar = m_aProgressBarElement.m_xUIElement;
+
+ bool bInternalStatusBar( false );
+ if ( xProgressBar.is() )
+ {
+ Reference< awt::XWindow > xStatusBar;
+ ProgressBarWrapper* pWrapper = static_cast<ProgressBarWrapper*>(xProgressBar.get());
+ if ( pWrapper )
+ xWindow = pWrapper->getStatusBar();
+ Reference< ui::XUIElement > xStatusBarElement = m_aStatusBarElement.m_xUIElement;
+ if ( xStatusBarElement.is() )
+ xStatusBar.set( xStatusBarElement->getRealInterface(), UNO_QUERY );
+ bInternalStatusBar = xStatusBar != xWindow;
+ }
+ m_aProgressBarElement.m_bVisible = false;
+ implts_readStatusBarState( STATUS_BAR_ALIAS );
+ bHideStatusBar = !m_aStatusBarElement.m_bVisible;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->IsVisible() && ( bHideStatusBar || bInternalStatusBar ))
+ {
+ implts_setOffset( 0 );
+ pWindow->Hide();
+ implts_doLayout_notify( false );
+ return true;
+ }
+
+ return false;
+}
+
+bool LayoutManager::implts_showStatusBar( bool bStoreState )
+{
+ SolarMutexClearableGuard aWriteLock;
+ Reference< ui::XUIElement > xStatusBar = m_aStatusBarElement.m_xUIElement;
+ if ( bStoreState )
+ m_aStatusBarElement.m_bVisible = true;
+ aWriteLock.clear();
+
+ if ( xStatusBar.is() )
+ {
+ Reference< awt::XWindow > xWindow( xStatusBar->getRealInterface(), UNO_QUERY );
+
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && !pWindow->IsVisible() )
+ {
+ implts_setOffset( pWindow->GetSizePixel().Height() );
+ pWindow->Show();
+ implts_doLayout_notify( false );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool LayoutManager::implts_hideStatusBar( bool bStoreState )
+{
+ SolarMutexClearableGuard aWriteLock;
+ Reference< ui::XUIElement > xStatusBar = m_aStatusBarElement.m_xUIElement;
+ if ( bStoreState )
+ m_aStatusBarElement.m_bVisible = false;
+ aWriteLock.clear();
+
+ if ( xStatusBar.is() )
+ {
+ Reference< awt::XWindow > xWindow( xStatusBar->getRealInterface(), UNO_QUERY );
+
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->IsVisible() )
+ {
+ implts_setOffset( 0 );
+ pWindow->Hide();
+ implts_doLayout_notify( false );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void LayoutManager::implts_setOffset( const sal_Int32 nBottomOffset )
+{
+ if ( m_xToolbarManager.is() )
+ m_xToolbarManager->setDockingAreaOffsets({ 0, 0, 0, nBottomOffset });
+}
+
+void LayoutManager::implts_setInplaceMenuBar( const Reference< XIndexAccess >& xMergedMenuBar )
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexClearableGuard aWriteLock;
+
+ if ( m_bInplaceMenuSet )
+ return;
+
+ SolarMutexGuard aGuard;
+
+ // Reset old inplace menubar!
+ VclPtr<Menu> pOldMenuBar;
+ if (m_xInplaceMenuBar.is())
+ {
+ pOldMenuBar = m_xInplaceMenuBar->GetMenuBar();
+ m_xInplaceMenuBar->dispose();
+ m_xInplaceMenuBar.clear();
+ }
+ pOldMenuBar.disposeAndClear();
+
+ m_bInplaceMenuSet = false;
+
+ if ( m_xFrame.is() && m_xContainerWindow.is() )
+ {
+ Reference< XDispatchProvider > xDispatchProvider;
+
+ VclPtr<MenuBar> pMenuBar = VclPtr<MenuBar>::Create();
+ m_xInplaceMenuBar = new MenuBarManager( m_xContext, m_xFrame, m_xURLTransformer, xDispatchProvider, OUString(), pMenuBar, true );
+ m_xInplaceMenuBar->SetItemContainer( xMergedMenuBar );
+
+ SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow );
+ if ( pSysWindow )
+ pSysWindow->SetMenuBar(pMenuBar);
+
+ m_bInplaceMenuSet = true;
+ }
+
+ aWriteLock.clear();
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ implts_updateMenuBarClose();
+}
+
+void LayoutManager::implts_resetInplaceMenuBar()
+{
+ SolarMutexGuard g;
+ m_bInplaceMenuSet = false;
+
+ if ( m_xContainerWindow.is() )
+ {
+ SolarMutexGuard aGuard;
+ MenuBarWrapper* pMenuBarWrapper = static_cast< MenuBarWrapper* >(m_xMenuBar.get());
+ SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow );
+ if ( pSysWindow )
+ {
+ if ( pMenuBarWrapper )
+ pSysWindow->SetMenuBar(static_cast<MenuBar *>(pMenuBarWrapper->GetMenuBarManager()->GetMenuBar()));
+ else
+ pSysWindow->SetMenuBar(nullptr);
+ }
+ }
+
+ // Remove inplace menu bar
+ VclPtr<Menu> pMenuBar;
+ if (m_xInplaceMenuBar.is())
+ {
+ pMenuBar = m_xInplaceMenuBar->GetMenuBar();
+ m_xInplaceMenuBar->dispose();
+ m_xInplaceMenuBar.clear();
+ }
+ pMenuBar.disposeAndClear();
+}
+
+void SAL_CALL LayoutManager::attachFrame( const Reference< XFrame >& xFrame )
+{
+ SolarMutexGuard g;
+ m_xFrame = xFrame;
+}
+
+void SAL_CALL LayoutManager::reset()
+{
+ implts_reset( true );
+}
+
+// XMenuBarMergingAcceptor
+
+sal_Bool SAL_CALL LayoutManager::setMergedMenuBar(
+ const Reference< XIndexAccess >& xMergedMenuBar )
+{
+ implts_setInplaceMenuBar( xMergedMenuBar );
+
+ uno::Any a;
+ implts_notifyListeners( frame::LayoutManagerEvents::MERGEDMENUBAR, a );
+ return true;
+}
+
+void SAL_CALL LayoutManager::removeMergedMenuBar()
+{
+ implts_resetInplaceMenuBar();
+}
+
+awt::Rectangle SAL_CALL LayoutManager::getCurrentDockingArea()
+{
+ SolarMutexGuard g;
+ return m_aDockingArea;
+}
+
+Reference< XDockingAreaAcceptor > SAL_CALL LayoutManager::getDockingAreaAcceptor()
+{
+ SolarMutexGuard g;
+ return m_xDockingAreaAcceptor;
+}
+
+void SAL_CALL LayoutManager::setDockingAreaAcceptor( const Reference< ui::XDockingAreaAcceptor >& xDockingAreaAcceptor )
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexClearableGuard aWriteLock;
+
+ if (( m_xDockingAreaAcceptor == xDockingAreaAcceptor ) || !m_xFrame.is() )
+ return;
+
+ // IMPORTANT: Be sure to stop layout timer if don't have a docking area acceptor!
+ if ( !xDockingAreaAcceptor.is() )
+ m_aAsyncLayoutTimer.Stop();
+
+ bool bAutomaticToolbars( m_bAutomaticToolbars );
+
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+
+ if ( !xDockingAreaAcceptor.is() )
+ m_aAsyncLayoutTimer.Stop();
+
+ // Remove listener from old docking area acceptor
+ if ( m_xDockingAreaAcceptor.is() )
+ {
+ Reference< awt::XWindow > xWindow( m_xDockingAreaAcceptor->getContainerWindow() );
+ if ( xWindow.is() && ( m_xFrame->getContainerWindow() != m_xContainerWindow || !xDockingAreaAcceptor.is() ) )
+ xWindow->removeWindowListener( Reference< awt::XWindowListener >(this) );
+
+ m_aDockingArea = awt::Rectangle();
+ if ( pToolbarManager )
+ pToolbarManager->resetDockingArea();
+
+ VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pContainerWindow )
+ pContainerWindow->RemoveChildEventListener( LINK( this, LayoutManager, WindowEventListener ) );
+ }
+
+ m_xDockingAreaAcceptor = xDockingAreaAcceptor;
+ if ( m_xDockingAreaAcceptor.is() )
+ {
+ m_aDockingArea = awt::Rectangle();
+ m_xContainerWindow = m_xDockingAreaAcceptor->getContainerWindow();
+ m_xContainerTopWindow.set( m_xContainerWindow, UNO_QUERY );
+ m_xContainerWindow->addWindowListener( Reference< awt::XWindowListener >(this) );
+
+ // we always must keep a connection to the window of our frame for resize events
+ if ( m_xContainerWindow != m_xFrame->getContainerWindow() )
+ m_xFrame->getContainerWindow()->addWindowListener( Reference< awt::XWindowListener >(this) );
+
+ // #i37884# set initial visibility state - in the plugin case the container window is already shown
+ // and we get no notification anymore
+ {
+ VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( m_xContainerWindow );
+ if( pContainerWindow )
+ m_bParentWindowVisible = pContainerWindow->IsVisible();
+ }
+ }
+
+ aWriteLock.clear();
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ if ( xDockingAreaAcceptor.is() )
+ {
+ SolarMutexGuard aGuard;
+
+ // Add layout manager as listener to get notifications about toolbar button activities
+ VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( m_xContainerWindow );
+ if ( pContainerWindow )
+ pContainerWindow->AddChildEventListener( LINK( this, LayoutManager, WindowEventListener ) );
+
+ // We have now a new container window, reparent all child windows!
+ implts_reparentChildWindows();
+ }
+ else
+ implts_destroyElements(); // remove all elements
+
+ if ( pToolbarManager && xDockingAreaAcceptor.is() )
+ {
+ if ( bAutomaticToolbars )
+ {
+ lock();
+ pToolbarManager->createStaticToolbars();
+ unlock();
+ }
+ implts_doLayout( true, false );
+ }
+}
+
+void LayoutManager::implts_reparentChildWindows()
+{
+ SolarMutexResettableGuard aWriteLock;
+ UIElement aStatusBarElement = m_aStatusBarElement;
+ uno::Reference< awt::XWindow > xContainerWindow = m_xContainerWindow;
+ aWriteLock.clear();
+
+ uno::Reference< awt::XWindow > xStatusBarWindow;
+ if ( aStatusBarElement.m_xUIElement.is() )
+ {
+ try
+ {
+ xStatusBarWindow.set( aStatusBarElement.m_xUIElement->getRealInterface(), UNO_QUERY );
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ if ( xStatusBarWindow.is() )
+ {
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow );
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xStatusBarWindow );
+ if ( pWindow && pContainerWindow )
+ pWindow->SetParent( pContainerWindow );
+ }
+
+ implts_resetMenuBar();
+
+ aWriteLock.reset();
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ if ( pToolbarManager )
+ pToolbarManager->setParentWindow( uno::Reference< awt::XWindowPeer >( xContainerWindow, uno::UNO_QUERY ));
+ aWriteLock.clear();
+}
+
+uno::Reference< ui::XUIElement > LayoutManager::implts_createDockingWindow( const OUString& aElementName )
+{
+ Reference< XUIElement > xUIElement = implts_createElement( aElementName );
+ return xUIElement;
+}
+
+IMPL_LINK( LayoutManager, WindowEventListener, VclWindowEvent&, rEvent, void )
+{
+ vcl::Window* pWindow = rEvent.GetWindow();
+ if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX )
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() );
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ pToolbarManager->childWindowEvent( &rEvent );
+ }
+}
+
+void SAL_CALL LayoutManager::createElement( const OUString& aName )
+{
+ SAL_INFO( "fwk", "LayoutManager::createElement " << aName );
+
+ SolarMutexClearableGuard aReadLock;
+ Reference< XFrame > xFrame = m_xFrame;
+ aReadLock.clear();
+
+ if ( !xFrame.is() )
+ return;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexClearableGuard aWriteLock;
+
+ bool bMustBeLayouted( false );
+ bool bNotify( false );
+
+ bool bPreviewFrame;
+ if (m_xToolbarManager.is())
+ // Assumes that we created the ToolbarLayoutManager with our frame, if
+ // not then we're somewhat fouled up ...
+ bPreviewFrame = m_xToolbarManager->isPreviewFrame();
+ else
+ {
+ Reference< XModel > xModel( impl_getModelFromFrame( xFrame ) );
+ bPreviewFrame = implts_isPreviewModel( xModel );
+ }
+
+ if ( m_xContainerWindow.is() && !bPreviewFrame ) // no UI elements on preview frames
+ {
+ OUString aElementType;
+ OUString aElementName;
+
+ parseResourceURL( aName, aElementType, aElementName );
+
+ if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR ) && m_xToolbarManager.is() )
+ {
+ bNotify = m_xToolbarManager->createToolbar( aName );
+ bMustBeLayouted = m_xToolbarManager->isLayoutDirty();
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase("menubar") &&
+ aElementName.equalsIgnoreAsciiCase("menubar") &&
+ implts_isFrameOrWindowTop(xFrame) )
+ {
+ implts_createMenuBar( aName );
+ if (m_bMenuVisible)
+ bNotify = true;
+
+ aWriteLock.clear();
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase("statusbar") &&
+ ( implts_isFrameOrWindowTop(xFrame) || implts_isEmbeddedLayoutManager() ))
+ {
+ implts_createStatusBar( aName );
+ bNotify = true;
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase("progressbar") &&
+ aElementName.equalsIgnoreAsciiCase("progressbar") &&
+ implts_isFrameOrWindowTop(xFrame) )
+ {
+ implts_createProgressBar();
+ bNotify = true;
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow"))
+ {
+ // Add layout manager as listener for docking and other window events
+ uno::Reference< uno::XInterface > xThis( static_cast< OWeakObject* >(this), uno::UNO_QUERY );
+ uno::Reference< ui::XUIElement > xUIElement( implts_createDockingWindow( aName ));
+
+ if ( xUIElement.is() )
+ {
+ impl_addWindowListeners( xThis, xUIElement );
+ }
+
+ // The docking window is created by a factory method located in the sfx2 library.
+// CreateDockingWindow( xFrame, aElementName );
+ }
+ }
+
+ if ( bMustBeLayouted )
+ implts_doLayout_notify( true );
+
+ if ( bNotify )
+ {
+ // UI element is invisible - provide information to listeners
+ implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_VISIBLE, uno::Any( aName ) );
+ }
+}
+
+void SAL_CALL LayoutManager::destroyElement( const OUString& aName )
+{
+ SAL_INFO( "fwk", "LayoutManager::destroyElement " << aName );
+
+ bool bMustBeLayouted(false);
+ bool bNotify(false);
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ {
+ SolarMutexClearableGuard aWriteLock;
+
+ OUString aElementType;
+ OUString aElementName;
+
+ parseResourceURL(aName, aElementType, aElementName);
+
+ if (aElementType.equalsIgnoreAsciiCase("menubar")
+ && aElementName.equalsIgnoreAsciiCase("menubar"))
+ {
+ if (!m_bInplaceMenuSet)
+ {
+ impl_clearUpMenuBar();
+ m_xMenuBar.clear();
+ bNotify = true;
+ }
+ }
+ else if ((aElementType.equalsIgnoreAsciiCase("statusbar")
+ && aElementName.equalsIgnoreAsciiCase("statusbar"))
+ || (m_aStatusBarElement.m_aName == aName))
+ {
+ aWriteLock.clear();
+ implts_destroyStatusBar();
+ bMustBeLayouted = true;
+ bNotify = true;
+ }
+ else if (aElementType.equalsIgnoreAsciiCase("progressbar")
+ && aElementName.equalsIgnoreAsciiCase("progressbar"))
+ {
+ aWriteLock.clear();
+ implts_createProgressBar();
+ bMustBeLayouted = true;
+ bNotify = true;
+ }
+ else if (aElementType.equalsIgnoreAsciiCase(UIRESOURCETYPE_TOOLBAR)
+ && m_xToolbarManager.is())
+ {
+ aWriteLock.clear();
+ bNotify = m_xToolbarManager->destroyToolbar(aName);
+ bMustBeLayouted = m_xToolbarManager->isLayoutDirty();
+ }
+ else if (aElementType.equalsIgnoreAsciiCase("dockingwindow"))
+ {
+ uno::Reference<frame::XFrame> xFrame(m_xFrame);
+ uno::Reference<XComponentContext> xContext(m_xContext);
+ aWriteLock.clear();
+
+ impl_setDockingWindowVisibility(xContext, xFrame, aElementName, false);
+ bMustBeLayouted = false;
+ bNotify = false;
+ }
+ }
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ if ( bMustBeLayouted )
+ doLayout();
+
+ if ( bNotify )
+ implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_INVISIBLE, uno::Any( aName ) );
+}
+
+sal_Bool SAL_CALL LayoutManager::requestElement( const OUString& rResourceURL )
+{
+ bool bResult( false );
+ bool bNotify( false );
+ OUString aElementType;
+ OUString aElementName;
+
+ parseResourceURL( rResourceURL, aElementType, aElementName );
+
+ SolarMutexClearableGuard aWriteLock;
+
+ OString aResName = OUStringToOString( aElementName, RTL_TEXTENCODING_ASCII_US );
+ SAL_INFO( "fwk", "LayoutManager::requestElement " << aResName );
+
+ if (( aElementType.equalsIgnoreAsciiCase("statusbar") &&
+ aElementName.equalsIgnoreAsciiCase("statusbar") ) ||
+ ( m_aStatusBarElement.m_aName == rResourceURL ))
+ {
+ implts_readStatusBarState( rResourceURL );
+ if ( m_aStatusBarElement.m_bVisible && !m_aStatusBarElement.m_bMasterHide )
+ {
+ aWriteLock.clear();
+ createElement( rResourceURL );
+
+ // There are some situation where we are not able to create an element.
+ // Therefore we have to check the reference before further action.
+ // See #i70019#
+ uno::Reference< ui::XUIElement > xUIElement( m_aStatusBarElement.m_xUIElement );
+ if ( xUIElement.is() )
+ {
+ // we need VCL here to pass special flags to Show()
+ SolarMutexGuard aGuard;
+ Reference< awt::XWindow > xWindow( xUIElement->getRealInterface(), UNO_QUERY );
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow )
+ {
+ pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+ bResult = true;
+ bNotify = true;
+ }
+ }
+ }
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase("progressbar") &&
+ aElementName.equalsIgnoreAsciiCase("progressbar") )
+ {
+ aWriteLock.clear();
+ implts_showProgressBar();
+ bResult = true;
+ bNotify = true;
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR ) && m_bVisible )
+ {
+ bool bComponentAttached( !m_aModuleIdentifier.isEmpty() );
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aWriteLock.clear();
+
+ if ( pToolbarManager && bComponentAttached )
+ {
+ bNotify = pToolbarManager->requestToolbar( rResourceURL );
+ }
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow"))
+ {
+ uno::Reference< frame::XFrame > xFrame( m_xFrame );
+ aWriteLock.clear();
+
+ CreateDockingWindow( xFrame, aElementName );
+ }
+
+ if ( bNotify )
+ implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_VISIBLE, uno::Any( rResourceURL ) );
+
+ return bResult;
+}
+
+Reference< XUIElement > SAL_CALL LayoutManager::getElement( const OUString& aName )
+{
+ Reference< XUIElement > xUIElement = implts_findElement( aName );
+ if ( !xUIElement.is() )
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() );
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ xUIElement = pToolbarManager->getToolbar( aName );
+ }
+
+ return xUIElement;
+}
+
+Sequence< Reference< ui::XUIElement > > SAL_CALL LayoutManager::getElements()
+{
+ SolarMutexClearableGuard aReadLock;
+ uno::Reference< ui::XUIElement > xMenuBar( m_xMenuBar );
+ uno::Reference< ui::XUIElement > xStatusBar( m_aStatusBarElement.m_xUIElement );
+ ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() );
+ aReadLock.clear();
+
+ Sequence< Reference< ui::XUIElement > > aSeq;
+ if ( pToolbarManager )
+ aSeq = pToolbarManager->getToolbars();
+
+ sal_Int32 nSize = aSeq.getLength();
+ sal_Int32 nMenuBarIndex(-1);
+ sal_Int32 nStatusBarIndex(-1);
+ if ( xMenuBar.is() )
+ {
+ nMenuBarIndex = nSize;
+ ++nSize;
+ }
+ if ( xStatusBar.is() )
+ {
+ nStatusBarIndex = nSize;
+ ++nSize;
+ }
+
+ aSeq.realloc(nSize);
+ auto pSeq = aSeq.getArray();
+ if ( nMenuBarIndex >= 0 )
+ pSeq[nMenuBarIndex] = xMenuBar;
+ if ( nStatusBarIndex >= 0 )
+ pSeq[nStatusBarIndex] = xStatusBar;
+
+ return aSeq;
+}
+
+sal_Bool SAL_CALL LayoutManager::showElement( const OUString& aName )
+{
+ bool bResult( false );
+ bool bNotify( false );
+ bool bMustLayout( false );
+ OUString aElementType;
+ OUString aElementName;
+
+ parseResourceURL( aName, aElementType, aElementName );
+
+ OString aResName = OUStringToOString( aElementName, RTL_TEXTENCODING_ASCII_US );
+ SAL_INFO( "fwk", "LayoutManager::showElement " << aResName );
+
+ if ( aElementType.equalsIgnoreAsciiCase("menubar") &&
+ aElementName.equalsIgnoreAsciiCase("menubar") )
+ {
+ {
+ SolarMutexGuard aWriteLock;
+ m_bMenuVisible = true;
+ }
+
+ bResult = implts_resetMenuBar();
+ bNotify = bResult;
+ }
+ else if (( aElementType.equalsIgnoreAsciiCase("statusbar") &&
+ aElementName.equalsIgnoreAsciiCase("statusbar") ) ||
+ ( m_aStatusBarElement.m_aName == aName ))
+ {
+ SolarMutexClearableGuard aWriteLock;
+ if ( m_aStatusBarElement.m_xUIElement.is() && !m_aStatusBarElement.m_bMasterHide &&
+ implts_showStatusBar( true ))
+ {
+ aWriteLock.clear();
+
+ implts_writeWindowStateData( STATUS_BAR_ALIAS, m_aStatusBarElement );
+ bMustLayout = true;
+ bResult = true;
+ bNotify = true;
+ }
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase("progressbar") &&
+ aElementName.equalsIgnoreAsciiCase("progressbar") )
+ {
+ bNotify = bResult = implts_showProgressBar();
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR ))
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ {
+ bNotify = pToolbarManager->showToolbar( aName );
+ bMustLayout = pToolbarManager->isLayoutDirty();
+ }
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow"))
+ {
+ SolarMutexClearableGuard aReadGuard;
+ uno::Reference< frame::XFrame > xFrame( m_xFrame );
+ uno::Reference< XComponentContext > xContext( m_xContext );
+ aReadGuard.clear();
+
+ impl_setDockingWindowVisibility( xContext, xFrame, aElementName, true );
+ }
+
+ if ( bMustLayout )
+ doLayout();
+
+ if ( bNotify )
+ implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_VISIBLE, uno::Any( aName ) );
+
+ return bResult;
+}
+
+sal_Bool SAL_CALL LayoutManager::hideElement( const OUString& aName )
+{
+ bool bNotify( false );
+ bool bMustLayout( false );
+ OUString aElementType;
+ OUString aElementName;
+
+ parseResourceURL( aName, aElementType, aElementName );
+ OString aResName = OUStringToOString( aElementName, RTL_TEXTENCODING_ASCII_US );
+ SAL_INFO( "fwk", "LayoutManager::hideElement " << aResName );
+
+ if ( aElementType.equalsIgnoreAsciiCase("menubar") &&
+ aElementName.equalsIgnoreAsciiCase("menubar") )
+ {
+ SolarMutexGuard g;
+
+ if ( m_xContainerWindow.is() )
+ {
+ m_bMenuVisible = false;
+
+ SolarMutexGuard aGuard;
+ SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow );
+ if ( pSysWindow )
+ {
+ MenuBar* pMenuBar = pSysWindow->GetMenuBar();
+ if ( pMenuBar )
+ {
+ pMenuBar->SetDisplayable( false );
+ bNotify = true;
+ }
+ }
+ }
+ }
+ else if (( aElementType.equalsIgnoreAsciiCase("statusbar") &&
+ aElementName.equalsIgnoreAsciiCase("statusbar") ) ||
+ ( m_aStatusBarElement.m_aName == aName ))
+ {
+ SolarMutexGuard g;
+ if ( m_aStatusBarElement.m_xUIElement.is() && !m_aStatusBarElement.m_bMasterHide &&
+ implts_hideStatusBar( true ))
+ {
+ implts_writeWindowStateData( STATUS_BAR_ALIAS, m_aStatusBarElement );
+ bMustLayout = true;
+ bNotify = true;
+ }
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase("progressbar") &&
+ aElementName.equalsIgnoreAsciiCase("progressbar") )
+ {
+ bNotify = implts_hideProgressBar();
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR ))
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ {
+ bNotify = pToolbarManager->hideToolbar( aName );
+ bMustLayout = pToolbarManager->isLayoutDirty();
+ }
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow"))
+ {
+ SolarMutexClearableGuard aReadGuard;
+ uno::Reference< frame::XFrame > xFrame( m_xFrame );
+ uno::Reference< XComponentContext > xContext( m_xContext );
+ aReadGuard.clear();
+
+ impl_setDockingWindowVisibility( xContext, xFrame, aElementName, false );
+ }
+
+ if ( bMustLayout )
+ doLayout();
+
+ if ( bNotify )
+ implts_notifyListeners( frame::LayoutManagerEvents::UIELEMENT_INVISIBLE, uno::Any( aName ) );
+
+ return false;
+}
+
+sal_Bool SAL_CALL LayoutManager::dockWindow( const OUString& aName, DockingArea DockingArea, const awt::Point& Pos )
+{
+ OUString aElementType;
+ OUString aElementName;
+
+ parseResourceURL( aName, aElementType, aElementName );
+ if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR ))
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ {
+ pToolbarManager->dockToolbar( aName, DockingArea, Pos );
+ if ( pToolbarManager->isLayoutDirty() )
+ doLayout();
+ }
+ }
+ return false;
+}
+
+sal_Bool SAL_CALL LayoutManager::dockAllWindows( ::sal_Int16 /*nElementType*/ )
+{
+ SolarMutexClearableGuard aReadLock;
+ bool bResult( false );
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ {
+ bResult = pToolbarManager->dockAllToolbars();
+ if ( pToolbarManager->isLayoutDirty() )
+ doLayout();
+ }
+ return bResult;
+}
+
+sal_Bool SAL_CALL LayoutManager::floatWindow( const OUString& aName )
+{
+ bool bResult( false );
+ if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR ))
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ {
+ bResult = pToolbarManager->floatToolbar( aName );
+ if ( pToolbarManager->isLayoutDirty() )
+ doLayout();
+ }
+ }
+ return bResult;
+}
+
+sal_Bool SAL_CALL LayoutManager::lockWindow( const OUString& aName )
+{
+ bool bResult( false );
+ if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR ))
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ {
+ bResult = pToolbarManager->lockToolbar( aName );
+ if ( pToolbarManager->isLayoutDirty() )
+ doLayout();
+ }
+ }
+ return bResult;
+}
+
+sal_Bool SAL_CALL LayoutManager::unlockWindow( const OUString& aName )
+{
+ bool bResult( false );
+ if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR ))
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ {
+ bResult = pToolbarManager->unlockToolbar( aName );
+ if ( pToolbarManager->isLayoutDirty() )
+ doLayout();
+ }
+ }
+ return bResult;
+}
+
+void SAL_CALL LayoutManager::setElementSize( const OUString& aName, const awt::Size& aSize )
+{
+ if ( !o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR ))
+ return;
+
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ {
+ pToolbarManager->setToolbarSize( aName, aSize );
+ if ( pToolbarManager->isLayoutDirty() )
+ doLayout();
+ }
+}
+
+void SAL_CALL LayoutManager::setElementPos( const OUString& aName, const awt::Point& aPos )
+{
+ if ( !o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR ))
+ return;
+
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() );
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ {
+ pToolbarManager->setToolbarPos( aName, aPos );
+ if ( pToolbarManager->isLayoutDirty() )
+ doLayout();
+ }
+}
+
+void SAL_CALL LayoutManager::setElementPosSize( const OUString& aName, const awt::Point& aPos, const awt::Size& aSize )
+{
+ if ( !o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR ))
+ return;
+
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager( m_xToolbarManager.get() );
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ {
+ pToolbarManager->setToolbarPosSize( aName, aPos, aSize );
+ if ( pToolbarManager->isLayoutDirty() )
+ doLayout();
+ }
+}
+
+sal_Bool SAL_CALL LayoutManager::isElementVisible( const OUString& aName )
+{
+ OUString aElementType;
+ OUString aElementName;
+
+ parseResourceURL( aName, aElementType, aElementName );
+ if ( aElementType.equalsIgnoreAsciiCase("menubar") &&
+ aElementName.equalsIgnoreAsciiCase("menubar") )
+ {
+ SolarMutexResettableGuard aReadLock;
+ if ( m_xContainerWindow.is() )
+ {
+ aReadLock.clear();
+
+ SolarMutexGuard aGuard;
+ SystemWindow* pSysWindow = getTopSystemWindow( m_xContainerWindow );
+ if ( pSysWindow )
+ {
+ MenuBar* pMenuBar = pSysWindow->GetMenuBar();
+ if ( pMenuBar && pMenuBar->IsDisplayable() )
+ return true;
+ }
+ else
+ {
+ aReadLock.reset();
+ return m_bMenuVisible;
+ }
+ }
+ }
+ else if (( aElementType.equalsIgnoreAsciiCase("statusbar") &&
+ aElementName.equalsIgnoreAsciiCase("statusbar") ) ||
+ ( m_aStatusBarElement.m_aName == aName ))
+ {
+ if ( m_aStatusBarElement.m_xUIElement.is() )
+ {
+ Reference< awt::XWindow > xWindow( m_aStatusBarElement.m_xUIElement->getRealInterface(), UNO_QUERY );
+ if ( xWindow.is() )
+ {
+ SolarMutexGuard g;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->IsVisible() )
+ return true;
+ else
+ return false;
+ }
+ }
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase("progressbar") &&
+ aElementName.equalsIgnoreAsciiCase("progressbar") )
+ {
+ if ( m_aProgressBarElement.m_xUIElement.is() )
+ return m_aProgressBarElement.m_bVisible;
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR ))
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ return pToolbarManager->isToolbarVisible( aName );
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase("dockingwindow"))
+ {
+ SolarMutexClearableGuard aReadGuard;
+ uno::Reference< frame::XFrame > xFrame( m_xFrame );
+ aReadGuard.clear();
+
+ return IsDockingWindowVisible( xFrame, aElementName );
+ }
+
+ return false;
+}
+
+sal_Bool SAL_CALL LayoutManager::isElementFloating( const OUString& aName )
+{
+ if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR ))
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ return pToolbarManager->isToolbarFloating( aName );
+ }
+
+ return false;
+}
+
+sal_Bool SAL_CALL LayoutManager::isElementDocked( const OUString& aName )
+{
+ if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR ))
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ return pToolbarManager->isToolbarDocked( aName );
+ }
+
+ return false;
+}
+
+sal_Bool SAL_CALL LayoutManager::isElementLocked( const OUString& aName )
+{
+ if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR ))
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ return pToolbarManager->isToolbarLocked( aName );
+ }
+
+ return false;
+}
+
+awt::Size SAL_CALL LayoutManager::getElementSize( const OUString& aName )
+{
+ if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR ))
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ return pToolbarManager->getToolbarSize( aName );
+ }
+
+ return awt::Size();
+}
+
+awt::Point SAL_CALL LayoutManager::getElementPos( const OUString& aName )
+{
+ if ( o3tl::equalsIgnoreAsciiCase(getElementTypeFromResourceURL( aName ), UIRESOURCETYPE_TOOLBAR ))
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ return pToolbarManager->getToolbarPos( aName );
+ }
+
+ return awt::Point();
+}
+
+void SAL_CALL LayoutManager::lock()
+{
+ implts_lock();
+
+ SolarMutexClearableGuard aReadLock;
+ sal_Int32 nLockCount( m_nLockCount );
+ aReadLock.clear();
+
+ SAL_INFO( "fwk", "LayoutManager::lock " << reinterpret_cast<sal_Int64>(this) << " - " << nLockCount );
+
+ Any a( nLockCount );
+ implts_notifyListeners( frame::LayoutManagerEvents::LOCK, a );
+}
+
+void SAL_CALL LayoutManager::unlock()
+{
+ bool bDoLayout( implts_unlock() );
+
+ SolarMutexClearableGuard aReadLock;
+ sal_Int32 nLockCount( m_nLockCount );
+ aReadLock.clear();
+
+ SAL_INFO( "fwk", "LayoutManager::unlock " << reinterpret_cast<sal_Int64>(this) << " - " << nLockCount);
+
+ // conform to documentation: unlock with lock count == 0 means force a layout
+
+ {
+ SolarMutexGuard aWriteLock;
+ if (bDoLayout)
+ m_aAsyncLayoutTimer.Stop();
+ }
+
+ Any a( nLockCount );
+ implts_notifyListeners( frame::LayoutManagerEvents::UNLOCK, a );
+
+ if ( bDoLayout )
+ implts_doLayout_notify( true );
+}
+
+void SAL_CALL LayoutManager::doLayout()
+{
+ implts_doLayout_notify( true );
+}
+
+// ILayoutNotifications
+
+void LayoutManager::requestLayout()
+{
+ doLayout();
+}
+
+void LayoutManager::implts_doLayout_notify( bool bOuterResize )
+{
+ bool bLayouted = implts_doLayout( false, bOuterResize );
+ if ( bLayouted )
+ implts_notifyListeners( frame::LayoutManagerEvents::LAYOUT, Any() );
+}
+
+bool LayoutManager::implts_doLayout( bool bForceRequestBorderSpace, bool bOuterResize )
+{
+ SAL_INFO( "fwk", "LayoutManager::implts_doLayout" );
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexClearableGuard aReadLock;
+
+ if ( !m_xFrame.is() || !m_bParentWindowVisible )
+ return false;
+
+ bool bPreserveContentSize( m_bPreserveContentSize );
+ bool bMustDoLayout( m_bMustDoLayout );
+ bool bNoLock = ( m_nLockCount == 0 );
+ awt::Rectangle aCurrBorderSpace( m_aDockingArea );
+ Reference< awt::XWindow > xContainerWindow( m_xContainerWindow );
+ Reference< awt::XTopWindow2 > xContainerTopWindow( m_xContainerTopWindow );
+ Reference< awt::XWindow > xComponentWindow;
+ try {
+ xComponentWindow = m_xFrame->getComponentWindow();
+ } catch (css::lang::DisposedException &) {
+ // There can be a race between one thread calling Frame::dispose
+ // (framework/source/services/frame.cxx) -> Frame::disableLayoutManager
+ // -> LayoutManager::attachFrame(null) setting m_xFrame to null, and
+ // the main thread firing the timer-triggered
+ // LayoutManager::AsyncLayoutHdl -> LayoutManager::implts_doLayout and
+ // calling into the in-dispose m_xFrame here, so silently ignore a
+ // DisposedException here:
+ return false;
+ }
+ Reference< XDockingAreaAcceptor > xDockingAreaAcceptor( m_xDockingAreaAcceptor );
+ aReadLock.clear();
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ bool bLayouted( false );
+
+ if ( bNoLock && xDockingAreaAcceptor.is() && xContainerWindow.is() && xComponentWindow.is() )
+ {
+ bLayouted = true;
+
+ awt::Rectangle aDockSpace( implts_calcDockingAreaSizes() );
+ awt::Rectangle aBorderSpace( aDockSpace );
+ bool bGotRequestedBorderSpace( true );
+
+ // We have to add the height of a possible status bar
+ aBorderSpace.Height += implts_getStatusBarSize().Height();
+
+ if ( !equalRectangles( aBorderSpace, aCurrBorderSpace ) || bForceRequestBorderSpace || bMustDoLayout )
+ {
+ // we always resize the content window (instead of the complete container window) if we're not set up
+ // to (attempt to) preserve the content window's size
+ if ( bOuterResize && !bPreserveContentSize )
+ bOuterResize = false;
+
+ // maximized windows can resized their content window only, not their container window
+ if ( bOuterResize && xContainerTopWindow.is() && xContainerTopWindow->getIsMaximized() )
+ bOuterResize = false;
+
+ // if the component window does not have a size (yet), then we can't use it to calc the container
+ // window size
+ awt::Rectangle aComponentRect = xComponentWindow->getPosSize();
+ if ( bOuterResize && ( aComponentRect.Width == 0 ) && ( aComponentRect.Height == 0 ) )
+ bOuterResize = false;
+
+ bGotRequestedBorderSpace = false;
+ if ( bOuterResize )
+ {
+ Reference< awt::XDevice > xDevice( xContainerWindow, uno::UNO_QUERY );
+ awt::DeviceInfo aContainerInfo = xDevice->getInfo();
+
+ awt::Size aRequestedSize( aComponentRect.Width + aContainerInfo.LeftInset + aContainerInfo.RightInset + aBorderSpace.X + aBorderSpace.Width,
+ aComponentRect.Height + aContainerInfo.TopInset + aContainerInfo.BottomInset + aBorderSpace.Y + aBorderSpace.Height );
+ awt::Point aComponentPos( aBorderSpace.X, aBorderSpace.Y );
+
+ bGotRequestedBorderSpace = implts_resizeContainerWindow( aRequestedSize, aComponentPos );
+ }
+
+ // if we did not do a container window resize, or it failed, then use the DockingAcceptor as usual
+ if ( !bGotRequestedBorderSpace )
+ bGotRequestedBorderSpace = xDockingAreaAcceptor->requestDockingAreaSpace( aBorderSpace );
+
+ if ( bGotRequestedBorderSpace )
+ {
+ SolarMutexGuard aWriteGuard;
+ m_aDockingArea = aBorderSpace;
+ m_bMustDoLayout = false;
+ }
+ }
+
+ if ( bGotRequestedBorderSpace )
+ {
+ ::Size aContainerSize;
+ ::Size aStatusBarSize;
+
+ // Interim solution to let the layout method within the
+ // toolbar layout manager.
+ implts_setOffset( implts_getStatusBarSize().Height() );
+ if ( m_xToolbarManager.is() )
+ m_xToolbarManager->setDockingArea( aDockSpace );
+
+ // Subtract status bar size from our container output size. Docking area windows
+ // don't contain the status bar!
+ aStatusBarSize = implts_getStatusBarSize();
+ aContainerSize = implts_getContainerWindowOutputSize();
+ aContainerSize.AdjustHeight( -(aStatusBarSize.Height()) );
+
+ if ( m_xToolbarManager.is() )
+ m_xToolbarManager->doLayout(aContainerSize);
+
+ // Position the status bar
+ if ( aStatusBarSize.Height() > 0 )
+ {
+ implts_setStatusBarPosSize( ::Point( 0, std::max(( aContainerSize.Height() ), tools::Long( 0 ))),
+ ::Size( aContainerSize.Width(),aStatusBarSize.Height() ));
+ }
+
+ xDockingAreaAcceptor->setDockingAreaSpace( aBorderSpace );
+ }
+ }
+
+ return bLayouted;
+}
+
+bool LayoutManager::implts_resizeContainerWindow( const awt::Size& rContainerSize,
+ const awt::Point& rComponentPos )
+{
+ SolarMutexClearableGuard aReadLock;
+ Reference< awt::XWindow > xContainerWindow = m_xContainerWindow;
+ Reference< awt::XTopWindow2 > xContainerTopWindow = m_xContainerTopWindow;
+ Reference< awt::XWindow > xComponentWindow = m_xFrame->getComponentWindow();
+ aReadLock.clear();
+
+ // calculate the maximum size we have for the container window
+ sal_Int32 nDisplay = xContainerTopWindow->getDisplay();
+ tools::Rectangle aWorkArea = Application::GetScreenPosSizePixel( nDisplay );
+
+ if (!aWorkArea.IsEmpty())
+ {
+ if (( rContainerSize.Width > aWorkArea.GetWidth() ) || ( rContainerSize.Height > aWorkArea.GetHeight() ))
+ return false;
+ // Strictly, this is not correct. If we have a multi-screen display (css.awt.DisplayAccess.MultiDisplay == true),
+ // the "effective work area" would be much larger than the work area of a single display, since we could in theory
+ // position the container window across multiple screens.
+ // However, this should suffice as a heuristics here ... (nobody really wants to check whether the different screens are
+ // stacked horizontally or vertically, whether their work areas can really be combined, or are separated by non-work-areas,
+ // and the like ... right?)
+ }
+
+ // resize our container window
+ xContainerWindow->setPosSize( 0, 0, rContainerSize.Width, rContainerSize.Height, awt::PosSize::SIZE );
+ // position the component window
+ xComponentWindow->setPosSize( rComponentPos.X, rComponentPos.Y, 0, 0, awt::PosSize::POS );
+ return true;
+}
+
+void SAL_CALL LayoutManager::setVisible( sal_Bool bVisible )
+{
+ SolarMutexClearableGuard aWriteLock;
+ bool bWasVisible( m_bVisible );
+ m_bVisible = bVisible;
+ aWriteLock.clear();
+
+ if ( bWasVisible != bool(bVisible) )
+ implts_setVisibleState( bVisible );
+}
+
+sal_Bool SAL_CALL LayoutManager::isVisible()
+{
+ SolarMutexGuard g;
+ return m_bVisible;
+}
+
+::Size LayoutManager::implts_getStatusBarSize()
+{
+ SolarMutexClearableGuard aReadLock;
+ bool bStatusBarVisible( isElementVisible( STATUS_BAR_ALIAS ));
+ bool bProgressBarVisible( isElementVisible( "private:resource/progressbar/progressbar" ));
+ bool bVisible( m_bVisible );
+ Reference< XUIElement > xStatusBar( m_aStatusBarElement.m_xUIElement );
+ Reference< XUIElement > xProgressBar( m_aProgressBarElement.m_xUIElement );
+
+ Reference< awt::XWindow > xWindow;
+ if ( bStatusBarVisible && bVisible && xStatusBar.is() )
+ xWindow.set( xStatusBar->getRealInterface(), UNO_QUERY );
+ else if ( xProgressBar.is() && !xStatusBar.is() && bProgressBarVisible )
+ {
+ ProgressBarWrapper* pWrapper = static_cast<ProgressBarWrapper*>(xProgressBar.get());
+ if ( pWrapper )
+ xWindow = pWrapper->getStatusBar();
+ }
+ aReadLock.clear();
+
+ if ( xWindow.is() )
+ {
+ awt::Rectangle aPosSize = xWindow->getPosSize();
+ return ::Size( aPosSize.Width, aPosSize.Height );
+ }
+ else
+ return ::Size();
+}
+
+awt::Rectangle LayoutManager::implts_calcDockingAreaSizes()
+{
+ SolarMutexClearableGuard aReadLock;
+ Reference< awt::XWindow > xContainerWindow( m_xContainerWindow );
+ Reference< XDockingAreaAcceptor > xDockingAreaAcceptor( m_xDockingAreaAcceptor );
+ aReadLock.clear();
+
+ awt::Rectangle aBorderSpace;
+ if ( m_xToolbarManager.is() && xDockingAreaAcceptor.is() && xContainerWindow.is() )
+ aBorderSpace = m_xToolbarManager->getDockingArea();
+
+ return aBorderSpace;
+}
+
+void LayoutManager::implts_setDockingAreaWindowSizes()
+{
+ SolarMutexClearableGuard aReadLock;
+ Reference< awt::XWindow > xContainerWindow( m_xContainerWindow );
+ aReadLock.clear();
+
+ uno::Reference< awt::XDevice > xDevice( xContainerWindow, uno::UNO_QUERY );
+ // Convert relative size to output size.
+ awt::Rectangle aRectangle = xContainerWindow->getPosSize();
+ awt::DeviceInfo aInfo = xDevice->getInfo();
+ awt::Size aContainerClientSize( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset,
+ aRectangle.Height - aInfo.TopInset - aInfo.BottomInset );
+ ::Size aStatusBarSize = implts_getStatusBarSize();
+
+ // Position the status bar
+ if ( aStatusBarSize.Height() > 0 )
+ {
+ implts_setStatusBarPosSize( ::Point( 0, std::max(( aContainerClientSize.Height - aStatusBarSize.Height() ), tools::Long( 0 ))),
+ ::Size( aContainerClientSize.Width, aStatusBarSize.Height() ));
+ }
+}
+
+void LayoutManager::implts_updateMenuBarClose()
+{
+ SolarMutexClearableGuard aWriteLock;
+ bool bShowCloseButton( m_bMenuBarCloseButton );
+ Reference< awt::XWindow > xContainerWindow( m_xContainerWindow );
+ aWriteLock.clear();
+
+ if ( !xContainerWindow.is() )
+ return;
+
+ SolarMutexGuard aGuard;
+
+ SystemWindow* pSysWindow = getTopSystemWindow( xContainerWindow );
+ if ( pSysWindow )
+ {
+ MenuBar* pMenuBar = pSysWindow->GetMenuBar();
+ if ( pMenuBar )
+ {
+ // TODO remove link on sal_False ?!
+ pMenuBar->ShowCloseButton(bShowCloseButton);
+ pMenuBar->SetCloseButtonClickHdl(LINK(this, LayoutManager, MenuBarClose));
+ }
+ }
+}
+
+bool LayoutManager::implts_resetMenuBar()
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexGuard aWriteLock;
+ bool bMenuVisible( m_bMenuVisible );
+ Reference< awt::XWindow > xContainerWindow( m_xContainerWindow );
+
+ MenuBar* pSetMenuBar = nullptr;
+ if ( m_xInplaceMenuBar.is() )
+ pSetMenuBar = static_cast<MenuBar *>(m_xInplaceMenuBar->GetMenuBar());
+ else
+ {
+ MenuBarWrapper* pMenuBarWrapper = static_cast< MenuBarWrapper* >( m_xMenuBar.get() );
+ if ( pMenuBarWrapper )
+ pSetMenuBar = static_cast<MenuBar*>(pMenuBarWrapper->GetMenuBarManager()->GetMenuBar());
+ }
+
+ SystemWindow* pSysWindow = getTopSystemWindow( xContainerWindow );
+ if ( pSysWindow && bMenuVisible && pSetMenuBar )
+ {
+ pSysWindow->SetMenuBar(pSetMenuBar);
+ pSetMenuBar->SetDisplayable( true );
+ return true;
+ }
+
+ return false;
+}
+
+void LayoutManager::implts_createMSCompatibleMenuBar( const OUString& aName )
+{
+ SolarMutexGuard aWriteLock;
+
+ // Find Form menu in the original menubar
+ m_xMenuBar = implts_createElement( aName );
+ uno::Reference< XUIElementSettings > xMenuBarSettings(m_xMenuBar, UNO_QUERY);
+ uno::Reference< container::XIndexReplace > xMenuIndex(xMenuBarSettings->getSettings(true), UNO_QUERY);
+
+ sal_Int32 nFormsMenu = -1;
+ for (sal_Int32 nIndex = 0; nIndex < xMenuIndex->getCount(); ++nIndex)
+ {
+ uno::Sequence< beans::PropertyValue > aProps;
+ xMenuIndex->getByIndex( nIndex ) >>= aProps;
+ OUString aCommand;
+ for ( beans::PropertyValue const & rProp : std::as_const(aProps) )
+ {
+ if (rProp.Name == "CommandURL")
+ {
+ rProp.Value >>= aCommand;
+ break;
+ }
+ }
+
+ if (aCommand == ".uno:FormatFormMenu")
+ nFormsMenu = nIndex;
+ }
+ assert(nFormsMenu != -1);
+
+ // Create the MS compatible Form menu
+ css::uno::Reference< css::ui::XUIElement > xFormsMenu = implts_createElement( "private:resource/menubar/mscompatibleformsmenu" );
+ if(!xFormsMenu.is())
+ return;
+
+ // Merge the MS compatible Form menu into the menubar
+ uno::Reference< XUIElementSettings > xFormsMenuSettings(xFormsMenu, UNO_QUERY);
+ uno::Reference< container::XIndexAccess > xFormsMenuIndex(xFormsMenuSettings->getSettings(true));
+
+ assert(xFormsMenuIndex->getCount() >= 1);
+ uno::Sequence< beans::PropertyValue > aNewFormsMenu;
+ xFormsMenuIndex->getByIndex( 0 ) >>= aNewFormsMenu;
+ xMenuIndex->replaceByIndex(nFormsMenu, uno::Any(aNewFormsMenu));
+
+ setMergedMenuBar( xMenuIndex );
+
+ // Clear up the temporal forms menubar
+ Reference< XComponent > xFormsMenuComp( xFormsMenu, UNO_QUERY );
+ if ( xFormsMenuComp.is() )
+ xFormsMenuComp->dispose();
+ xFormsMenu.clear();
+}
+
+IMPL_LINK_NOARG(LayoutManager, MenuBarClose, void*, void)
+{
+ SolarMutexClearableGuard aReadLock;
+ uno::Reference< frame::XDispatchProvider > xProvider(m_xFrame, uno::UNO_QUERY);
+ uno::Reference< XComponentContext > xContext( m_xContext );
+ aReadLock.clear();
+
+ if ( !xProvider.is())
+ return;
+
+ uno::Reference< frame::XDispatchHelper > xDispatcher = frame::DispatchHelper::create( xContext );
+
+ xDispatcher->executeDispatch(
+ xProvider,
+ ".uno:CloseWin",
+ "_self",
+ 0,
+ uno::Sequence< beans::PropertyValue >());
+}
+
+// XLayoutManagerEventBroadcaster
+
+void SAL_CALL LayoutManager::addLayoutManagerEventListener( const uno::Reference< frame::XLayoutManagerListener >& xListener )
+{
+ m_aListenerContainer.addInterface( cppu::UnoType<frame::XLayoutManagerListener>::get(), xListener );
+}
+
+void SAL_CALL LayoutManager::removeLayoutManagerEventListener( const uno::Reference< frame::XLayoutManagerListener >& xListener )
+{
+ m_aListenerContainer.removeInterface( cppu::UnoType<frame::XLayoutManagerListener>::get(), xListener );
+}
+
+void LayoutManager::implts_notifyListeners(short nEvent, const uno::Any& rInfoParam)
+{
+ comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( cppu::UnoType<frame::XLayoutManagerListener>::get());
+ if (pContainer==nullptr)
+ return;
+
+ lang::EventObject aSource( static_cast< ::cppu::OWeakObject*>(this) );
+ comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer);
+ while (pIterator.hasMoreElements())
+ {
+ try
+ {
+ static_cast<frame::XLayoutManagerListener*>(pIterator.next())->layoutEvent(aSource, nEvent, rInfoParam);
+ }
+ catch( const uno::RuntimeException& )
+ {
+ pIterator.remove();
+ }
+ }
+}
+
+// XWindowListener
+
+void SAL_CALL LayoutManager::windowResized( const awt::WindowEvent& aEvent )
+{
+ SolarMutexGuard g;
+ Reference< awt::XWindow > xContainerWindow( m_xContainerWindow );
+
+ Reference< XInterface > xIfac( xContainerWindow, UNO_QUERY );
+ if ( xIfac == aEvent.Source && m_bVisible )
+ {
+ // We have to call our resize handler at least once synchronously, as some
+ // application modules need this. So we have to check if this is the first
+ // call after the async layout time expired.
+ m_bMustDoLayout = true;
+ if ( !m_aAsyncLayoutTimer.IsActive() )
+ {
+ m_aAsyncLayoutTimer.Invoke();
+ if ( m_nLockCount == 0 )
+ m_aAsyncLayoutTimer.Start();
+ }
+ }
+ else if ( m_xFrame.is() && aEvent.Source == m_xFrame->getContainerWindow() )
+ {
+ // the container window of my DockingAreaAcceptor is not the same as of my frame
+ // I still have to resize my frames' window as nobody else will do it
+ Reference< awt::XWindow > xComponentWindow( m_xFrame->getComponentWindow() );
+ if( xComponentWindow.is() )
+ {
+ uno::Reference< awt::XDevice > xDevice( m_xFrame->getContainerWindow(), uno::UNO_QUERY );
+
+ // Convert relative size to output size.
+ awt::Rectangle aRectangle = m_xFrame->getContainerWindow()->getPosSize();
+ awt::DeviceInfo aInfo = xDevice->getInfo();
+ awt::Size aSize( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset ,
+ aRectangle.Height - aInfo.TopInset - aInfo.BottomInset );
+
+ // Resize our component window.
+ xComponentWindow->setPosSize( 0, 0, aSize.Width, aSize.Height, awt::PosSize::POSSIZE );
+ }
+ }
+}
+
+void SAL_CALL LayoutManager::windowMoved( const awt::WindowEvent& )
+{
+}
+
+void SAL_CALL LayoutManager::windowShown( const lang::EventObject& aEvent )
+{
+ SolarMutexClearableGuard aReadLock;
+ Reference< awt::XWindow > xContainerWindow( m_xContainerWindow );
+ bool bParentWindowVisible( m_bParentWindowVisible );
+ aReadLock.clear();
+
+ Reference< XInterface > xIfac( xContainerWindow, UNO_QUERY );
+ if ( xIfac == aEvent.Source )
+ {
+ SolarMutexClearableGuard aWriteLock;
+ m_bParentWindowVisible = true;
+ bool bSetVisible = ( m_bParentWindowVisible != bParentWindowVisible );
+ aWriteLock.clear();
+
+ if ( bSetVisible )
+ implts_updateUIElementsVisibleState( true );
+ }
+}
+
+void SAL_CALL LayoutManager::windowHidden( const lang::EventObject& aEvent )
+{
+ SolarMutexClearableGuard aReadLock;
+ Reference< awt::XWindow > xContainerWindow( m_xContainerWindow );
+ bool bParentWindowVisible( m_bParentWindowVisible );
+ aReadLock.clear();
+
+ Reference< XInterface > xIfac( xContainerWindow, UNO_QUERY );
+ if ( xIfac == aEvent.Source )
+ {
+ SolarMutexClearableGuard aWriteLock;
+ m_bParentWindowVisible = false;
+ bool bSetInvisible = ( m_bParentWindowVisible != bParentWindowVisible );
+ aWriteLock.clear();
+
+ if ( bSetInvisible )
+ implts_updateUIElementsVisibleState( false );
+ }
+}
+
+IMPL_LINK_NOARG(LayoutManager, AsyncLayoutHdl, Timer *, void)
+{
+ {
+ SolarMutexGuard aReadLock;
+
+ if (!m_xContainerWindow.is())
+ return;
+ }
+
+ implts_setDockingAreaWindowSizes();
+ implts_doLayout( true, false );
+}
+
+// XFrameActionListener
+
+void SAL_CALL LayoutManager::frameAction( const FrameActionEvent& aEvent )
+{
+ if (( aEvent.Action == FrameAction_COMPONENT_ATTACHED ) || ( aEvent.Action == FrameAction_COMPONENT_REATTACHED ))
+ {
+ SAL_INFO( "fwk", "LayoutManager::frameAction (COMPONENT_ATTACHED|REATTACHED)" );
+
+ {
+ SolarMutexGuard aWriteLock;
+ m_bMustDoLayout = true;
+ }
+
+ implts_reset( true );
+ implts_doLayout( true, false );
+ implts_doLayout( true, true );
+ }
+ else if (( aEvent.Action == FrameAction_FRAME_UI_ACTIVATED ) || ( aEvent.Action == FrameAction_FRAME_UI_DEACTIVATING ))
+ {
+ SAL_INFO( "fwk", "LayoutManager::frameAction (FRAME_UI_ACTIVATED|DEACTIVATING)" );
+
+ implts_toggleFloatingUIElementsVisibility( aEvent.Action == FrameAction_FRAME_UI_ACTIVATED );
+ }
+ else if ( aEvent.Action == FrameAction_COMPONENT_DETACHING )
+ {
+ SAL_INFO( "fwk", "LayoutManager::frameAction (COMPONENT_DETACHING)" );
+
+ implts_reset( false );
+ }
+}
+
+void SAL_CALL LayoutManager::disposing( const lang::EventObject& rEvent )
+{
+ bool bDisposeAndClear( false );
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ {
+ SolarMutexGuard aWriteLock;
+
+ if (rEvent.Source == Reference<XInterface>(m_xFrame, UNO_QUERY))
+ {
+ // Our frame gets disposed, release all our references that depends on a working frame reference.
+
+ setDockingAreaAcceptor(Reference<ui::XDockingAreaAcceptor>());
+
+ // destroy all elements, it's possible that detaching is NOT called!
+ implts_destroyElements();
+ impl_clearUpMenuBar();
+ m_xMenuBar.clear();
+ VclPtr<Menu> pMenuBar;
+ if (m_xInplaceMenuBar.is())
+ {
+ pMenuBar = m_xInplaceMenuBar->GetMenuBar();
+ m_xInplaceMenuBar->dispose();
+ m_xInplaceMenuBar.clear();
+ }
+ pMenuBar.disposeAndClear();
+ m_xContainerWindow.clear();
+ m_xContainerTopWindow.clear();
+
+ // forward disposing call to toolbar manager
+ if (m_xToolbarManager.is())
+ m_xToolbarManager->disposing(rEvent);
+
+ if (m_xModuleCfgMgr.is())
+ {
+ try
+ {
+ Reference<XUIConfiguration> xModuleCfgMgr(m_xModuleCfgMgr, UNO_QUERY);
+ xModuleCfgMgr->removeConfigurationListener(Reference<XUIConfigurationListener>(this));
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ if (m_xDocCfgMgr.is())
+ {
+ try
+ {
+ Reference<XUIConfiguration> xDocCfgMgr(m_xDocCfgMgr, UNO_QUERY);
+ xDocCfgMgr->removeConfigurationListener(Reference<XUIConfigurationListener>(this));
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ m_xDocCfgMgr.clear();
+ m_xModuleCfgMgr.clear();
+ m_xFrame.clear();
+ m_pGlobalSettings.reset();
+
+ bDisposeAndClear = true;
+ }
+ else if (rEvent.Source == Reference<XInterface>(m_xContainerWindow, UNO_QUERY))
+ {
+ // Our container window gets disposed. Remove all user interface elements.
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ if (pToolbarManager)
+ {
+ uno::Reference<awt::XWindowPeer> aEmptyWindowPeer;
+ pToolbarManager->setParentWindow(aEmptyWindowPeer);
+ }
+ impl_clearUpMenuBar();
+ m_xMenuBar.clear();
+ VclPtr<Menu> pMenuBar;
+ if (m_xInplaceMenuBar.is())
+ {
+ pMenuBar = m_xInplaceMenuBar->GetMenuBar();
+ m_xInplaceMenuBar->dispose();
+ m_xInplaceMenuBar.clear();
+ }
+ pMenuBar.disposeAndClear();
+ m_xContainerWindow.clear();
+ m_xContainerTopWindow.clear();
+ }
+ else if (rEvent.Source == Reference<XInterface>(m_xDocCfgMgr, UNO_QUERY))
+ m_xDocCfgMgr.clear();
+ else if (rEvent.Source == Reference<XInterface>(m_xModuleCfgMgr, UNO_QUERY))
+ m_xModuleCfgMgr.clear();
+ }
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ // Send disposing to our listener when we have lost our frame.
+ if ( bDisposeAndClear )
+ {
+ // Send message to all listener and forget her references.
+ uno::Reference< frame::XLayoutManager > xThis(this);
+ lang::EventObject aEvent( xThis );
+ m_aListenerContainer.disposeAndClear( aEvent );
+ }
+}
+
+void SAL_CALL LayoutManager::elementInserted( const ui::ConfigurationEvent& Event )
+{
+ SolarMutexClearableGuard aReadLock;
+ Reference< XFrame > xFrame( m_xFrame );
+ rtl::Reference< ToolbarLayoutManager > xToolbarManager( m_xToolbarManager );
+ aReadLock.clear();
+
+ if ( !xFrame.is() )
+ return;
+
+ OUString aElementType;
+ OUString aElementName;
+ bool bRefreshLayout(false);
+
+ parseResourceURL( Event.ResourceURL, aElementType, aElementName );
+ if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR ))
+ {
+ if ( xToolbarManager.is() )
+ {
+ xToolbarManager->elementInserted( Event );
+ bRefreshLayout = xToolbarManager->isLayoutDirty();
+ }
+ }
+ else if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_MENUBAR ))
+ {
+ Reference< XUIElement > xUIElement = implts_findElement( Event.ResourceURL );
+ Reference< XUIElementSettings > xElementSettings( xUIElement, UNO_QUERY );
+ if ( xElementSettings.is() )
+ {
+ uno::Reference< XPropertySet > xPropSet( xElementSettings, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ if ( Event.Source == uno::Reference< uno::XInterface >( m_xDocCfgMgr, uno::UNO_QUERY ))
+ xPropSet->setPropertyValue( "ConfigurationSource", Any( m_xDocCfgMgr ));
+ }
+ xElementSettings->updateSettings();
+ }
+ }
+
+ if ( bRefreshLayout )
+ doLayout();
+}
+
+void SAL_CALL LayoutManager::elementRemoved( const ui::ConfigurationEvent& Event )
+{
+ SolarMutexClearableGuard aReadLock;
+ Reference< frame::XFrame > xFrame( m_xFrame );
+ rtl::Reference< ToolbarLayoutManager > xToolbarManager( m_xToolbarManager );
+ Reference< awt::XWindow > xContainerWindow( m_xContainerWindow );
+ Reference< ui::XUIElement > xMenuBar( m_xMenuBar );
+ Reference< ui::XUIConfigurationManager > xModuleCfgMgr( m_xModuleCfgMgr );
+ Reference< ui::XUIConfigurationManager > xDocCfgMgr( m_xDocCfgMgr );
+ aReadLock.clear();
+
+ if ( !xFrame.is() )
+ return;
+
+ OUString aElementType;
+ OUString aElementName;
+ bool bRefreshLayout(false);
+
+ parseResourceURL( Event.ResourceURL, aElementType, aElementName );
+ if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR ))
+ {
+ if ( xToolbarManager.is() )
+ {
+ xToolbarManager->elementRemoved( Event );
+ bRefreshLayout = xToolbarManager->isLayoutDirty();
+ }
+ }
+ else
+ {
+ Reference< XUIElement > xUIElement = implts_findElement( Event.ResourceURL );
+ Reference< XUIElementSettings > xElementSettings( xUIElement, UNO_QUERY );
+ if ( xElementSettings.is() )
+ {
+ bool bNoSettings( false );
+ OUString aConfigSourcePropName( "ConfigurationSource" );
+ Reference< XInterface > xElementCfgMgr;
+ Reference< XPropertySet > xPropSet( xElementSettings, UNO_QUERY );
+
+ if ( xPropSet.is() )
+ xPropSet->getPropertyValue( aConfigSourcePropName ) >>= xElementCfgMgr;
+
+ if ( !xElementCfgMgr.is() )
+ return;
+
+ // Check if the same UI configuration manager has changed => check further
+ if ( Event.Source == xElementCfgMgr )
+ {
+ // Same UI configuration manager where our element has its settings
+ if ( Event.Source == Reference< XInterface >( xDocCfgMgr, UNO_QUERY ))
+ {
+ // document settings removed
+ if ( xModuleCfgMgr->hasSettings( Event.ResourceURL ))
+ {
+ xPropSet->setPropertyValue( aConfigSourcePropName, Any( m_xModuleCfgMgr ));
+ xElementSettings->updateSettings();
+ return;
+ }
+ }
+
+ bNoSettings = true;
+ }
+
+ // No settings anymore, element must be destroyed
+ if ( xContainerWindow.is() && bNoSettings )
+ {
+ if ( aElementType.equalsIgnoreAsciiCase("menubar") &&
+ aElementName.equalsIgnoreAsciiCase("menubar") )
+ {
+ SystemWindow* pSysWindow = getTopSystemWindow( xContainerWindow );
+ if ( pSysWindow && !m_bInplaceMenuSet )
+ pSysWindow->SetMenuBar( nullptr );
+
+ Reference< XComponent > xComp( xMenuBar, UNO_QUERY );
+ if ( xComp.is() )
+ xComp->dispose();
+
+ SolarMutexGuard g;
+ m_xMenuBar.clear();
+ }
+ }
+ }
+ }
+
+ if ( bRefreshLayout )
+ doLayout();
+}
+
+void SAL_CALL LayoutManager::elementReplaced( const ui::ConfigurationEvent& Event )
+{
+ SolarMutexClearableGuard aReadLock;
+ Reference< XFrame > xFrame( m_xFrame );
+ rtl::Reference< ToolbarLayoutManager > xToolbarManager( m_xToolbarManager );
+ aReadLock.clear();
+
+ if ( !xFrame.is() )
+ return;
+
+ OUString aElementType;
+ OUString aElementName;
+ bool bRefreshLayout(false);
+
+ parseResourceURL( Event.ResourceURL, aElementType, aElementName );
+ if ( aElementType.equalsIgnoreAsciiCase( UIRESOURCETYPE_TOOLBAR ))
+ {
+ if ( xToolbarManager.is() )
+ {
+ xToolbarManager->elementReplaced( Event );
+ bRefreshLayout = xToolbarManager->isLayoutDirty();
+ }
+ }
+ else
+ {
+ Reference< XUIElement > xUIElement = implts_findElement( Event.ResourceURL );
+ Reference< XUIElementSettings > xElementSettings( xUIElement, UNO_QUERY );
+ if ( xElementSettings.is() )
+ {
+ Reference< XInterface > xElementCfgMgr;
+ Reference< XPropertySet > xPropSet( xElementSettings, UNO_QUERY );
+
+ if ( xPropSet.is() )
+ xPropSet->getPropertyValue( "ConfigurationSource" ) >>= xElementCfgMgr;
+
+ if ( !xElementCfgMgr.is() )
+ return;
+
+ // Check if the same UI configuration manager has changed => update settings
+ if ( Event.Source == xElementCfgMgr )
+ xElementSettings->updateSettings();
+ }
+ }
+
+ if ( bRefreshLayout )
+ doLayout();
+}
+
+void SAL_CALL LayoutManager::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle,
+ const uno::Any& aValue )
+{
+ if ( nHandle != LAYOUTMANAGER_PROPHANDLE_REFRESHVISIBILITY )
+ LayoutManager_PBase::setFastPropertyValue_NoBroadcast( nHandle, aValue );
+
+ switch( nHandle )
+ {
+ case LAYOUTMANAGER_PROPHANDLE_MENUBARCLOSER:
+ implts_updateMenuBarClose();
+ break;
+
+ case LAYOUTMANAGER_PROPHANDLE_REFRESHVISIBILITY:
+ {
+ bool bValue(false);
+ if (( aValue >>= bValue ) && bValue )
+ {
+ SolarMutexClearableGuard aReadLock;
+ ToolbarLayoutManager* pToolbarManager = m_xToolbarManager.get();
+ bool bAutomaticToolbars( m_bAutomaticToolbars );
+ aReadLock.clear();
+
+ if ( pToolbarManager )
+ pToolbarManager->refreshToolbarsVisibility( bAutomaticToolbars );
+ }
+ break;
+ }
+
+ case LAYOUTMANAGER_PROPHANDLE_HIDECURRENTUI:
+ implts_setCurrentUIVisibility( !m_bHideCurrentUI );
+ break;
+ default: break;
+ }
+}
+
+namespace detail
+{
+ class InfoHelperBuilder
+ {
+ private:
+ std::unique_ptr<::cppu::OPropertyArrayHelper> m_pInfoHelper;
+ public:
+ explicit InfoHelperBuilder(const LayoutManager &rManager)
+ {
+ uno::Sequence< beans::Property > aProperties;
+ rManager.describeProperties(aProperties);
+ m_pInfoHelper.reset( new ::cppu::OPropertyArrayHelper(aProperties, true) );
+ }
+ InfoHelperBuilder(const InfoHelperBuilder&) = delete;
+ InfoHelperBuilder& operator=(const InfoHelperBuilder&) = delete;
+
+ ::cppu::OPropertyArrayHelper& getHelper() { return *m_pInfoHelper; }
+ };
+}
+
+::cppu::IPropertyArrayHelper& SAL_CALL LayoutManager::getInfoHelper()
+{
+ static detail::InfoHelperBuilder INFO(*this);
+ return INFO.getHelper();
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL LayoutManager::getPropertySetInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+
+ return xInfo;
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_LayoutManager_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new framework::LayoutManager(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/layoutmanager/toolbarlayoutmanager.cxx b/framework/source/layoutmanager/toolbarlayoutmanager.cxx
new file mode 100644
index 000000000..34b2b0899
--- /dev/null
+++ b/framework/source/layoutmanager/toolbarlayoutmanager.cxx
@@ -0,0 +1,4116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "toolbarlayoutmanager.hxx"
+#include <uiconfiguration/windowstateproperties.hxx>
+#include <uielement/addonstoolbarwrapper.hxx>
+#include "helpers.hxx"
+#include <services/layoutmanager.hxx>
+#include <strings.hrc>
+#include <classes/fwkresid.hxx>
+
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/ui/UIElementType.hpp>
+#include <com/sun/star/container/XNameReplace.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/ui/XUIElementSettings.hpp>
+#include <com/sun/star/ui/XUIFunctionListener.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <o3tl/string_view.hxx>
+#include <unotools/cmdoptions.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <utility>
+#include <vcl/i18nhelp.hxx>
+#include <vcl/dockingarea.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+#include <tools/gen.hxx>
+
+
+using namespace ::com::sun::star;
+
+namespace framework
+{
+
+ToolbarLayoutManager::ToolbarLayoutManager(
+ uno::Reference< uno::XComponentContext > xContext,
+ uno::Reference< ui::XUIElementFactory > xUIElementFactory,
+ LayoutManager* pParentLayouter ):
+ m_xContext(std::move( xContext)),
+ m_xUIElementFactoryManager(std::move( xUIElementFactory )),
+ m_pParentLayouter( pParentLayouter ),
+ m_aDockingArea(0, 0, 0, 0),
+ m_aDockingAreaOffsets(0, 0, 0, 0),
+ m_eDockOperation( DOCKOP_ON_COLROW ),
+ m_ePreviewDetection( PREVIEWFRAME_UNKNOWN ),
+ m_bComponentAttached( false ),
+ m_bLayoutDirty( false ),
+ m_bGlobalSettings( false ),
+ m_bDockingInProgress( false ),
+ m_bLayoutInProgress( false ),
+ m_bToolbarCreation( false )
+{
+}
+
+ToolbarLayoutManager::~ToolbarLayoutManager()
+{
+ m_pGlobalSettings.reset();
+ m_pAddonOptions.reset();
+}
+
+// XInterface
+
+void SAL_CALL ToolbarLayoutManager::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL ToolbarLayoutManager::release() noexcept
+{
+ OWeakObject::release();
+}
+
+uno::Any SAL_CALL ToolbarLayoutManager::queryInterface( const uno::Type & rType )
+{
+ uno::Any a = ::cppu::queryInterface( rType,
+ static_cast< awt::XDockableWindowListener* >(this),
+ static_cast< ui::XUIConfigurationListener* >(this),
+ static_cast< awt::XWindowListener* >(this));
+
+ if ( a.hasValue() )
+ return a;
+
+ return OWeakObject::queryInterface( rType );
+}
+
+void SAL_CALL ToolbarLayoutManager::disposing( const lang::EventObject& aEvent )
+{
+ if ( aEvent.Source == m_xFrame )
+ {
+ // Reset all internal references
+ reset();
+ implts_destroyDockingAreaWindows();
+ }
+}
+
+awt::Rectangle ToolbarLayoutManager::getDockingArea()
+{
+ SolarMutexResettableGuard aWriteLock;
+ tools::Rectangle aNewDockingArea( m_aDockingArea );
+ aWriteLock.clear();
+
+ if ( isLayoutDirty() )
+ aNewDockingArea = implts_calcDockingArea();
+
+ aWriteLock.reset();
+ m_aDockingArea = aNewDockingArea;
+ aWriteLock.clear();
+
+ return putRectangleValueToAWT(aNewDockingArea);
+}
+
+void ToolbarLayoutManager::setDockingArea( const awt::Rectangle& rDockingArea )
+{
+ SolarMutexGuard g;
+ m_aDockingArea = putAWTToRectangle( rDockingArea );
+ m_bLayoutDirty = true;
+}
+
+void ToolbarLayoutManager::implts_setDockingAreaWindowSizes( const awt::Rectangle& rBorderSpace )
+{
+ SolarMutexClearableGuard aReadLock;
+ tools::Rectangle aDockOffsets = m_aDockingAreaOffsets;
+ uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow );
+ uno::Reference< awt::XWindow > xTopDockAreaWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] );
+ uno::Reference< awt::XWindow > xBottomDockAreaWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] );
+ uno::Reference< awt::XWindow > xLeftDockAreaWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] );
+ uno::Reference< awt::XWindow > xRightDockAreaWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] );
+ aReadLock.clear();
+
+ uno::Reference< awt::XDevice > xDevice( xContainerWindow, uno::UNO_QUERY );
+
+ // Convert relative size to output size.
+ awt::Rectangle aRectangle = xContainerWindow->getPosSize();
+ awt::DeviceInfo aInfo = xDevice->getInfo();
+ awt::Size aContainerClientSize( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset ,
+ aRectangle.Height - aInfo.TopInset - aInfo.BottomInset );
+ tools::Long aStatusBarHeight = aDockOffsets.GetHeight();
+
+ sal_Int32 nLeftRightDockingAreaHeight( aContainerClientSize.Height );
+ if ( rBorderSpace.Y >= 0 )
+ {
+ // Top docking area window
+ xTopDockAreaWindow->setPosSize( 0, 0, aContainerClientSize.Width, rBorderSpace.Y, awt::PosSize::POSSIZE );
+ xTopDockAreaWindow->setVisible( true );
+ nLeftRightDockingAreaHeight -= rBorderSpace.Y;
+ }
+
+ if ( rBorderSpace.Height >= 0 )
+ {
+ // Bottom docking area window
+ sal_Int32 nBottomPos = std::max( sal_Int32( aContainerClientSize.Height - rBorderSpace.Height - aStatusBarHeight + 1 ), sal_Int32( 0 ));
+ sal_Int32 nHeight = ( nBottomPos == 0 ) ? 0 : rBorderSpace.Height;
+
+ xBottomDockAreaWindow->setPosSize( 0, nBottomPos, aContainerClientSize.Width, nHeight, awt::PosSize::POSSIZE );
+ xBottomDockAreaWindow->setVisible( true );
+ nLeftRightDockingAreaHeight -= nHeight - 1;
+ }
+
+ nLeftRightDockingAreaHeight -= aStatusBarHeight;
+ if ( rBorderSpace.X >= 0 || nLeftRightDockingAreaHeight > 0 )
+ {
+ // Left docking area window
+ // We also have to change our right docking area window if the top or bottom area has changed. They have a higher priority!
+ sal_Int32 nHeight = std::max<sal_Int32>( 0, nLeftRightDockingAreaHeight );
+
+ xLeftDockAreaWindow->setPosSize( 0, rBorderSpace.Y, rBorderSpace.X, nHeight, awt::PosSize::POSSIZE );
+ xLeftDockAreaWindow->setVisible( true );
+ }
+ if ( rBorderSpace.Width >= 0 || nLeftRightDockingAreaHeight > 0 )
+ {
+ // Right docking area window
+ // We also have to change our right docking area window if the top or bottom area has changed. They have a higher priority!
+ sal_Int32 nLeftPos = std::max<sal_Int32>( 0, aContainerClientSize.Width - rBorderSpace.Width );
+ sal_Int32 nHeight = std::max<sal_Int32>( 0, nLeftRightDockingAreaHeight );
+ sal_Int32 nWidth = ( nLeftPos == 0 ) ? 0 : rBorderSpace.Width;
+
+ xRightDockAreaWindow->setPosSize( nLeftPos, rBorderSpace.Y, nWidth, nHeight, awt::PosSize::POSSIZE );
+ xRightDockAreaWindow->setVisible( true );
+ }
+}
+
+
+void ToolbarLayoutManager::doLayout(const ::Size& aContainerSize)
+{
+ SolarMutexResettableGuard aWriteLock;
+ bool bLayoutInProgress( m_bLayoutInProgress );
+ m_bLayoutInProgress = true;
+ awt::Rectangle aDockingArea = putRectangleValueToAWT( m_aDockingArea );
+ aWriteLock.clear();
+
+ if ( bLayoutInProgress )
+ return;
+
+ // Retrieve row/column dependent data from all docked user-interface elements
+ for ( sal_Int32 i = 0; i < DOCKINGAREAS_COUNT; i++ )
+ {
+ bool bReverse( isReverseOrderDockingArea( i ));
+ std::vector< SingleRowColumnWindowData > aRowColumnsWindowData;
+
+ implts_getDockingAreaElementInfos( static_cast<ui::DockingArea>(i), aRowColumnsWindowData );
+
+ sal_Int32 nOffset( 0 );
+ const sal_uInt32 nCount = aRowColumnsWindowData.size();
+ for ( sal_uInt32 j = 0; j < nCount; ++j )
+ {
+ sal_uInt32 nIndex = bReverse ? nCount-j-1 : j;
+ implts_calcWindowPosSizeOnSingleRowColumn( i, nOffset, aRowColumnsWindowData[nIndex], aContainerSize );
+ nOffset += aRowColumnsWindowData[j].nStaticSize;
+ }
+ }
+
+ implts_setDockingAreaWindowSizes( aDockingArea );
+
+ aWriteLock.reset();
+ m_bLayoutDirty = false;
+ m_bLayoutInProgress = false;
+ aWriteLock.clear();
+}
+
+bool ToolbarLayoutManager::implts_isParentWindowVisible() const
+{
+ SolarMutexGuard g;
+ bool bVisible( false );
+ if ( m_xContainerWindow.is() )
+ bVisible = m_xContainerWindow->isVisible();
+
+ return bVisible;
+}
+
+tools::Rectangle ToolbarLayoutManager::implts_calcDockingArea()
+{
+ SolarMutexClearableGuard aReadLock;
+ UIElementVector aWindowVector( m_aUIElements );
+ aReadLock.clear();
+
+ tools::Rectangle aBorderSpace;
+ sal_Int32 nCurrRowColumn( 0 );
+ sal_Int32 nCurrPos( 0 );
+ ui::DockingArea nCurrDockingArea( ui::DockingArea_DOCKINGAREA_TOP );
+ std::vector< sal_Int32 > aRowColumnSizes[DOCKINGAREAS_COUNT];
+
+ // initialize rectangle with zero values!
+ aBorderSpace.setWidth(0);
+ aBorderSpace.setHeight(0);
+
+ aRowColumnSizes[static_cast<int>(nCurrDockingArea)].clear();
+ aRowColumnSizes[static_cast<int>(nCurrDockingArea)].push_back( 0 );
+
+ for (auto const& window : aWindowVector)
+ {
+ if ( window.m_xUIElement.is() )
+ {
+ uno::Reference< awt::XWindow > xWindow( window.m_xUIElement->getRealInterface(), uno::UNO_QUERY );
+ uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY );
+ if ( xWindow.is() && xDockWindow.is() )
+ {
+ SolarMutexGuard aGuard;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && !xDockWindow->isFloating() && window.m_bVisible && !window.m_bMasterHide )
+ {
+ awt::Rectangle aPosSize = xWindow->getPosSize();
+ if ( window.m_aDockedData.m_nDockedArea != nCurrDockingArea )
+ {
+ nCurrDockingArea = window.m_aDockedData.m_nDockedArea;
+ nCurrRowColumn = 0;
+ nCurrPos = 0;
+ aRowColumnSizes[static_cast<int>(nCurrDockingArea)].clear();
+ aRowColumnSizes[static_cast<int>(nCurrDockingArea)].push_back( 0 );
+ }
+
+ if ( window.m_aDockedData.m_nDockedArea == nCurrDockingArea )
+ {
+ if ( isHorizontalDockingArea( window.m_aDockedData.m_nDockedArea ))
+ {
+ if ( window.m_aDockedData.m_aPos.Y > nCurrPos )
+ {
+ ++nCurrRowColumn;
+ nCurrPos = window.m_aDockedData.m_aPos.Y;
+ aRowColumnSizes[static_cast<int>(nCurrDockingArea)].push_back( 0 );
+ }
+
+ if ( aPosSize.Height > aRowColumnSizes[static_cast<int>(nCurrDockingArea)][nCurrRowColumn] )
+ aRowColumnSizes[static_cast<int>(nCurrDockingArea)][nCurrRowColumn] = aPosSize.Height;
+ }
+ else
+ {
+ if ( window.m_aDockedData.m_aPos.X > nCurrPos )
+ {
+ ++nCurrRowColumn;
+ nCurrPos = window.m_aDockedData.m_aPos.X;
+ aRowColumnSizes[static_cast<int>(nCurrDockingArea)].push_back( 0 );
+ }
+
+ if ( aPosSize.Width > aRowColumnSizes[static_cast<int>(nCurrDockingArea)][nCurrRowColumn] )
+ aRowColumnSizes[static_cast<int>(nCurrDockingArea)][nCurrRowColumn] = aPosSize.Width;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Sum up max heights from every row/column
+ if ( !aWindowVector.empty() )
+ {
+ for ( sal_Int32 i = 0; i <= sal_Int32(ui::DockingArea_DOCKINGAREA_RIGHT); i++ )
+ {
+ sal_Int32 nSize( 0 );
+ const sal_uInt32 nCount = aRowColumnSizes[i].size();
+ for ( sal_uInt32 j = 0; j < nCount; j++ )
+ nSize += aRowColumnSizes[i][j];
+
+ if ( i == sal_Int32(ui::DockingArea_DOCKINGAREA_TOP) )
+ aBorderSpace.SetTop( nSize );
+ else if ( i == sal_Int32(ui::DockingArea_DOCKINGAREA_BOTTOM) )
+ aBorderSpace.SetBottom( nSize );
+ else if ( i == sal_Int32(ui::DockingArea_DOCKINGAREA_LEFT) )
+ aBorderSpace.SetLeft( nSize );
+ else
+ aBorderSpace.SetRight( nSize );
+ }
+ }
+
+ return aBorderSpace;
+}
+
+void ToolbarLayoutManager::reset()
+{
+ {
+ SolarMutexGuard aWriteLock;
+ m_xModuleCfgMgr.clear();
+ m_xDocCfgMgr.clear();
+ m_ePreviewDetection = PREVIEWFRAME_UNKNOWN;
+ m_bComponentAttached = false;
+ }
+
+ destroyToolbars();
+ resetDockingArea();
+}
+
+void ToolbarLayoutManager::attach(
+ const uno::Reference< frame::XFrame >& xFrame,
+ const uno::Reference< ui::XUIConfigurationManager >& xModuleCfgMgr,
+ const uno::Reference< ui::XUIConfigurationManager >& xDocCfgMgr,
+ const uno::Reference< container::XNameAccess >& xPersistentWindowState )
+{
+ // reset toolbar manager if we lose our current frame
+ if ( m_xFrame.is() && m_xFrame != xFrame )
+ reset();
+
+ SolarMutexGuard g;
+ m_xFrame = xFrame;
+ m_xModuleCfgMgr = xModuleCfgMgr;
+ m_xDocCfgMgr = xDocCfgMgr;
+ m_xPersistentWindowState = xPersistentWindowState;
+ m_bComponentAttached = true;
+}
+
+bool ToolbarLayoutManager::isPreviewFrame()
+{
+ SolarMutexGuard g;
+ if (m_ePreviewDetection == PREVIEWFRAME_UNKNOWN)
+ {
+ uno::Reference< frame::XFrame > xFrame( m_xFrame );
+
+ uno::Reference< frame::XModel > xModel( impl_getModelFromFrame( xFrame ));
+
+ m_ePreviewDetection = (implts_isPreviewModel( xModel ) ? PREVIEWFRAME_YES : PREVIEWFRAME_NO);
+ }
+ return m_ePreviewDetection == PREVIEWFRAME_YES;
+}
+
+void ToolbarLayoutManager::createStaticToolbars()
+{
+ resetDockingArea();
+ implts_createCustomToolBars();
+ implts_createAddonsToolBars();
+ implts_createNonContextSensitiveToolBars();
+ implts_sortUIElements();
+}
+
+bool ToolbarLayoutManager::requestToolbar( const OUString& rResourceURL )
+{
+ if (isPreviewFrame())
+ return false; // no toolbars for preview frame!
+
+ bool bNotify( false );
+ bool bMustCallCreate( false );
+ uno::Reference< ui::XUIElement > xUIElement;
+
+ UIElement aRequestedToolbar = impl_findToolbar( rResourceURL );
+ if ( aRequestedToolbar.m_aName != rResourceURL )
+ {
+ bMustCallCreate = true;
+ aRequestedToolbar.m_aName = rResourceURL;
+ aRequestedToolbar.m_aType = UIRESOURCETYPE_TOOLBAR;
+ aRequestedToolbar.m_xUIElement = xUIElement;
+ implts_readWindowStateData( rResourceURL, aRequestedToolbar );
+ }
+
+ xUIElement = aRequestedToolbar.m_xUIElement;
+ if ( !xUIElement.is() )
+ bMustCallCreate = true;
+
+ bool bCreateOrShowToolbar( aRequestedToolbar.m_bVisible && !aRequestedToolbar.m_bMasterHide );
+
+ if ( bCreateOrShowToolbar )
+ bNotify = bMustCallCreate ? createToolbar( rResourceURL ) : showToolbar( rResourceURL );
+
+ return bNotify;
+}
+
+bool ToolbarLayoutManager::createToolbar( const OUString& rResourceURL )
+{
+ bool bNotify( false );
+
+ uno::Reference<frame::XFrame> xFrame;
+ uno::Reference<awt::XWindow2> xContainerWindow;
+ {
+ SolarMutexGuard aReadLock;
+ xFrame.set(m_xFrame);
+ xContainerWindow.set(m_xContainerWindow);
+ }
+
+ if ( !xFrame.is() || !xContainerWindow.is() )
+ return false;
+
+ UIElement aToolbarElement = implts_findToolbar( rResourceURL );
+ if ( !aToolbarElement.m_xUIElement.is() )
+ {
+ uno::Reference< ui::XUIElement > xUIElement;
+
+ uno::Sequence< beans::PropertyValue > aPropSeq{
+ comphelper::makePropertyValue("Frame", xFrame),
+ comphelper::makePropertyValue("Persistent", true)
+ };
+ uno::Reference<ui::XUIElementFactory> xUIElementFactory;
+ {
+ SolarMutexGuard aReadLock;
+ xUIElementFactory.set(m_xUIElementFactoryManager);
+ }
+
+ implts_setToolbarCreation();
+ try
+ {
+ if ( xUIElementFactory.is() )
+ xUIElement = xUIElementFactory->createUIElement( rResourceURL, aPropSeq );
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ }
+ catch (const lang::IllegalArgumentException&)
+ {
+ }
+ implts_setToolbarCreation( false );
+
+ if ( xUIElement.is() )
+ {
+ uno::Reference< awt::XWindow > xWindow( xUIElement->getRealInterface(), uno::UNO_QUERY );
+ uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY );
+ if ( xDockWindow.is() && xWindow.is() )
+ {
+ try
+ {
+ xDockWindow->addDockableWindowListener( uno::Reference< awt::XDockableWindowListener >(this) );
+ xWindow->addWindowListener( uno::Reference< awt::XWindowListener >(this) );
+ xDockWindow->enableDocking( true );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ bool bVisible = false;
+ bool bFloating = false;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ {
+ SolarMutexClearableGuard aWriteLock;
+
+ UIElement& rElement = impl_findToolbar(rResourceURL);
+ if (rElement.m_xUIElement.is())
+ {
+ // somebody else must have created it while we released
+ // the SolarMutex - just dispose our new instance and
+ // do nothing. (We have to dispose either the new or the
+ // existing m_xUIElement.)
+ aWriteLock.clear();
+ uno::Reference<lang::XComponent> const xC(xUIElement, uno::UNO_QUERY);
+ xC->dispose();
+ return false;
+ }
+ if (!rElement.m_aName.isEmpty())
+ {
+ // Reuse a local entry so we are able to use the latest
+ // UI changes for this document.
+ implts_setElementData(rElement, xDockWindow);
+ rElement.m_xUIElement = xUIElement;
+ bVisible = rElement.m_bVisible;
+ bFloating = rElement.m_bFloating;
+ }
+ else
+ {
+ // Create new UI element and try to read its state data
+ UIElement aNewToolbar(rResourceURL, UIRESOURCETYPE_TOOLBAR, xUIElement);
+ implts_readWindowStateData(rResourceURL, aNewToolbar);
+ implts_setElementData(aNewToolbar, xDockWindow);
+ implts_insertToolbar(aNewToolbar);
+ bVisible = aNewToolbar.m_bVisible;
+ bFloating = rElement.m_bFloating;
+ }
+ }
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ // set toolbar menu style according to customize command state
+ SvtCommandOptions aCmdOptions;
+
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX )
+ {
+ ToolBox* pToolbar = static_cast<ToolBox *>(pWindow.get());
+ ToolBoxMenuType nMenuType = pToolbar->GetMenuType();
+ if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, "ConfigureDialog" ))
+ pToolbar->SetMenuType( nMenuType & ~ToolBoxMenuType::Customize );
+ else
+ pToolbar->SetMenuType( nMenuType | ToolBoxMenuType::Customize );
+ }
+ bNotify = true;
+
+ implts_sortUIElements();
+
+ if ( bVisible && !bFloating )
+ implts_setLayoutDirty();
+ }
+ }
+
+ return bNotify;
+}
+
+bool ToolbarLayoutManager::destroyToolbar( std::u16string_view rResourceURL )
+{
+ uno::Reference< lang::XComponent > xComponent;
+
+ bool bNotify( false );
+ bool bMustBeSorted( false );
+ bool bMustLayouted( false );
+ bool bMustBeDestroyed( !o3tl::starts_with(rResourceURL, u"private:resource/toolbar/addon_") );
+
+ {
+ SolarMutexGuard aWriteLock;
+ for (auto & elem : m_aUIElements)
+ {
+ if (elem.m_aName == rResourceURL)
+ {
+ xComponent.set(elem.m_xUIElement, uno::UNO_QUERY);
+ if (bMustBeDestroyed)
+ elem.m_xUIElement.clear();
+ else
+ elem.m_bVisible = false;
+ break;
+ }
+ }
+ }
+
+ uno::Reference< ui::XUIElement > xUIElement( xComponent, uno::UNO_QUERY );
+ if ( xUIElement.is() )
+ {
+ uno::Reference< awt::XWindow > xWindow( xUIElement->getRealInterface(), uno::UNO_QUERY );
+ uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY );
+
+ if ( bMustBeDestroyed )
+ {
+ try
+ {
+ if ( xWindow.is() )
+ xWindow->removeWindowListener( uno::Reference< awt::XWindowListener >(this) );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ try
+ {
+ if ( xDockWindow.is() )
+ xDockWindow->removeDockableWindowListener( uno::Reference< awt::XDockableWindowListener >(this) );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ else
+ {
+ if ( xWindow.is() )
+ xWindow->setVisible( false );
+ bNotify = true;
+ }
+
+ if ( !xDockWindow->isFloating() )
+ bMustLayouted = true;
+ bMustBeSorted = true;
+ }
+
+ if ( bMustBeDestroyed )
+ {
+ if ( xComponent.is() )
+ xComponent->dispose();
+ bNotify = true;
+ }
+
+ if ( bMustLayouted )
+ implts_setLayoutDirty();
+
+ if ( bMustBeSorted )
+ implts_sortUIElements();
+
+ return bNotify;
+}
+
+void ToolbarLayoutManager::destroyToolbars()
+{
+ UIElementVector aUIElementVector;
+ implts_getUIElementVectorCopy( aUIElementVector );
+
+ {
+ SolarMutexGuard aWriteLock;
+ m_aUIElements.clear();
+ m_bLayoutDirty = true;
+ }
+
+ for (auto const& elem : aUIElementVector)
+ {
+ uno::Reference< lang::XComponent > xComponent( elem.m_xUIElement, uno::UNO_QUERY );
+ if ( xComponent.is() )
+ xComponent->dispose();
+ }
+}
+
+bool ToolbarLayoutManager::showToolbar( std::u16string_view rResourceURL )
+{
+ UIElement aUIElement = implts_findToolbar( rResourceURL );
+
+ SolarMutexGuard aGuard;
+ vcl::Window* pWindow = getWindowFromXUIElement( aUIElement.m_xUIElement );
+
+ // Addons appear to need to be populated at start, but we don't
+ // want to populate them with (scaled) images until later.
+ AddonsToolBarWrapper *pAddOns;
+ pAddOns = dynamic_cast<AddonsToolBarWrapper *>( aUIElement.m_xUIElement.get());
+ if (pAddOns)
+ pAddOns->populateImages();
+
+ if ( pWindow )
+ {
+ if ( !aUIElement.m_bFloating )
+ implts_setLayoutDirty();
+ else
+ pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+
+ aUIElement.m_bVisible = true;
+ implts_writeWindowStateData( aUIElement );
+ implts_setToolbar( aUIElement );
+ implts_sortUIElements();
+ return true;
+ }
+
+ return false;
+}
+
+bool ToolbarLayoutManager::hideToolbar( std::u16string_view rResourceURL )
+{
+ UIElement aUIElement = implts_findToolbar( rResourceURL );
+
+ SolarMutexGuard aGuard;
+ vcl::Window* pWindow = getWindowFromXUIElement( aUIElement.m_xUIElement );
+ if ( pWindow )
+ {
+ pWindow->Show( false );
+ if ( !aUIElement.m_bFloating )
+ implts_setLayoutDirty();
+
+ aUIElement.m_bVisible = false;
+ implts_writeWindowStateData( aUIElement );
+ implts_setToolbar( aUIElement );
+ return true;
+ }
+
+ return false;
+}
+
+void ToolbarLayoutManager::refreshToolbarsVisibility( bool bAutomaticToolbars )
+{
+ UIElementVector aUIElementVector;
+
+ if ( !bAutomaticToolbars )
+ return;
+
+ implts_getUIElementVectorCopy( aUIElementVector );
+
+ UIElement aUIElement;
+ SolarMutexGuard aGuard;
+ for (auto const& elem : aUIElementVector)
+ {
+ if ( implts_readWindowStateData( elem.m_aName, aUIElement ) &&
+ ( elem.m_bVisible != aUIElement.m_bVisible ) && !elem.m_bMasterHide )
+ {
+ SolarMutexGuard g;
+ UIElement& rUIElement = impl_findToolbar( elem.m_aName );
+ if ( rUIElement.m_aName == elem.m_aName )
+ {
+ rUIElement.m_bVisible = aUIElement.m_bVisible;
+ implts_setLayoutDirty();
+ }
+ }
+ }
+}
+
+void ToolbarLayoutManager::setFloatingToolbarsVisibility( bool bVisible )
+{
+ UIElementVector aUIElementVector;
+ implts_getUIElementVectorCopy( aUIElementVector );
+
+ SolarMutexGuard aGuard;
+ for (auto const& elem : aUIElementVector)
+ {
+ vcl::Window* pWindow = getWindowFromXUIElement( elem.m_xUIElement );
+ if ( pWindow && elem.m_bFloating )
+ {
+ if ( bVisible )
+ {
+ if ( elem.m_bVisible && !elem.m_bMasterHide )
+ pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+ }
+ else
+ pWindow->Show( false );
+ }
+ }
+}
+
+void ToolbarLayoutManager::setVisible( bool bVisible )
+{
+ UIElementVector aUIElementVector;
+ implts_getUIElementVectorCopy( aUIElementVector );
+
+ SolarMutexGuard aGuard;
+ for (auto & elem : aUIElementVector)
+ {
+ if (!elem.m_bFloating)
+ {
+ elem.m_bMasterHide = !bVisible;
+ implts_setToolbar(elem);
+ implts_setLayoutDirty();
+ }
+
+ vcl::Window* pWindow = getWindowFromXUIElement( elem.m_xUIElement );
+ if ( pWindow )
+ {
+ bool bSetVisible( elem.m_bVisible && bVisible );
+ if ( !bSetVisible )
+ pWindow->Hide();
+ else
+ {
+ if ( elem.m_bFloating )
+ pWindow->Show(true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+ }
+ }
+ }
+
+ if ( !bVisible )
+ resetDockingArea();
+}
+
+bool ToolbarLayoutManager::dockToolbar( std::u16string_view rResourceURL, ui::DockingArea eDockingArea, const awt::Point& aPos )
+{
+ UIElement aUIElement = implts_findToolbar( rResourceURL );
+
+ if ( !aUIElement.m_xUIElement.is() )
+ return false;
+
+ try
+ {
+ uno::Reference< awt::XWindow > xWindow( aUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY );
+ uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY );
+ if ( xDockWindow.is() )
+ {
+ if ( eDockingArea != ui::DockingArea_DOCKINGAREA_DEFAULT )
+ aUIElement.m_aDockedData.m_nDockedArea = eDockingArea;
+
+ if ( !isDefaultPos( aPos ))
+ aUIElement.m_aDockedData.m_aPos = aPos;
+
+ if ( !xDockWindow->isFloating() )
+ {
+ vcl::Window* pWindow( nullptr );
+ ToolBox* pToolBox( nullptr );
+
+ {
+ SolarMutexGuard aGuard;
+ pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX )
+ {
+ pToolBox = static_cast<ToolBox *>(pWindow);
+
+ // We have to set the alignment of the toolbox. It's possible that the toolbox is moved from a
+ // horizontal to a vertical docking area!
+ pToolBox->SetAlign( ImplConvertAlignment( aUIElement.m_aDockedData.m_nDockedArea ));
+ }
+ }
+
+ if ( hasDefaultPosValue( aUIElement.m_aDockedData.m_aPos ))
+ {
+ // Docking on its default position without a preset position -
+ // we have to find a good place for it.
+ ::Size aSize;
+
+ SolarMutexGuard aGuard;
+ {
+ if (pToolBox)
+ aSize = pToolBox->CalcWindowSizePixel( 1, ImplConvertAlignment( aUIElement.m_aDockedData.m_nDockedArea ) );
+ else if (pWindow)
+ aSize = pWindow->GetSizePixel();
+ }
+
+ ::Point aPixelPos;
+ awt::Point aDockPos;
+ implts_findNextDockingPos(aUIElement.m_aDockedData.m_nDockedArea, aSize, aDockPos, aPixelPos );
+ aUIElement.m_aDockedData.m_aPos = aDockPos;
+ }
+ }
+
+ implts_setToolbar( aUIElement );
+
+ if ( xDockWindow->isFloating() )
+ {
+ // ATTENTION: This will call toggleFloatingMode() via notifications which
+ // sets the floating member of the UIElement correctly!
+ xDockWindow->setFloatingMode( false );
+ }
+ else
+ {
+ implts_writeWindowStateData( aUIElement );
+ implts_sortUIElements();
+
+ if ( aUIElement.m_bVisible )
+ implts_setLayoutDirty();
+ }
+ return true;
+ }
+ }
+ catch (const lang::DisposedException&)
+ {
+ }
+
+ return false;
+}
+
+bool ToolbarLayoutManager::dockAllToolbars()
+{
+ std::vector< OUString > aToolBarNameVector;
+
+ {
+ SolarMutexGuard aReadLock;
+ for (auto const& elem : m_aUIElements)
+ {
+ if (elem.m_aType == "toolbar" && elem.m_xUIElement.is() && elem.m_bFloating
+ && elem.m_bVisible)
+ aToolBarNameVector.push_back(elem.m_aName);
+ }
+ }
+
+ bool bResult(true);
+ const sal_uInt32 nCount = aToolBarNameVector.size();
+ for ( sal_uInt32 i = 0; i < nCount; ++i )
+ {
+ awt::Point aPoint;
+ aPoint.X = aPoint.Y = SAL_MAX_INT32;
+ bResult &= dockToolbar( aToolBarNameVector[i], ui::DockingArea_DOCKINGAREA_DEFAULT, aPoint );
+ }
+
+ return bResult;
+}
+
+void ToolbarLayoutManager::childWindowEvent( VclSimpleEvent const * pEvent )
+{
+ // To enable toolbar controllers to change their image when a sub-toolbar function
+ // is activated, we need this mechanism. We have NO connection between these toolbars
+ // anymore!
+ auto pWindowEvent = dynamic_cast< const VclWindowEvent* >(pEvent);
+ if (!pWindowEvent)
+ return;
+
+ if ( pEvent->GetId() == VclEventId::ToolboxSelect )
+ {
+ OUString aToolbarName;
+ OUString aCommand;
+ ToolBox* pToolBox = getToolboxPtr( pWindowEvent->GetWindow() );
+
+ if ( pToolBox )
+ {
+ aToolbarName = retrieveToolbarNameFromHelpURL( pToolBox );
+ ToolBoxItemId nId = pToolBox->GetCurItemId();
+ if ( nId > ToolBoxItemId(0) )
+ aCommand = pToolBox->GetItemCommand( nId );
+ }
+
+ if ( !aToolbarName.isEmpty() && !aCommand.isEmpty() )
+ {
+ SolarMutexClearableGuard aReadLock;
+ ::std::vector< uno::Reference< ui::XUIFunctionListener > > aListenerArray;
+
+ for (auto const& elem : m_aUIElements)
+ {
+ if ( elem.m_xUIElement.is() )
+ {
+ uno::Reference< ui::XUIFunctionListener > xListener( elem.m_xUIElement, uno::UNO_QUERY );
+ if ( xListener.is() )
+ aListenerArray.push_back( xListener );
+ }
+ }
+ aReadLock.clear();
+
+ const sal_uInt32 nCount = aListenerArray.size();
+ for ( sal_uInt32 i = 0; i < nCount; ++i )
+ {
+ try
+ {
+ aListenerArray[i]->functionExecute( aToolbarName, aCommand );
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+ }
+ else if ( pEvent->GetId() == VclEventId::ToolboxFormatChanged )
+ {
+ if ( !implts_isToolbarCreationActive() )
+ {
+ ToolBox* pToolBox = getToolboxPtr( pWindowEvent->GetWindow() );
+ if ( pToolBox )
+ {
+ OUString aToolbarName = retrieveToolbarNameFromHelpURL( pToolBox );
+ if ( !aToolbarName.isEmpty() )
+ {
+ OUString aToolbarUrl = "private:resource/toolbar/" + aToolbarName;
+
+ UIElement aToolbar = implts_findToolbar( aToolbarUrl );
+ if ( aToolbar.m_xUIElement.is() && !aToolbar.m_bFloating )
+ {
+ implts_setLayoutDirty();
+ m_pParentLayouter->requestLayout();
+ }
+ }
+ }
+ }
+ }
+}
+
+void ToolbarLayoutManager::resetDockingArea()
+{
+ SolarMutexClearableGuard aReadLock;
+ uno::Reference< awt::XWindow > xTopDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] );
+ uno::Reference< awt::XWindow > xLeftDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] );
+ uno::Reference< awt::XWindow > xRightDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] );
+ uno::Reference< awt::XWindow > xBottomDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] );
+ aReadLock.clear();
+
+ if ( xTopDockingWindow.is() )
+ xTopDockingWindow->setPosSize( 0, 0, 0, 0, awt::PosSize::POSSIZE );
+ if ( xLeftDockingWindow.is() )
+ xLeftDockingWindow->setPosSize( 0, 0, 0, 0, awt::PosSize::POSSIZE );
+ if ( xRightDockingWindow.is() )
+ xRightDockingWindow->setPosSize( 0, 0, 0, 0, awt::PosSize::POSSIZE );
+ if ( xBottomDockingWindow.is() )
+ xBottomDockingWindow->setPosSize( 0, 0, 0, 0, awt::PosSize::POSSIZE );
+}
+
+void ToolbarLayoutManager::setParentWindow(
+ const uno::Reference< awt::XWindowPeer >& xParentWindow )
+{
+ static const char DOCKINGAREASTRING[] = "dockingarea";
+
+ uno::Reference< awt::XWindow > xTopDockWindow( createToolkitWindow( m_xContext, xParentWindow, DOCKINGAREASTRING ), uno::UNO_QUERY );
+ uno::Reference< awt::XWindow > xLeftDockWindow( createToolkitWindow( m_xContext, xParentWindow, DOCKINGAREASTRING ), uno::UNO_QUERY );
+ uno::Reference< awt::XWindow > xRightDockWindow( createToolkitWindow( m_xContext, xParentWindow, DOCKINGAREASTRING ), uno::UNO_QUERY );
+ uno::Reference< awt::XWindow > xBottomDockWindow( createToolkitWindow( m_xContext, xParentWindow, DOCKINGAREASTRING ), uno::UNO_QUERY );
+
+ {
+ SolarMutexGuard aWriteLock;
+ m_xContainerWindow.set(xParentWindow, uno::UNO_QUERY);
+ m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] = xTopDockWindow;
+ m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] = xLeftDockWindow;
+ m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] = xRightDockWindow;
+ m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] = xBottomDockWindow;
+ }
+
+ if ( xParentWindow.is() )
+ {
+ SolarMutexGuard aGuard;
+ VclPtr< ::DockingAreaWindow > pWindow = dynamic_cast< ::DockingAreaWindow* >(VCLUnoHelper::GetWindow( xTopDockWindow ) );
+ if( pWindow )
+ pWindow->SetAlign( WindowAlign::Top );
+ pWindow = dynamic_cast< ::DockingAreaWindow* >(VCLUnoHelper::GetWindow( xBottomDockWindow ) );
+ if( pWindow )
+ pWindow->SetAlign( WindowAlign::Bottom );
+ pWindow = dynamic_cast< ::DockingAreaWindow* >(VCLUnoHelper::GetWindow( xLeftDockWindow ) );
+ if( pWindow )
+ pWindow->SetAlign( WindowAlign::Left );
+ pWindow = dynamic_cast< ::DockingAreaWindow* >(VCLUnoHelper::GetWindow( xRightDockWindow ) );
+ if( pWindow )
+ pWindow->SetAlign( WindowAlign::Right );
+ implts_reparentToolbars();
+ }
+ else
+ {
+ destroyToolbars();
+ resetDockingArea();
+ }
+}
+
+void ToolbarLayoutManager::setDockingAreaOffsets( const ::tools::Rectangle& rOffsets )
+{
+ SolarMutexGuard g;
+ m_aDockingAreaOffsets = rOffsets;
+ m_bLayoutDirty = true;
+}
+
+OUString ToolbarLayoutManager::implts_generateGenericAddonToolbarTitle( sal_Int32 nNumber ) const
+{
+ OUString aAddonGenericTitle(FwkResId(STR_TOOLBAR_TITLE_ADDON));
+ const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
+
+ OUString aNumStr = rI18nHelper.GetNum( nNumber, 0, false, false );
+ aAddonGenericTitle = aAddonGenericTitle.replaceFirst( "%num%", aNumStr );
+
+ return aAddonGenericTitle;
+}
+
+void ToolbarLayoutManager::implts_createAddonsToolBars()
+{
+ SolarMutexClearableGuard aWriteLock;
+ if ( !m_pAddonOptions )
+ m_pAddonOptions.reset( new AddonsOptions );
+
+ uno::Reference< ui::XUIElementFactory > xUIElementFactory( m_xUIElementFactoryManager );
+ uno::Reference< frame::XFrame > xFrame( m_xFrame );
+ aWriteLock.clear();
+
+ if (isPreviewFrame())
+ return; // no addon toolbars for preview frame!
+
+ uno::Sequence< uno::Sequence< beans::PropertyValue > > aAddonToolBarData;
+ uno::Reference< ui::XUIElement > xUIElement;
+
+ sal_uInt32 nCount = m_pAddonOptions->GetAddonsToolBarCount();
+
+ uno::Sequence< beans::PropertyValue > aPropSeq( 2 );
+ auto pPropSeq = aPropSeq.getArray();
+ pPropSeq[0].Name = "Frame";
+ pPropSeq[0].Value <<= xFrame;
+ pPropSeq[1].Name = "ConfigurationData";
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ OUString aAddonToolBarName( "private:resource/toolbar/addon_" +
+ m_pAddonOptions->GetAddonsToolbarResourceName(i) );
+ aAddonToolBarData = m_pAddonOptions->GetAddonsToolBarPart( i );
+ pPropSeq[1].Value <<= aAddonToolBarData;
+
+ UIElement aElement = implts_findToolbar( aAddonToolBarName );
+
+ // #i79828
+ // It's now possible that we are called more than once. Be sure to not create
+ // add-on toolbars more than once!
+ if ( aElement.m_xUIElement.is() )
+ continue;
+
+ try
+ {
+ xUIElement = xUIElementFactory->createUIElement( aAddonToolBarName, aPropSeq );
+ if ( xUIElement.is() )
+ {
+ uno::Reference< awt::XDockableWindow > xDockWindow( xUIElement->getRealInterface(), uno::UNO_QUERY );
+ if ( xDockWindow.is() )
+ {
+ try
+ {
+ xDockWindow->addDockableWindowListener( uno::Reference< awt::XDockableWindowListener >(this) );
+ xDockWindow->enableDocking( true );
+ uno::Reference< awt::XWindow > xWindow( xDockWindow, uno::UNO_QUERY );
+ if ( xWindow.is() )
+ xWindow->addWindowListener( uno::Reference< awt::XWindowListener >(this) );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ OUString aGenericAddonTitle = implts_generateGenericAddonToolbarTitle( i+1 );
+
+ if ( !aElement.m_aName.isEmpty() )
+ {
+ // Reuse a local entry so we are able to use the latest
+ // UI changes for this document.
+ implts_setElementData( aElement, xDockWindow );
+ aElement.m_xUIElement = xUIElement;
+ if ( aElement.m_aUIName.isEmpty() )
+ {
+ aElement.m_aUIName = aGenericAddonTitle;
+ implts_writeWindowStateData( aElement );
+ }
+ }
+ else
+ {
+ // Create new UI element and try to read its state data
+ UIElement aNewToolbar( aAddonToolBarName, "toolbar", xUIElement );
+ aNewToolbar.m_bFloating = true;
+ implts_readWindowStateData( aAddonToolBarName, aNewToolbar );
+ implts_setElementData( aNewToolbar, xDockWindow );
+ if ( aNewToolbar.m_aUIName.isEmpty() )
+ {
+ aNewToolbar.m_aUIName = aGenericAddonTitle;
+ implts_writeWindowStateData( aNewToolbar );
+ }
+ implts_insertToolbar( aNewToolbar );
+ }
+
+ uno::Reference< awt::XWindow > xWindow( xDockWindow, uno::UNO_QUERY );
+ if ( xWindow.is() )
+ {
+ // Set generic title for add-on toolbar
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow->GetText().isEmpty() )
+ pWindow->SetText( aGenericAddonTitle );
+ if ( pWindow->GetType() == WindowType::TOOLBOX )
+ {
+ ToolBox* pToolbar = static_cast<ToolBox *>(pWindow.get());
+ pToolbar->SetMenuType();
+ }
+ }
+ }
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ }
+ catch (const lang::IllegalArgumentException&)
+ {
+ }
+ }
+}
+
+void ToolbarLayoutManager::implts_createCustomToolBars()
+{
+ SolarMutexClearableGuard aReadLock;
+ if ( !m_bComponentAttached )
+ return;
+
+ uno::Reference< frame::XFrame > xFrame( m_xFrame );
+ uno::Reference< ui::XUIConfigurationManager > xModuleCfgMgr = m_xModuleCfgMgr;
+ uno::Reference< ui::XUIConfigurationManager > xDocCfgMgr = m_xDocCfgMgr;
+ aReadLock.clear();
+
+ if ( !xFrame.is() )
+ return;
+
+ if (isPreviewFrame())
+ return; // no custom toolbars for preview frame!
+
+ uno::Sequence< uno::Sequence< beans::PropertyValue > > aTbxSeq;
+ if ( xDocCfgMgr.is() )
+ {
+ aTbxSeq = xDocCfgMgr->getUIElementsInfo( ui::UIElementType::TOOLBAR );
+ implts_createCustomToolBars( aTbxSeq ); // first create all document based toolbars
+ }
+ if ( xModuleCfgMgr.is() )
+ {
+ aTbxSeq = xModuleCfgMgr->getUIElementsInfo( ui::UIElementType::TOOLBAR );
+ implts_createCustomToolBars( aTbxSeq ); // second create module based toolbars
+ }
+}
+
+void ToolbarLayoutManager::implts_createNonContextSensitiveToolBars()
+{
+ SolarMutexClearableGuard aReadLock;
+
+ if ( !m_xPersistentWindowState.is() || !m_xFrame.is() || !m_bComponentAttached )
+ return;
+
+ uno::Reference< container::XNameAccess > xPersistentWindowState( m_xPersistentWindowState );
+ aReadLock.clear();
+
+ if (isPreviewFrame())
+ return;
+
+ std::vector< OUString > aMakeVisibleToolbars;
+
+ try
+ {
+ const uno::Sequence< OUString > aToolbarNames = xPersistentWindowState->getElementNames();
+
+ if ( aToolbarNames.hasElements() )
+ {
+ OUString aElementType;
+ OUString aElementName;
+
+ aMakeVisibleToolbars.reserve(aToolbarNames.getLength());
+
+ SolarMutexGuard g;
+
+ for ( OUString const & aName : aToolbarNames )
+ {
+ parseResourceURL( aName, aElementType, aElementName );
+
+ // Check that we only create:
+ // - Toolbars (the statusbar is also member of the persistent window state)
+ // - Not custom toolbars, there are created with their own method (implts_createCustomToolbars)
+ if ( aElementType.equalsIgnoreAsciiCase("toolbar") &&
+ aElementName.indexOf( "custom_" ) == -1 )
+ {
+ UIElement aNewToolbar = implts_findToolbar( aName );
+ bool bFound = ( aNewToolbar.m_aName == aName );
+ if ( !bFound )
+ implts_readWindowStateData( aName, aNewToolbar );
+
+ if ( aNewToolbar.m_bVisible && !aNewToolbar.m_bContextSensitive )
+ {
+ if ( !bFound )
+ implts_insertToolbar( aNewToolbar );
+ aMakeVisibleToolbars.push_back( aName );
+ }
+ }
+ }
+ }
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ for (auto const& rURL : aMakeVisibleToolbars)
+ {
+ requestToolbar(rURL);
+ }
+}
+
+void ToolbarLayoutManager::implts_createCustomToolBars( const uno::Sequence< uno::Sequence< beans::PropertyValue > >& aTbxSeqSeq )
+{
+ for ( const uno::Sequence< beans::PropertyValue >& rTbxSeq : aTbxSeqSeq )
+ {
+ OUString aTbxResName;
+ OUString aTbxTitle;
+ for ( const beans::PropertyValue& rProp : rTbxSeq )
+ {
+ if ( rProp.Name == "ResourceURL" )
+ rProp.Value >>= aTbxResName;
+ else if ( rProp.Name == "UIName" )
+ rProp.Value >>= aTbxTitle;
+ }
+
+ // Only create custom toolbars. Their name have to start with "custom_"!
+ if ( !aTbxResName.isEmpty() && ( aTbxResName.indexOf( "custom_" ) != -1 ) )
+ implts_createCustomToolBar( aTbxResName, aTbxTitle );
+ }
+}
+
+void ToolbarLayoutManager::implts_createCustomToolBar( const OUString& aTbxResName, const OUString& aTitle )
+{
+ if ( aTbxResName.isEmpty() )
+ return;
+
+ if ( !createToolbar( aTbxResName ) )
+ SAL_WARN("fwk.uielement", "ToolbarLayoutManager cannot create custom toolbar");
+
+ uno::Reference< ui::XUIElement > xUIElement = getToolbar( aTbxResName );
+
+ if ( !aTitle.isEmpty() && xUIElement.is() )
+ {
+ SolarMutexGuard aGuard;
+
+ vcl::Window* pWindow = getWindowFromXUIElement( xUIElement );
+ if ( pWindow )
+ pWindow->SetText( aTitle );
+ }
+}
+
+void ToolbarLayoutManager::implts_reparentToolbars()
+{
+ SolarMutexClearableGuard aWriteLock;
+ UIElementVector aUIElementVector = m_aUIElements;
+ VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( m_xContainerWindow );
+ VclPtr<vcl::Window> pTopDockWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] );
+ VclPtr<vcl::Window> pBottomDockWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] );
+ VclPtr<vcl::Window> pLeftDockWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] );
+ VclPtr<vcl::Window> pRightDockWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] );
+ aWriteLock.clear();
+
+ SolarMutexGuard aGuard;
+ if ( !pContainerWindow )
+ return;
+
+ for (auto const& elem : aUIElementVector)
+ {
+ uno::Reference< ui::XUIElement > xUIElement( elem.m_xUIElement );
+ if ( xUIElement.is() )
+ {
+ uno::Reference< awt::XWindow > xWindow;
+ try
+ {
+ // We have to retrieve the window reference with try/catch as it is
+ // possible that all elements have been disposed!
+ xWindow.set( xUIElement->getRealInterface(), uno::UNO_QUERY );
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow )
+ {
+ // Reparent our child windows according to their current state.
+ if ( elem.m_bFloating )
+ pWindow->SetParent( pContainerWindow );
+ else
+ {
+ if ( elem.m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_TOP )
+ pWindow->SetParent( pTopDockWindow );
+ else if ( elem.m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM )
+ pWindow->SetParent( pBottomDockWindow );
+ else if ( elem.m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_LEFT )
+ pWindow->SetParent( pLeftDockWindow );
+ else
+ pWindow->SetParent( pRightDockWindow );
+ }
+ }
+ }
+ }
+}
+
+void ToolbarLayoutManager::implts_setToolbarCreation( bool bStart )
+{
+ SolarMutexGuard g;
+ m_bToolbarCreation = bStart;
+}
+
+bool ToolbarLayoutManager::implts_isToolbarCreationActive()
+{
+ SolarMutexGuard g;
+ return m_bToolbarCreation;
+}
+
+void ToolbarLayoutManager::implts_setElementData( UIElement& rElement, const uno::Reference< awt::XDockableWindow >& rDockWindow )
+{
+ SolarMutexClearableGuard aReadLock;
+ bool bShowElement( rElement.m_bVisible && !rElement.m_bMasterHide && implts_isParentWindowVisible() );
+ aReadLock.clear();
+
+ uno::Reference< awt::XWindow2 > xWindow( rDockWindow, uno::UNO_QUERY );
+
+ vcl::Window* pWindow( nullptr );
+ ToolBox* pToolBox( nullptr );
+
+ if ( !(rDockWindow.is() && xWindow.is()) )
+ return;
+
+ {
+ SolarMutexGuard aGuard;
+ pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow )
+ {
+ OUString aText = pWindow->GetText();
+ if ( aText.isEmpty() )
+ pWindow->SetText( rElement.m_aUIName );
+ if ( rElement.m_bNoClose )
+ pWindow->SetStyle( pWindow->GetStyle() & ~WB_CLOSEABLE );
+ if ( pWindow->GetType() == WindowType::TOOLBOX )
+ pToolBox = static_cast<ToolBox *>(pWindow);
+ }
+ if ( pToolBox )
+ {
+ pToolBox->SetButtonType( rElement.m_nStyle );
+ if ( rElement.m_bNoClose )
+ pToolBox->SetFloatStyle( pToolBox->GetFloatStyle() & ~WB_CLOSEABLE );
+ }
+ }
+
+ if ( rElement.m_bFloating )
+ {
+ if ( pWindow )
+ {
+ SolarMutexGuard aGuard;
+ OUString aText = pWindow->GetText();
+ if ( aText.isEmpty() )
+ pWindow->SetText( rElement.m_aUIName );
+ }
+
+ awt::Point aPos(rElement.m_aFloatingData.m_aPos);
+ bool bWriteData( false );
+ bool bUndefPos = hasDefaultPosValue( rElement.m_aFloatingData.m_aPos );
+ bool bSetSize = ( rElement.m_aFloatingData.m_aSize.Width != 0 &&
+ rElement.m_aFloatingData.m_aSize.Height != 0 );
+ rDockWindow->setFloatingMode( true );
+ if ( bUndefPos )
+ {
+ aPos = implts_findNextCascadeFloatingPos();
+ rElement.m_aFloatingData.m_aPos = aPos; // set new cascaded position
+ bWriteData = true;
+ }
+
+ if( bSetSize )
+ xWindow->setOutputSize(rElement.m_aFloatingData.m_aSize);
+ else
+ {
+ if( pToolBox )
+ {
+ // set an optimal initial floating size
+ SolarMutexGuard aGuard;
+ ::Size aSize( pToolBox->CalcFloatingWindowSizePixel() );
+ pToolBox->SetOutputSizePixel( aSize );
+ }
+ }
+
+ // #i60882# IMPORTANT: Set position after size as it is
+ // possible that we position some part of the toolbar
+ // outside of the desktop. A default constructed toolbar
+ // always has one line. Now VCL automatically
+ // position the toolbar back into the desktop. Therefore
+ // we resize the toolbar with the new (wrong) position.
+ // To fix this problem we have to set the size BEFORE the
+ // position.
+ xWindow->setPosSize( aPos.X, aPos.Y, 0, 0, awt::PosSize::POS );
+
+ if ( bWriteData )
+ implts_writeWindowStateData( rElement );
+ if ( bShowElement && pWindow )
+ {
+ SolarMutexGuard aGuard;
+ pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+ }
+ }
+ else
+ {
+ bool bSetSize( false );
+ ::Point aPixelPos;
+ ::Size aSize;
+
+ if ( pToolBox )
+ {
+ SolarMutexGuard aGuard;
+ pToolBox->SetAlign( ImplConvertAlignment(rElement.m_aDockedData.m_nDockedArea ) );
+ pToolBox->SetLineCount( 1 );
+ rDockWindow->setFloatingMode( false );
+ if ( rElement.m_aDockedData.m_bLocked )
+ rDockWindow->lock();
+ aSize = pToolBox->CalcWindowSizePixel();
+ bSetSize = true;
+
+ if ( isDefaultPos( rElement.m_aDockedData.m_aPos ))
+ {
+ awt::Point aDockPos;
+ implts_findNextDockingPos( rElement.m_aDockedData.m_nDockedArea, aSize, aDockPos, aPixelPos );
+ rElement.m_aDockedData.m_aPos = aDockPos;
+ }
+ }
+
+ xWindow->setPosSize( aPixelPos.X(), aPixelPos.Y(), 0, 0, awt::PosSize::POS );
+ if( bSetSize )
+ xWindow->setOutputSize( AWTSize( aSize) );
+
+ if ( pWindow )
+ {
+ SolarMutexGuard aGuard;
+ if ( !bShowElement )
+ pWindow->Hide();
+ }
+ }
+}
+
+void ToolbarLayoutManager::implts_destroyDockingAreaWindows()
+{
+ SolarMutexClearableGuard aWriteLock;
+ uno::Reference< awt::XWindow > xTopDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] );
+ uno::Reference< awt::XWindow > xLeftDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] );
+ uno::Reference< awt::XWindow > xRightDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] );
+ uno::Reference< awt::XWindow > xBottomDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] );
+ m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)].clear();
+ m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)].clear();
+ m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)].clear();
+ m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)].clear();
+ aWriteLock.clear();
+
+ // destroy windows
+ xTopDockingWindow->dispose();
+ xLeftDockingWindow->dispose();
+ xRightDockingWindow->dispose();
+ xBottomDockingWindow->dispose();
+}
+
+// persistence methods
+
+bool ToolbarLayoutManager::implts_readWindowStateData( const OUString& aName, UIElement& rElementData )
+{
+ return LayoutManager::readWindowStateData( aName, rElementData, m_xPersistentWindowState,
+ m_pGlobalSettings, m_bGlobalSettings, m_xContext );
+}
+
+void ToolbarLayoutManager::implts_writeWindowStateData( const UIElement& rElementData )
+{
+ SolarMutexClearableGuard aWriteLock;
+ uno::Reference< container::XNameAccess > xPersistentWindowState( m_xPersistentWindowState );
+ aWriteLock.clear();
+
+ bool bPersistent( false );
+ uno::Reference< beans::XPropertySet > xPropSet( rElementData.m_xUIElement, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ // Check persistent flag of the user interface element
+ xPropSet->getPropertyValue("Persistent") >>= bPersistent;
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ bPersistent = true; // Non-configurable elements should at least store their dimension/position
+ }
+ catch (const lang::WrappedTargetException&)
+ {
+ }
+ }
+
+ if ( !(bPersistent && xPersistentWindowState.is()) )
+ return;
+
+ try
+ {
+ uno::Sequence<beans::PropertyValue> aWindowState{
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKED, !rElementData.m_bFloating),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_VISIBLE, rElementData.m_bVisible),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKINGAREA,
+ rElementData.m_aDockedData.m_nDockedArea),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_DOCKPOS,
+ rElementData.m_aDockedData.m_aPos),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_POS,
+ rElementData.m_aFloatingData.m_aPos),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_SIZE,
+ rElementData.m_aFloatingData.m_aSize),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_UINAME, rElementData.m_aUIName),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_LOCKED,
+ rElementData.m_aDockedData.m_bLocked),
+ comphelper::makePropertyValue(WINDOWSTATE_PROPERTY_STYLE,
+ static_cast<sal_uInt16>(rElementData.m_nStyle))
+ };
+
+ OUString aName = rElementData.m_aName;
+ if ( xPersistentWindowState->hasByName( aName ))
+ {
+ uno::Reference< container::XNameReplace > xReplace( xPersistentWindowState, uno::UNO_QUERY );
+ xReplace->replaceByName( aName, uno::Any( aWindowState ));
+ }
+ else
+ {
+ uno::Reference< container::XNameContainer > xInsert( xPersistentWindowState, uno::UNO_QUERY );
+ xInsert->insertByName( aName, uno::Any( aWindowState ));
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+/******************************************************************************
+ LOOKUP PART FOR TOOLBARS
+******************************************************************************/
+
+UIElement& ToolbarLayoutManager::impl_findToolbar( std::u16string_view aName )
+{
+ static UIElement aEmptyElement;
+
+ SolarMutexGuard g;
+ for (auto & elem : m_aUIElements)
+ {
+ if ( elem.m_aName == aName )
+ return elem;
+ }
+
+ return aEmptyElement;
+}
+
+UIElement ToolbarLayoutManager::implts_findToolbar( std::u16string_view aName )
+{
+ SolarMutexGuard g;
+ return impl_findToolbar( aName );
+}
+
+UIElement ToolbarLayoutManager::implts_findToolbar( const uno::Reference< uno::XInterface >& xToolbar )
+{
+ UIElement aToolbar;
+
+ SolarMutexGuard g;
+ for (auto const& elem : m_aUIElements)
+ {
+ if ( elem.m_xUIElement.is() )
+ {
+ uno::Reference< uno::XInterface > xIfac( elem.m_xUIElement->getRealInterface(), uno::UNO_QUERY );
+ if ( xIfac == xToolbar )
+ {
+ aToolbar = elem;
+ break;
+ }
+ }
+ }
+
+ return aToolbar;
+}
+
+uno::Reference< awt::XWindow > ToolbarLayoutManager::implts_getXWindow( std::u16string_view aName )
+{
+ uno::Reference< awt::XWindow > xWindow;
+
+ SolarMutexGuard g;
+ for (auto const& elem : m_aUIElements)
+ {
+ if ( elem.m_aName == aName && elem.m_xUIElement.is() )
+ {
+ xWindow.set( elem.m_xUIElement->getRealInterface(), uno::UNO_QUERY );
+ break;
+ }
+ }
+
+ return xWindow;
+}
+
+vcl::Window* ToolbarLayoutManager::implts_getWindow( std::u16string_view aName )
+{
+ uno::Reference< awt::XWindow > xWindow = implts_getXWindow( aName );
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+
+ return pWindow;
+}
+
+bool ToolbarLayoutManager::implts_insertToolbar( const UIElement& rUIElement )
+{
+ UIElement aTempData;
+ bool bFound( false );
+ bool bResult( false );
+
+ aTempData = implts_findToolbar( rUIElement.m_aName );
+ if ( aTempData.m_aName == rUIElement.m_aName )
+ bFound = true;
+
+ if ( !bFound )
+ {
+ SolarMutexGuard g;
+ m_aUIElements.push_back( rUIElement );
+ bResult = true;
+ }
+
+ return bResult;
+}
+
+void ToolbarLayoutManager::implts_setToolbar( const UIElement& rUIElement )
+{
+ SolarMutexGuard g;
+ UIElement& rData = impl_findToolbar( rUIElement.m_aName );
+ if ( rData.m_aName == rUIElement.m_aName )
+ rData = rUIElement;
+ else
+ m_aUIElements.push_back( rUIElement );
+}
+
+/******************************************************************************
+ LAYOUT CODE PART FOR TOOLBARS
+******************************************************************************/
+
+awt::Point ToolbarLayoutManager::implts_findNextCascadeFloatingPos()
+{
+ const sal_Int32 nHotZoneX = 50;
+ const sal_Int32 nHotZoneY = 50;
+ const sal_Int32 nCascadeIndentX = 15;
+ const sal_Int32 nCascadeIndentY = 15;
+
+ SolarMutexClearableGuard aReadLock;
+ uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow );
+ uno::Reference< awt::XWindow > xTopDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] );
+ uno::Reference< awt::XWindow > xLeftDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] );
+ aReadLock.clear();
+
+ awt::Point aStartPos( nCascadeIndentX, nCascadeIndentY );
+ awt::Point aCurrPos( aStartPos );
+
+ if ( xContainerWindow.is() )
+ {
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow );
+ if ( pContainerWindow )
+ aStartPos = AWTPoint(pContainerWindow->OutputToScreenPixel(VCLPoint(aStartPos)));
+ }
+
+ // Determine size of top and left docking area
+ awt::Rectangle aTopRect( xTopDockingWindow->getPosSize() );
+ awt::Rectangle aLeftRect( xLeftDockingWindow->getPosSize() );
+
+ aStartPos.X += aLeftRect.Width + nCascadeIndentX;
+ aStartPos.Y += aTopRect.Height + nCascadeIndentY;
+ aCurrPos = aStartPos;
+
+ // Try to find a cascaded position for the new floating window
+ for (auto const& elem : m_aUIElements)
+ {
+ if ( elem.m_xUIElement.is() )
+ {
+ uno::Reference< awt::XDockableWindow > xDockWindow( elem.m_xUIElement->getRealInterface(), uno::UNO_QUERY );
+ uno::Reference< awt::XWindow > xWindow( xDockWindow, uno::UNO_QUERY );
+ if ( xDockWindow.is() && xDockWindow->isFloating() )
+ {
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->IsVisible() )
+ {
+ awt::Rectangle aFloatRect = xWindow->getPosSize();
+ if ((( aFloatRect.X - nHotZoneX ) <= aCurrPos.X ) &&
+ ( aFloatRect.X >= aCurrPos.X ) &&
+ (( aFloatRect.Y - nHotZoneY ) <= aCurrPos.Y ) &&
+ ( aFloatRect.Y >= aCurrPos.Y ))
+ {
+ aCurrPos.X = aFloatRect.X + nCascadeIndentX;
+ aCurrPos.Y = aFloatRect.Y + nCascadeIndentY;
+ }
+ }
+ }
+ }
+ }
+
+ return aCurrPos;
+}
+
+void ToolbarLayoutManager::implts_sortUIElements()
+{
+ SolarMutexGuard g;
+
+ std::stable_sort( m_aUIElements.begin(), m_aUIElements.end()); // first created element should first
+
+ // We have to reset our temporary flags.
+ for (auto & elem : m_aUIElements)
+ elem.m_bUserActive = false;
+}
+
+void ToolbarLayoutManager::implts_getUIElementVectorCopy( UIElementVector& rCopy )
+{
+ SolarMutexGuard g;
+ rCopy = m_aUIElements;
+}
+
+::Size ToolbarLayoutManager::implts_getTopBottomDockingAreaSizes()
+{
+ ::Size aSize;
+ uno::Reference< awt::XWindow > xTopDockingAreaWindow;
+ uno::Reference< awt::XWindow > xBottomDockingAreaWindow;
+
+ {
+ SolarMutexGuard aReadLock;
+ xTopDockingAreaWindow = m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)];
+ xBottomDockingAreaWindow = m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)];
+ }
+
+ if ( xTopDockingAreaWindow.is() )
+ aSize.setWidth( xTopDockingAreaWindow->getPosSize().Height );
+ if ( xBottomDockingAreaWindow.is() )
+ aSize.setHeight( xBottomDockingAreaWindow->getPosSize().Height );
+
+ return aSize;
+}
+
+void ToolbarLayoutManager::implts_getDockingAreaElementInfos( ui::DockingArea eDockingArea, std::vector< SingleRowColumnWindowData >& rRowColumnsWindowData )
+{
+ std::vector< UIElement > aWindowVector;
+
+ if (( eDockingArea < ui::DockingArea_DOCKINGAREA_TOP ) || ( eDockingArea > ui::DockingArea_DOCKINGAREA_RIGHT ))
+ eDockingArea = ui::DockingArea_DOCKINGAREA_TOP;
+
+ uno::Reference< awt::XWindow > xDockAreaWindow;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ {
+ SolarMutexGuard aReadLock;
+ aWindowVector.reserve(m_aUIElements.size());
+ xDockAreaWindow = m_xDockAreaWindows[static_cast<int>(eDockingArea)];
+ for (auto const& elem : m_aUIElements)
+ {
+ if (elem.m_aDockedData.m_nDockedArea == eDockingArea && elem.m_bVisible)
+ {
+ uno::Reference<ui::XUIElement> xUIElement(elem.m_xUIElement);
+ if (xUIElement.is())
+ {
+ uno::Reference<awt::XWindow> xWindow(xUIElement->getRealInterface(),
+ uno::UNO_QUERY);
+ uno::Reference<awt::XDockableWindow> xDockWindow(xWindow, uno::UNO_QUERY);
+ if (xDockWindow.is())
+ {
+ if (!elem.m_bFloating)
+ {
+ // docked windows
+ aWindowVector.push_back(elem);
+ }
+ else
+ {
+ // floating windows
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ DockingManager* pDockMgr = vcl::Window::GetDockingManager();
+ if (pDockMgr != nullptr)
+ {
+ SystemWindow* pFloatingWindow = pDockMgr->GetFloatingWindow(pWindow);
+ if (pFloatingWindow)
+ {
+ // update the position data of the floating window
+ if (pFloatingWindow->UpdatePositionData())
+ {
+ awt::Rectangle aTmpRect = xWindow->getPosSize();
+ UIElement uiElem = elem;
+ uiElem.m_aFloatingData.m_aPos
+ = awt::Point(aTmpRect.X, aTmpRect.Y);
+ implts_setToolbar(uiElem);
+ implts_writeWindowStateData(uiElem);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ rRowColumnsWindowData.clear();
+
+ // Collect data from windows that are on the same row/column
+ sal_Int32 j;
+ sal_Int32 nIndex( 0 );
+ sal_Int32 nLastPos( 0 );
+ sal_Int32 nCurrPos( -1 );
+ sal_Int32 nLastRowColPixelPos( 0 );
+ awt::Rectangle aDockAreaRect;
+
+ if ( xDockAreaWindow.is() )
+ aDockAreaRect = xDockAreaWindow->getPosSize();
+
+ if ( eDockingArea == ui::DockingArea_DOCKINGAREA_TOP )
+ nLastRowColPixelPos = 0;
+ else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM )
+ nLastRowColPixelPos = aDockAreaRect.Height;
+ else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_LEFT )
+ nLastRowColPixelPos = 0;
+ else
+ nLastRowColPixelPos = aDockAreaRect.Width;
+
+ const sal_uInt32 nCount = aWindowVector.size();
+ for ( j = 0; j < sal_Int32( nCount); j++ )
+ {
+ const UIElement& rElement = aWindowVector[j];
+ uno::Reference< awt::XWindow > xWindow;
+ uno::Reference< ui::XUIElement > xUIElement( rElement.m_xUIElement );
+ awt::Rectangle aPosSize;
+
+ if ( !lcl_checkUIElement(xUIElement,aPosSize,xWindow) )
+ continue;
+ if ( isHorizontalDockingArea( eDockingArea ))
+ {
+ if ( nCurrPos == -1 )
+ {
+ nCurrPos = rElement.m_aDockedData.m_aPos.Y;
+ nLastPos = 0;
+
+ SingleRowColumnWindowData aRowColumnWindowData;
+ aRowColumnWindowData.nRowColumn = nCurrPos;
+ rRowColumnsWindowData.push_back( aRowColumnWindowData );
+ }
+
+ sal_Int32 nSpace( 0 );
+ if ( rElement.m_aDockedData.m_aPos.Y != nCurrPos )
+ {
+ if ( eDockingArea == ui::DockingArea_DOCKINGAREA_TOP )
+ nLastRowColPixelPos += rRowColumnsWindowData[nIndex].nStaticSize;
+ else
+ nLastRowColPixelPos -= rRowColumnsWindowData[nIndex].nStaticSize;
+ ++nIndex;
+ nLastPos = 0;
+ nCurrPos = rElement.m_aDockedData.m_aPos.Y;
+ SingleRowColumnWindowData aRowColumnWindowData;
+ aRowColumnWindowData.nRowColumn = nCurrPos;
+ rRowColumnsWindowData.push_back( aRowColumnWindowData );
+ }
+
+ // Calc space before an element and store it
+ nSpace = ( rElement.m_aDockedData.m_aPos.X - nLastPos );
+ if ( rElement.m_aDockedData.m_aPos.X >= nLastPos )
+ {
+ rRowColumnsWindowData[nIndex].nSpace += nSpace;
+ nLastPos = rElement.m_aDockedData.m_aPos.X + aPosSize.Width;
+ }
+ else
+ {
+ nSpace = 0;
+ nLastPos += aPosSize.Width;
+ }
+ rRowColumnsWindowData[nIndex].aRowColumnSpace.push_back( nSpace );
+
+ rRowColumnsWindowData[nIndex].aRowColumnWindows.push_back( xWindow );
+ rRowColumnsWindowData[nIndex].aUIElementNames.push_back( rElement.m_aName );
+ rRowColumnsWindowData[nIndex].aRowColumnWindowSizes.emplace_back(
+ rElement.m_aDockedData.m_aPos.X,
+ rElement.m_aDockedData.m_aPos.Y,
+ aPosSize.Width,
+ aPosSize.Height );
+ if ( rRowColumnsWindowData[nIndex].nStaticSize < aPosSize.Height )
+ rRowColumnsWindowData[nIndex].nStaticSize = aPosSize.Height;
+ if ( eDockingArea == ui::DockingArea_DOCKINGAREA_TOP )
+ rRowColumnsWindowData[nIndex].aRowColumnRect = awt::Rectangle( 0, nLastRowColPixelPos,
+ aDockAreaRect.Width, aPosSize.Height );
+ else
+ rRowColumnsWindowData[nIndex].aRowColumnRect = awt::Rectangle( 0, ( nLastRowColPixelPos - aPosSize.Height ),
+ aDockAreaRect.Width, aPosSize.Height );
+ rRowColumnsWindowData[nIndex].nVarSize += aPosSize.Width + nSpace;
+ }
+ else
+ {
+ if ( nCurrPos == -1 )
+ {
+ nCurrPos = rElement.m_aDockedData.m_aPos.X;
+ nLastPos = 0;
+
+ SingleRowColumnWindowData aRowColumnWindowData;
+ aRowColumnWindowData.nRowColumn = nCurrPos;
+ rRowColumnsWindowData.push_back( aRowColumnWindowData );
+ }
+
+ sal_Int32 nSpace( 0 );
+ if ( rElement.m_aDockedData.m_aPos.X != nCurrPos )
+ {
+ if ( eDockingArea == ui::DockingArea_DOCKINGAREA_LEFT )
+ nLastRowColPixelPos += rRowColumnsWindowData[nIndex].nStaticSize;
+ else
+ nLastRowColPixelPos -= rRowColumnsWindowData[nIndex].nStaticSize;
+ ++nIndex;
+ nLastPos = 0;
+ nCurrPos = rElement.m_aDockedData.m_aPos.X;
+ SingleRowColumnWindowData aRowColumnWindowData;
+ aRowColumnWindowData.nRowColumn = nCurrPos;
+ rRowColumnsWindowData.push_back( aRowColumnWindowData );
+ }
+
+ // Calc space before an element and store it
+ nSpace = ( rElement.m_aDockedData.m_aPos.Y - nLastPos );
+ if ( rElement.m_aDockedData.m_aPos.Y > nLastPos )
+ {
+ rRowColumnsWindowData[nIndex].nSpace += nSpace;
+ nLastPos = rElement.m_aDockedData.m_aPos.Y + aPosSize.Height;
+ }
+ else
+ {
+ nSpace = 0;
+ nLastPos += aPosSize.Height;
+ }
+ rRowColumnsWindowData[nIndex].aRowColumnSpace.push_back( nSpace );
+
+ rRowColumnsWindowData[nIndex].aRowColumnWindows.push_back( xWindow );
+ rRowColumnsWindowData[nIndex].aUIElementNames.push_back( rElement.m_aName );
+ rRowColumnsWindowData[nIndex].aRowColumnWindowSizes.emplace_back(
+ rElement.m_aDockedData.m_aPos.X,
+ rElement.m_aDockedData.m_aPos.Y,
+ aPosSize.Width,
+ aPosSize.Height );
+ if ( rRowColumnsWindowData[nIndex].nStaticSize < aPosSize.Width )
+ rRowColumnsWindowData[nIndex].nStaticSize = aPosSize.Width;
+ if ( eDockingArea == ui::DockingArea_DOCKINGAREA_LEFT )
+ rRowColumnsWindowData[nIndex].aRowColumnRect = awt::Rectangle( nLastRowColPixelPos, 0,
+ aPosSize.Width, aDockAreaRect.Height );
+ else
+ rRowColumnsWindowData[nIndex].aRowColumnRect = awt::Rectangle( ( nLastRowColPixelPos - aPosSize.Width ), 0,
+ aPosSize.Width, aDockAreaRect.Height );
+ rRowColumnsWindowData[nIndex].nVarSize += aPosSize.Height + nSpace;
+ }
+ }
+}
+
+void ToolbarLayoutManager::implts_getDockingAreaElementInfoOnSingleRowCol( ui::DockingArea eDockingArea, sal_Int32 nRowCol, SingleRowColumnWindowData& rRowColumnWindowData )
+{
+ std::vector< UIElement > aWindowVector;
+
+ if (( eDockingArea < ui::DockingArea_DOCKINGAREA_TOP ) || ( eDockingArea > ui::DockingArea_DOCKINGAREA_RIGHT ))
+ eDockingArea = ui::DockingArea_DOCKINGAREA_TOP;
+
+ bool bHorzDockArea = isHorizontalDockingArea( eDockingArea );
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ {
+ SolarMutexGuard aReadLock;
+ for (auto const& elem : m_aUIElements)
+ {
+ if (elem.m_aDockedData.m_nDockedArea == eDockingArea)
+ {
+ bool bSameRowCol = bHorzDockArea ? (elem.m_aDockedData.m_aPos.Y == nRowCol)
+ : (elem.m_aDockedData.m_aPos.X == nRowCol);
+ uno::Reference<ui::XUIElement> xUIElement(elem.m_xUIElement);
+
+ if (bSameRowCol && xUIElement.is())
+ {
+ uno::Reference<awt::XWindow> xWindow(xUIElement->getRealInterface(),
+ uno::UNO_QUERY);
+ if (xWindow.is())
+ {
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ uno::Reference<awt::XDockableWindow> xDockWindow(xWindow, uno::UNO_QUERY);
+ if (pWindow && elem.m_bVisible && xDockWindow.is() && !elem.m_bFloating)
+ aWindowVector.push_back(elem); // docked windows
+ }
+ }
+ }
+ }
+ }
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ // Initialize structure
+ rRowColumnWindowData.aUIElementNames.clear();
+ rRowColumnWindowData.aRowColumnWindows.clear();
+ rRowColumnWindowData.aRowColumnWindowSizes.clear();
+ rRowColumnWindowData.aRowColumnSpace.clear();
+ rRowColumnWindowData.nVarSize = 0;
+ rRowColumnWindowData.nStaticSize = 0;
+ rRowColumnWindowData.nSpace = 0;
+ rRowColumnWindowData.nRowColumn = nRowCol;
+
+ // Collect data from windows that are on the same row/column
+ sal_Int32 j;
+ sal_Int32 nLastPos( 0 );
+
+ const sal_uInt32 nCount = aWindowVector.size();
+ for ( j = 0; j < sal_Int32( nCount); j++ )
+ {
+ const UIElement& rElement = aWindowVector[j];
+ uno::Reference< awt::XWindow > xWindow;
+ uno::Reference< ui::XUIElement > xUIElement( rElement.m_xUIElement );
+ awt::Rectangle aPosSize;
+ if ( !lcl_checkUIElement(xUIElement,aPosSize,xWindow) )
+ continue;
+
+ sal_Int32 nSpace;
+ if ( isHorizontalDockingArea( eDockingArea ))
+ {
+ nSpace = ( rElement.m_aDockedData.m_aPos.X - nLastPos );
+
+ // Calc space before an element and store it
+ if ( rElement.m_aDockedData.m_aPos.X > nLastPos )
+ rRowColumnWindowData.nSpace += nSpace;
+ else
+ nSpace = 0;
+
+ nLastPos = rElement.m_aDockedData.m_aPos.X + aPosSize.Width;
+
+ rRowColumnWindowData.aRowColumnWindowSizes.emplace_back(
+ rElement.m_aDockedData.m_aPos.X, rElement.m_aDockedData.m_aPos.Y,
+ aPosSize.Width, aPosSize.Height );
+ if ( rRowColumnWindowData.nStaticSize < aPosSize.Height )
+ rRowColumnWindowData.nStaticSize = aPosSize.Height;
+ rRowColumnWindowData.nVarSize += aPosSize.Width;
+ }
+ else
+ {
+ // Calc space before an element and store it
+ nSpace = ( rElement.m_aDockedData.m_aPos.Y - nLastPos );
+ if ( rElement.m_aDockedData.m_aPos.Y > nLastPos )
+ rRowColumnWindowData.nSpace += nSpace;
+ else
+ nSpace = 0;
+
+ nLastPos = rElement.m_aDockedData.m_aPos.Y + aPosSize.Height;
+
+ rRowColumnWindowData.aRowColumnWindowSizes.emplace_back(
+ rElement.m_aDockedData.m_aPos.X, rElement.m_aDockedData.m_aPos.Y,
+ aPosSize.Width, aPosSize.Height );
+ if ( rRowColumnWindowData.nStaticSize < aPosSize.Width )
+ rRowColumnWindowData.nStaticSize = aPosSize.Width;
+ rRowColumnWindowData.nVarSize += aPosSize.Height;
+ }
+
+ rRowColumnWindowData.aUIElementNames.push_back( rElement.m_aName );
+ rRowColumnWindowData.aRowColumnWindows.push_back( xWindow );
+ rRowColumnWindowData.aRowColumnSpace.push_back( nSpace );
+ rRowColumnWindowData.nVarSize += nSpace;
+ }
+}
+
+::tools::Rectangle ToolbarLayoutManager::implts_getWindowRectFromRowColumn(
+ ui::DockingArea DockingArea,
+ const SingleRowColumnWindowData& rRowColumnWindowData,
+ const ::Point& rMousePos,
+ std::u16string_view rExcludeElementName )
+{
+ ::tools::Rectangle aWinRect;
+
+ if (( DockingArea < ui::DockingArea_DOCKINGAREA_TOP ) || ( DockingArea > ui::DockingArea_DOCKINGAREA_RIGHT ))
+ DockingArea = ui::DockingArea_DOCKINGAREA_TOP;
+
+ if ( rRowColumnWindowData.aRowColumnWindows.empty() )
+ return aWinRect;
+ else
+ {
+ SolarMutexClearableGuard aReadLock;
+ VclPtr<vcl::Window> pContainerWindow( VCLUnoHelper::GetWindow( m_xContainerWindow ));
+ VclPtr<vcl::Window> pDockingAreaWindow( VCLUnoHelper::GetWindow( m_xDockAreaWindows[static_cast<int>(DockingArea)] ));
+ aReadLock.clear();
+
+ // Calc correct position of the column/row rectangle to be able to compare it with mouse pos/tracking rect
+ SolarMutexGuard aGuard;
+
+ // Retrieve output size from container Window
+ if ( pDockingAreaWindow && pContainerWindow )
+ {
+ const sal_uInt32 nCount = rRowColumnWindowData.aRowColumnWindows.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ awt::Rectangle aWindowRect = rRowColumnWindowData.aRowColumnWindows[i]->getPosSize();
+ ::tools::Rectangle aRect( aWindowRect.X, aWindowRect.Y, aWindowRect.X+aWindowRect.Width, aWindowRect.Y+aWindowRect.Height );
+ aRect.SetPos( pContainerWindow->ScreenToOutputPixel( pDockingAreaWindow->OutputToScreenPixel( aRect.TopLeft() )));
+ if ( aRect.Contains( rMousePos ))
+ {
+ // Check if we have found the excluded element. If yes, we have to provide an empty rectangle.
+ // We prevent that a toolbar cannot be moved when the mouse pointer is inside its own rectangle!
+ if ( rExcludeElementName != rRowColumnWindowData.aUIElementNames[i] )
+ return aRect;
+ else
+ break;
+ }
+ }
+ }
+ }
+
+ return aWinRect;
+}
+
+::tools::Rectangle ToolbarLayoutManager::implts_determineFrontDockingRect(
+ ui::DockingArea eDockingArea,
+ sal_Int32 nRowCol,
+ const ::tools::Rectangle& rDockedElementRect,
+ std::u16string_view rMovedElementName,
+ const ::tools::Rectangle& rMovedElementRect )
+{
+ SingleRowColumnWindowData aRowColumnWindowData;
+
+ bool bHorzDockArea( isHorizontalDockingArea( eDockingArea ));
+ implts_getDockingAreaElementInfoOnSingleRowCol( eDockingArea, nRowCol, aRowColumnWindowData );
+ if ( aRowColumnWindowData.aRowColumnWindows.empty() )
+ return rMovedElementRect;
+ else
+ {
+ sal_Int32 nSpace( 0 );
+ ::tools::Rectangle aFrontDockingRect( rMovedElementRect );
+ const sal_uInt32 nCount = aRowColumnWindowData.aRowColumnWindows.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ if ( bHorzDockArea )
+ {
+ if ( aRowColumnWindowData.aRowColumnWindowSizes[i].X >= rDockedElementRect.Left() )
+ {
+ nSpace += aRowColumnWindowData.aRowColumnSpace[i];
+ break;
+ }
+ else if ( aRowColumnWindowData.aUIElementNames[i] == rMovedElementName )
+ nSpace += aRowColumnWindowData.aRowColumnWindowSizes[i].Width +
+ aRowColumnWindowData.aRowColumnSpace[i];
+ else
+ nSpace = 0;
+ }
+ else
+ {
+ if ( aRowColumnWindowData.aRowColumnWindowSizes[i].Y >= rDockedElementRect.Top() )
+ {
+ nSpace += aRowColumnWindowData.aRowColumnSpace[i];
+ break;
+ }
+ else if ( aRowColumnWindowData.aUIElementNames[i] == rMovedElementName )
+ nSpace += aRowColumnWindowData.aRowColumnWindowSizes[i].Height +
+ aRowColumnWindowData.aRowColumnSpace[i];
+ else
+ nSpace = 0;
+ }
+ }
+
+ if ( nSpace > 0 )
+ {
+ sal_Int32 nMove = std::min( nSpace, static_cast<sal_Int32>(aFrontDockingRect.getWidth()) );
+ if ( bHorzDockArea )
+ aFrontDockingRect.Move( -nMove, 0 );
+ else
+ aFrontDockingRect.Move( 0, -nMove );
+ }
+
+ return aFrontDockingRect;
+ }
+}
+
+void ToolbarLayoutManager::implts_findNextDockingPos( ui::DockingArea DockingArea, const ::Size& aUIElementSize, awt::Point& rVirtualPos, ::Point& rPixelPos )
+{
+ SolarMutexClearableGuard aReadLock;
+ if (( DockingArea < ui::DockingArea_DOCKINGAREA_TOP ) || ( DockingArea > ui::DockingArea_DOCKINGAREA_RIGHT ))
+ DockingArea = ui::DockingArea_DOCKINGAREA_TOP;
+ uno::Reference< awt::XWindow > xDockingWindow( m_xDockAreaWindows[static_cast<int>(DockingArea)] );
+ ::Size aDockingWinSize;
+
+ // Retrieve output size from container Window
+ vcl::Window* pDockingWindow = VCLUnoHelper::GetWindow( xDockingWindow );
+ if ( pDockingWindow )
+ aDockingWinSize = pDockingWindow->GetOutputSizePixel();
+ aReadLock.clear();
+
+ sal_Int32 nFreeRowColPixelPos( 0 );
+ sal_Int32 nMaxSpace( 0 );
+ sal_Int32 nNeededSpace( 0 );
+ sal_Int32 nTopDockingAreaSize( 0 );
+
+ if ( isHorizontalDockingArea( DockingArea ))
+ {
+ nMaxSpace = aDockingWinSize.Width();
+ nNeededSpace = aUIElementSize.Width();
+ }
+ else
+ {
+ nMaxSpace = aDockingWinSize.Height();
+ nNeededSpace = aUIElementSize.Height();
+ nTopDockingAreaSize = implts_getTopBottomDockingAreaSizes().Width();
+ }
+
+ std::vector< SingleRowColumnWindowData > aRowColumnsWindowData;
+
+ implts_getDockingAreaElementInfos( DockingArea, aRowColumnsWindowData );
+ sal_Int32 nPixelPos( 0 );
+ const sal_uInt32 nCount = aRowColumnsWindowData.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ SingleRowColumnWindowData& rRowColumnWindowData = aRowColumnsWindowData[i];
+
+ if (( DockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) ||
+ ( DockingArea == ui::DockingArea_DOCKINGAREA_RIGHT ))
+ nPixelPos += rRowColumnWindowData.nStaticSize;
+
+ if ((( nMaxSpace - rRowColumnWindowData.nVarSize ) >= nNeededSpace ) ||
+ ( rRowColumnWindowData.nSpace >= nNeededSpace ))
+ {
+ // Check current row where we can find the needed space
+ sal_Int32 nCurrPos( 0 );
+ const sal_uInt32 nWindowSizesCount = rRowColumnWindowData.aRowColumnWindowSizes.size();
+ for ( sal_uInt32 j = 0; j < nWindowSizesCount; j++ )
+ {
+ awt::Rectangle rRect = rRowColumnWindowData.aRowColumnWindowSizes[j];
+ sal_Int32& rSpace = rRowColumnWindowData.aRowColumnSpace[j];
+ if ( isHorizontalDockingArea( DockingArea ))
+ {
+ if ( rSpace >= nNeededSpace )
+ {
+ rVirtualPos = awt::Point( nCurrPos, rRowColumnWindowData.nRowColumn );
+ if ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP )
+ rPixelPos = ::Point( nCurrPos, nPixelPos );
+ else
+ rPixelPos = ::Point( nCurrPos, aDockingWinSize.Height() - nPixelPos );
+ return;
+ }
+ nCurrPos = rRect.X + rRect.Width;
+ }
+ else
+ {
+ if ( rSpace >= nNeededSpace )
+ {
+ rVirtualPos = awt::Point( rRowColumnWindowData.nRowColumn, nCurrPos );
+ if ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT )
+ rPixelPos = ::Point( nPixelPos, nTopDockingAreaSize + nCurrPos );
+ else
+ rPixelPos = ::Point( aDockingWinSize.Width() - nPixelPos , nTopDockingAreaSize + nCurrPos );
+ return;
+ }
+ nCurrPos = rRect.Y + rRect.Height;
+ }
+ }
+
+ if (( nCurrPos + nNeededSpace ) <= nMaxSpace )
+ {
+ if ( isHorizontalDockingArea( DockingArea ))
+ {
+ rVirtualPos = awt::Point( nCurrPos, rRowColumnWindowData.nRowColumn );
+ if ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP )
+ rPixelPos = ::Point( nCurrPos, nPixelPos );
+ else
+ rPixelPos = ::Point( nCurrPos, aDockingWinSize.Height() - nPixelPos );
+ return;
+ }
+ else
+ {
+ rVirtualPos = awt::Point( rRowColumnWindowData.nRowColumn, nCurrPos );
+ if ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT )
+ rPixelPos = ::Point( nPixelPos, nTopDockingAreaSize + nCurrPos );
+ else
+ rPixelPos = ::Point( aDockingWinSize.Width() - nPixelPos , nTopDockingAreaSize + nCurrPos );
+ return;
+ }
+ }
+ }
+
+ if (( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) || ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT ))
+ nPixelPos += rRowColumnWindowData.nStaticSize;
+ }
+
+ sal_Int32 nNextFreeRowCol( 0 );
+ sal_Int32 nRowColumnsCount = aRowColumnsWindowData.size();
+ if ( nRowColumnsCount > 0 )
+ nNextFreeRowCol = aRowColumnsWindowData[nRowColumnsCount-1].nRowColumn+1;
+ else
+ nNextFreeRowCol = 0;
+
+ if ( nNextFreeRowCol == 0 )
+ {
+ if ( DockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM )
+ nFreeRowColPixelPos = aDockingWinSize.Height() - aUIElementSize.Height();
+ else if ( DockingArea == ui::DockingArea_DOCKINGAREA_RIGHT )
+ nFreeRowColPixelPos = aDockingWinSize.Width() - aUIElementSize.Width();
+ }
+
+ if ( isHorizontalDockingArea( DockingArea ))
+ {
+ rVirtualPos = awt::Point( 0, nNextFreeRowCol );
+ if ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP )
+ rPixelPos = ::Point( 0, nFreeRowColPixelPos );
+ else
+ rPixelPos = ::Point( 0, aDockingWinSize.Height() - nFreeRowColPixelPos );
+ }
+ else
+ {
+ rVirtualPos = awt::Point( nNextFreeRowCol, 0 );
+ rPixelPos = ::Point( aDockingWinSize.Width() - nFreeRowColPixelPos, 0 );
+ }
+}
+
+void ToolbarLayoutManager::implts_calcWindowPosSizeOnSingleRowColumn(
+ sal_Int32 nDockingArea,
+ sal_Int32 nOffset,
+ SingleRowColumnWindowData& rRowColumnWindowData,
+ const ::Size& rContainerSize )
+{
+ sal_Int32 nDiff(0);
+ sal_Int32 nRCSpace( rRowColumnWindowData.nSpace );
+ sal_Int32 nContainerClientSize(0);
+
+ if ( rRowColumnWindowData.aRowColumnWindows.empty() )
+ return;
+
+ if ( isHorizontalDockingArea( nDockingArea ))
+ {
+ nContainerClientSize = rContainerSize.Width();
+ nDiff = nContainerClientSize - rRowColumnWindowData.nVarSize;
+ }
+ else
+ {
+ sal_Int32 nTopDockingAreaSize = implts_getTopBottomDockingAreaSizes().Width();
+ sal_Int32 nBottomDockingAreaSize = implts_getTopBottomDockingAreaSizes().Height();
+ nContainerClientSize = ( rContainerSize.Height() - nTopDockingAreaSize - nBottomDockingAreaSize );
+ nDiff = nContainerClientSize - rRowColumnWindowData.nVarSize;
+ }
+
+ const sal_uInt32 nCount = rRowColumnWindowData.aRowColumnWindowSizes.size();
+ if (( nDiff < 0 ) && ( nRCSpace > 0 ))
+ {
+ // First we try to reduce the size of blank space before/behind docked windows
+ sal_Int32 i = nCount - 1;
+ while ( i >= 0 )
+ {
+ sal_Int32 nSpace = rRowColumnWindowData.aRowColumnSpace[i];
+ if ( nSpace >= -nDiff )
+ {
+ if ( isHorizontalDockingArea( nDockingArea ))
+ {
+ // Try to move this and all user elements behind with the calculated difference
+ for ( sal_uInt32 j = i; j < nCount; j++ )
+ rRowColumnWindowData.aRowColumnWindowSizes[j].X += nDiff;
+ }
+ else
+ {
+ // Try to move this and all user elements behind with the calculated difference
+ for ( sal_uInt32 j = i; j < nCount; j++ )
+ rRowColumnWindowData.aRowColumnWindowSizes[j].Y += nDiff;
+ }
+ nDiff = 0;
+
+ break;
+ }
+ else if ( nSpace > 0 )
+ {
+ if ( isHorizontalDockingArea( nDockingArea ))
+ {
+ // Try to move this and all user elements behind with the calculated difference
+ for ( sal_uInt32 j = i; j < nCount; j++ )
+ rRowColumnWindowData.aRowColumnWindowSizes[j].X -= nSpace;
+ }
+ else
+ {
+ // Try to move this and all user elements behind with the calculated difference
+ for ( sal_uInt32 j = i; j < nCount; j++ )
+ rRowColumnWindowData.aRowColumnWindowSizes[j].Y -= nSpace;
+ }
+ nDiff += nSpace;
+ }
+ --i;
+ }
+ }
+
+ // Check if we have to reduce further
+ if ( nDiff < 0 )
+ {
+ // Now we have to reduce the size of certain docked windows
+ sal_Int32 i = sal_Int32( nCount - 1 );
+ while ( i >= 0 )
+ {
+ awt::Rectangle& rWinRect = rRowColumnWindowData.aRowColumnWindowSizes[i];
+ ::Size aMinSize;
+
+ SolarMutexGuard aGuard;
+ {
+ uno::Reference< awt::XWindow > xWindow = rRowColumnWindowData.aRowColumnWindows[i];
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX )
+ aMinSize = static_cast<ToolBox *>(pWindow.get())->CalcMinimumWindowSizePixel();
+ }
+
+ if ( !aMinSize.IsEmpty() )
+ {
+ if ( isHorizontalDockingArea( nDockingArea ))
+ {
+ sal_Int32 nMaxReducation = rWinRect.Width - aMinSize.Width();
+ if ( nMaxReducation >= -nDiff )
+ {
+ rWinRect.Width = rWinRect.Width + nDiff;
+ nDiff = 0;
+ }
+ else
+ {
+ rWinRect.Width = aMinSize.Width();
+ nDiff += nMaxReducation;
+ }
+
+ // Try to move this and all user elements behind with the calculated difference
+ for ( sal_uInt32 j = i; j < nCount; j++ )
+ rRowColumnWindowData.aRowColumnWindowSizes[j].X += nDiff;
+ }
+ else
+ {
+ sal_Int32 nMaxReducation = rWinRect.Height - aMinSize.Height();
+ if ( nMaxReducation >= -nDiff )
+ {
+ rWinRect.Height = rWinRect.Height + nDiff;
+ nDiff = 0;
+ }
+ else
+ {
+ rWinRect.Height = aMinSize.Height();
+ nDiff += nMaxReducation;
+ }
+
+ // Try to move this and all user elements behind with the calculated difference
+ for ( sal_uInt32 j = i; j < nCount; j++ )
+ rRowColumnWindowData.aRowColumnWindowSizes[j].Y += nDiff;
+ }
+ }
+
+ if ( nDiff >= 0 )
+ break;
+
+ --i;
+ }
+ }
+
+ SolarMutexClearableGuard aReadLock;
+ VclPtr<vcl::Window> pDockAreaWindow = VCLUnoHelper::GetWindow( m_xDockAreaWindows[nDockingArea] );
+ aReadLock.clear();
+
+ sal_Int32 nCurrPos( 0 );
+
+ SolarMutexGuard aGuard;
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ uno::Reference< awt::XWindow > xWindow = rRowColumnWindowData.aRowColumnWindows[i];
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ vcl::Window* pOldParentWindow = pWindow->GetParent();
+
+ if ( pDockAreaWindow != pOldParentWindow )
+ pWindow->SetParent( pDockAreaWindow );
+
+ awt::Rectangle aWinRect = rRowColumnWindowData.aRowColumnWindowSizes[i];
+ if ( isHorizontalDockingArea( nDockingArea ))
+ {
+ if ( aWinRect.X < nCurrPos )
+ aWinRect.X = nCurrPos;
+ pWindow->SetPosSizePixel( ::Point( aWinRect.X, nOffset ), ::Size( aWinRect.Width, rRowColumnWindowData.nStaticSize ));
+ pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+ nCurrPos += ( aWinRect.X - nCurrPos ) + aWinRect.Width;
+ }
+ else
+ {
+ if ( aWinRect.Y < nCurrPos )
+ aWinRect.Y = nCurrPos;
+ pWindow->SetPosSizePixel( ::Point( nOffset, aWinRect.Y ), ::Size( rRowColumnWindowData.nStaticSize, aWinRect.Height ));
+ pWindow->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+ nCurrPos += ( aWinRect.Y - nCurrPos ) + aWinRect.Height;
+ }
+ }
+}
+
+void ToolbarLayoutManager::implts_setLayoutDirty()
+{
+ SolarMutexGuard g;
+ m_bLayoutDirty = true;
+}
+
+void ToolbarLayoutManager::implts_setLayoutInProgress( bool bInProgress )
+{
+ SolarMutexGuard g;
+ m_bLayoutInProgress = bInProgress;
+}
+
+::tools::Rectangle ToolbarLayoutManager::implts_calcHotZoneRect( const ::tools::Rectangle& rRect, sal_Int32 nHotZoneOffset )
+{
+ ::tools::Rectangle aRect( rRect );
+
+ aRect.AdjustLeft( -nHotZoneOffset );
+ aRect.AdjustTop( -nHotZoneOffset );
+ aRect.AdjustRight(nHotZoneOffset );
+ aRect.AdjustBottom(nHotZoneOffset );
+
+ return aRect;
+}
+
+void ToolbarLayoutManager::implts_calcDockingPosSize(
+ UIElement& rUIElement,
+ DockingOperation& rDockingOperation,
+ ::tools::Rectangle& rTrackingRect,
+ const Point& rMousePos )
+{
+ SolarMutexResettableGuard aReadLock;
+ uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow );
+ ::Size aContainerWinSize;
+ vcl::Window* pContainerWindow( nullptr );
+ ::tools::Rectangle aDockingAreaOffsets( m_aDockingAreaOffsets );
+ aReadLock.clear();
+
+ if ( !rUIElement.m_xUIElement.is() )
+ {
+ rTrackingRect = ::tools::Rectangle();
+ return;
+ }
+
+ {
+ // Retrieve output size from container Window
+ SolarMutexGuard aGuard;
+ pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow );
+ aContainerWinSize = pContainerWindow->GetOutputSizePixel();
+ }
+
+ vcl::Window* pDockingAreaWindow( nullptr );
+ uno::Reference< awt::XWindow > xWindow( rUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY );
+ uno::Reference< awt::XWindow > xDockingAreaWindow;
+ ::tools::Rectangle aTrackingRect( rTrackingRect );
+ ui::DockingArea eDockedArea( rUIElement.m_aDockedData.m_nDockedArea );
+ sal_Int32 nTopDockingAreaSize( implts_getTopBottomDockingAreaSizes().Width() );
+ sal_Int32 nBottomDockingAreaSize( implts_getTopBottomDockingAreaSizes().Height() );
+ bool bHorizontalDockArea(( eDockedArea == ui::DockingArea_DOCKINGAREA_TOP ) ||
+ ( eDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM ));
+ sal_Int32 nMaxLeftRightDockAreaSize = aContainerWinSize.Height() -
+ nTopDockingAreaSize -
+ nBottomDockingAreaSize -
+ aDockingAreaOffsets.Top() -
+ aDockingAreaOffsets.Bottom();
+ ::tools::Rectangle aDockingAreaRect;
+
+ aReadLock.reset();
+ xDockingAreaWindow = m_xDockAreaWindows[static_cast<int>(eDockedArea)];
+ aReadLock.clear();
+
+ {
+ SolarMutexGuard aGuard;
+ pDockingAreaWindow = VCLUnoHelper::GetWindow( xDockingAreaWindow );
+ VclPtr<vcl::Window> pDockWindow = VCLUnoHelper::GetWindow( xWindow );
+ ToolBox* pToolBox( nullptr );
+ if ( pDockWindow && pDockWindow->GetType() == WindowType::TOOLBOX )
+ pToolBox = static_cast<ToolBox *>(pDockWindow.get());
+
+ aDockingAreaRect = ::tools::Rectangle( pDockingAreaWindow->GetPosPixel(), pDockingAreaWindow->GetSizePixel() );
+ if ( pToolBox )
+ {
+ // docked toolbars always have one line
+ ::Size aSize = pToolBox->CalcWindowSizePixel( 1, ImplConvertAlignment( eDockedArea ) );
+ aTrackingRect.SetSize( ::Size( aSize.Width(), aSize.Height() ));
+ }
+ }
+
+ // default docking operation, dock on the given row/column
+ bool bOpOutsideOfDockingArea( !aDockingAreaRect.Contains( rMousePos ));
+
+ std::vector< SingleRowColumnWindowData > aRowColumnsWindowData;
+
+ rDockingOperation = DOCKOP_ON_COLROW;
+ implts_getDockingAreaElementInfos( eDockedArea, aRowColumnsWindowData );
+
+ // determine current first row/column and last row/column
+ sal_Int32 nMaxRowCol( -1 );
+ sal_Int32 nMinRowCol( SAL_MAX_INT32 );
+ const sal_uInt32 nCount = aRowColumnsWindowData.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ if ( aRowColumnsWindowData[i].nRowColumn > nMaxRowCol )
+ nMaxRowCol = aRowColumnsWindowData[i].nRowColumn;
+ if ( aRowColumnsWindowData[i].nRowColumn < nMinRowCol )
+ nMinRowCol = aRowColumnsWindowData[i].nRowColumn;
+ }
+
+ if ( !bOpOutsideOfDockingArea )
+ {
+ // docking inside our docking area
+ sal_Int32 nIndex( -1 );
+ sal_Int32 nRowCol( -1 );
+ ::tools::Rectangle aWindowRect;
+ ::tools::Rectangle aRowColumnRect;
+
+ const sal_uInt32 nWindowDataCount = aRowColumnsWindowData.size();
+ for ( sal_uInt32 i = 0; i < nWindowDataCount; i++ )
+ {
+ ::tools::Rectangle aRect( aRowColumnsWindowData[i].aRowColumnRect.X,
+ aRowColumnsWindowData[i].aRowColumnRect.Y,
+ aRowColumnsWindowData[i].aRowColumnRect.X + aRowColumnsWindowData[i].aRowColumnRect.Width,
+ aRowColumnsWindowData[i].aRowColumnRect.Y + aRowColumnsWindowData[i].aRowColumnRect.Height );
+
+ {
+ // Calc correct position of the column/row rectangle to be able to compare it with mouse pos/tracking rect
+ SolarMutexGuard aGuard;
+ aRect.SetPos( pContainerWindow->ScreenToOutputPixel( pDockingAreaWindow->OutputToScreenPixel( aRect.TopLeft() )));
+ }
+
+ bool bIsInsideRowCol( aRect.Contains( rMousePos ) );
+ if ( bIsInsideRowCol )
+ {
+ nIndex = i;
+ nRowCol = aRowColumnsWindowData[i].nRowColumn;
+ rDockingOperation = implts_determineDockingOperation( eDockedArea, aRect, rMousePos );
+ aWindowRect = implts_getWindowRectFromRowColumn( eDockedArea, aRowColumnsWindowData[i], rMousePos, rUIElement.m_aName );
+ aRowColumnRect = aRect;
+ break;
+ }
+ }
+
+ OSL_ENSURE( ( nIndex >= 0 ) && ( nRowCol >= 0 ), "Impossible case - no row/column found but mouse pointer is inside our docking area" );
+ if (( nIndex >= 0 ) && ( nRowCol >= 0 ))
+ {
+ if ( rDockingOperation == DOCKOP_ON_COLROW )
+ {
+ if ( !aWindowRect.IsEmpty())
+ {
+ // Tracking rect is on a row/column and mouse is over a docked toolbar.
+ // Determine if the tracking rect must be located before/after the docked toolbar.
+
+ ::tools::Rectangle aUIElementRect( aWindowRect );
+ sal_Int32 nMiddle( bHorizontalDockArea ? ( aWindowRect.Left() + aWindowRect.getWidth() / 2 ) :
+ ( aWindowRect.Top() + aWindowRect.getHeight() / 2 ));
+ bool bInsertBefore( bHorizontalDockArea ? ( rMousePos.X() < nMiddle ) : ( rMousePos.Y() < nMiddle ));
+ if ( bInsertBefore )
+ {
+ if ( bHorizontalDockArea )
+ {
+ sal_Int32 nSize = std::clamp( sal_Int32(aContainerWinSize.Width() - aWindowRect.Left()),
+ sal_Int32(0), sal_Int32(aTrackingRect.getWidth()) );
+ if ( nSize == 0 )
+ nSize = aWindowRect.getWidth();
+
+ aUIElementRect.SetSize( ::Size( nSize, aWindowRect.getHeight() ));
+ aWindowRect = implts_determineFrontDockingRect( eDockedArea, nRowCol, aWindowRect,rUIElement.m_aName, aUIElementRect );
+
+ // Set virtual position
+ rUIElement.m_aDockedData.m_aPos.X = aWindowRect.Left();
+ rUIElement.m_aDockedData.m_aPos.Y = nRowCol;
+ }
+ else
+ {
+ sal_Int32 nSize = std::clamp( sal_Int32(nTopDockingAreaSize + nMaxLeftRightDockAreaSize - aWindowRect.Top()),
+ sal_Int32(0), sal_Int32(aTrackingRect.getHeight()) );
+ if ( nSize == 0 )
+ nSize = aWindowRect.getHeight();
+
+ aUIElementRect.SetSize( ::Size( aWindowRect.getWidth(), nSize ));
+ aWindowRect = implts_determineFrontDockingRect( eDockedArea, nRowCol, aWindowRect, rUIElement.m_aName, aUIElementRect );
+
+ // Set virtual position
+ sal_Int32 nPosY = pDockingAreaWindow->ScreenToOutputPixel(
+ pContainerWindow->OutputToScreenPixel( aWindowRect.TopLeft() )).Y();
+ rUIElement.m_aDockedData.m_aPos.X = nRowCol;
+ rUIElement.m_aDockedData.m_aPos.Y = nPosY;
+ }
+
+ rTrackingRect = aWindowRect;
+ return;
+ }
+ else
+ {
+ if ( bHorizontalDockArea )
+ {
+ sal_Int32 nSize = ::std::clamp( sal_Int32(aContainerWinSize.Width() - aWindowRect.Right()),
+ sal_Int32(0), sal_Int32(aTrackingRect.getWidth()) );
+ if ( nSize == 0 )
+ {
+ aUIElementRect.SetPos( ::Point( aContainerWinSize.Width() - aTrackingRect.getWidth(), aWindowRect.Top() ));
+ aUIElementRect.SetSize( ::Size( aTrackingRect.getWidth(), aWindowRect.getHeight() ));
+ rUIElement.m_aDockedData.m_aPos.X = aUIElementRect.Left();
+
+ }
+ else
+ {
+ aUIElementRect.SetPos( ::Point( aWindowRect.Right(), aWindowRect.Top() ));
+ aUIElementRect.SetSize( ::Size( nSize, aWindowRect.getHeight() ));
+ rUIElement.m_aDockedData.m_aPos.X = aWindowRect.Right();
+ }
+
+ // Set virtual position
+ rUIElement.m_aDockedData.m_aPos.Y = nRowCol;
+ }
+ else
+ {
+ sal_Int32 nSize = std::clamp( sal_Int32(nTopDockingAreaSize + nMaxLeftRightDockAreaSize - aWindowRect.Bottom()),
+ sal_Int32(0), sal_Int32(aTrackingRect.getHeight()) );
+ aUIElementRect.SetPos( ::Point( aWindowRect.Left(), aWindowRect.Bottom() ));
+ aUIElementRect.SetSize( ::Size( aWindowRect.getWidth(), nSize ));
+
+ // Set virtual position
+ sal_Int32 nPosY( 0 );
+ {
+ SolarMutexGuard aGuard;
+ nPosY = pDockingAreaWindow->ScreenToOutputPixel(
+ pContainerWindow->OutputToScreenPixel( aWindowRect.BottomRight() )).Y();
+ }
+ rUIElement.m_aDockedData.m_aPos.X = nRowCol;
+ rUIElement.m_aDockedData.m_aPos.Y = nPosY;
+ }
+
+ rTrackingRect = aUIElementRect;
+ return;
+ }
+ }
+ else
+ {
+ implts_setTrackingRect( eDockedArea, rMousePos, aTrackingRect );
+ rTrackingRect = implts_calcTrackingAndElementRect(
+ eDockedArea, nRowCol, rUIElement,
+ aTrackingRect, aRowColumnRect, aContainerWinSize );
+ return;
+ }
+ }
+ else
+ {
+ if ((( nRowCol == nMinRowCol ) && ( rDockingOperation == DOCKOP_BEFORE_COLROW )) ||
+ (( nRowCol == nMaxRowCol ) && ( rDockingOperation == DOCKOP_AFTER_COLROW )))
+ bOpOutsideOfDockingArea = true;
+ else
+ {
+ // handle docking before/after a row
+ implts_setTrackingRect( eDockedArea, rMousePos, aTrackingRect );
+ rTrackingRect = implts_calcTrackingAndElementRect(
+ eDockedArea, nRowCol, rUIElement,
+ aTrackingRect, aRowColumnRect, aContainerWinSize );
+
+ sal_Int32 nOffsetX( 0 );
+ sal_Int32 nOffsetY( 0 );
+ if ( bHorizontalDockArea )
+ nOffsetY = sal_Int32( floor( aRowColumnRect.getHeight() / 2.0 + 0.5 ));
+ else
+ nOffsetX = sal_Int32( floor( aRowColumnRect.getWidth() / 2.0 + 0.5 ));
+
+ if ( rDockingOperation == DOCKOP_BEFORE_COLROW )
+ {
+ if (( eDockedArea == ui::DockingArea_DOCKINGAREA_TOP ) || ( eDockedArea == ui::DockingArea_DOCKINGAREA_LEFT ))
+ {
+ // Docking before/after means move track rectangle half column/row.
+ // As left and top are ordered 0...n instead of right and bottom
+ // which uses n...0, we have to use negative values for top/left.
+ nOffsetX *= -1;
+ nOffsetY *= -1;
+ }
+ }
+ else
+ {
+ if (( eDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM ) || ( eDockedArea == ui::DockingArea_DOCKINGAREA_RIGHT ))
+ {
+ // Docking before/after means move track rectangle half column/row.
+ // As left and top are ordered 0...n instead of right and bottom
+ // which uses n...0, we have to use negative values for top/left.
+ nOffsetX *= -1;
+ nOffsetY *= -1;
+ }
+ nRowCol++;
+ }
+
+ if ( bHorizontalDockArea )
+ rUIElement.m_aDockedData.m_aPos.Y = nRowCol;
+ else
+ rUIElement.m_aDockedData.m_aPos.X = nRowCol;
+
+ rTrackingRect.Move( nOffsetX, nOffsetY );
+ rTrackingRect.SetSize( aTrackingRect.GetSize() );
+ }
+ }
+ }
+ }
+
+ // Docking outside of our docking window area =>
+ // Users want to dock before/after first/last docked element or to an empty docking area
+ if ( !bOpOutsideOfDockingArea )
+ return;
+
+ // set correct size for docking
+ implts_setTrackingRect( eDockedArea, rMousePos, aTrackingRect );
+ rTrackingRect = aTrackingRect;
+
+ if ( bHorizontalDockArea )
+ {
+ sal_Int32 nPosX( std::max( sal_Int32( rTrackingRect.Left()), sal_Int32( 0 )));
+ if (( nPosX + rTrackingRect.getWidth()) > aContainerWinSize.Width() )
+ nPosX = std::min( nPosX,
+ std::max( sal_Int32( aContainerWinSize.Width() - rTrackingRect.getWidth() ),
+ sal_Int32( 0 )));
+
+ sal_Int32 nSize = std::min( aContainerWinSize.Width(), rTrackingRect.getWidth() );
+ sal_Int32 nDockHeight = std::max( static_cast<sal_Int32>(aDockingAreaRect.getHeight()), sal_Int32( 0 ));
+ if ( nDockHeight == 0 )
+ {
+ sal_Int32 nPosY( std::max( aDockingAreaRect.Top(), aDockingAreaRect.Bottom() ));
+ if ( eDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM )
+ nPosY -= rTrackingRect.getHeight();
+ rTrackingRect.SetPos( Point( nPosX, nPosY ));
+ rUIElement.m_aDockedData.m_aPos.Y = 0;
+ }
+ else if ( rMousePos.Y() < ( aDockingAreaRect.Top() + ( nDockHeight / 2 )))
+ {
+ rTrackingRect.SetPos( Point( nPosX, aDockingAreaRect.Top() - rTrackingRect.getHeight() ));
+ if ( eDockedArea == ui::DockingArea_DOCKINGAREA_TOP )
+ rUIElement.m_aDockedData.m_aPos.Y = 0;
+ else
+ rUIElement.m_aDockedData.m_aPos.Y = ( nMaxRowCol >= 0 ) ? nMaxRowCol+1 : 0;
+ rDockingOperation = DOCKOP_BEFORE_COLROW;
+ }
+ else
+ {
+ rTrackingRect.SetPos( Point( nPosX, aDockingAreaRect.Bottom() ));
+ if ( eDockedArea == ui::DockingArea_DOCKINGAREA_TOP )
+ rUIElement.m_aDockedData.m_aPos.Y = ( nMaxRowCol >= 0 ) ? nMaxRowCol+1 : 0;
+ else
+ rUIElement.m_aDockedData.m_aPos.Y = 0;
+ rDockingOperation = DOCKOP_AFTER_COLROW;
+ }
+ rTrackingRect.setWidth( nSize );
+
+ {
+ SolarMutexGuard aGuard;
+ nPosX = pDockingAreaWindow->ScreenToOutputPixel(
+ pContainerWindow->OutputToScreenPixel( rTrackingRect.TopLeft() )).X();
+ }
+ rUIElement.m_aDockedData.m_aPos.X = nPosX;
+ }
+ else
+ {
+ sal_Int32 nMaxDockingAreaHeight = std::max<sal_Int32>( 0, nMaxLeftRightDockAreaSize );
+ sal_Int32 nPosY( std::max<sal_Int32>( aTrackingRect.Top(), nTopDockingAreaSize ));
+ if (( nPosY + aTrackingRect.getHeight()) > ( nTopDockingAreaSize + nMaxDockingAreaHeight ))
+ nPosY = std::min( nPosY,
+ std::max<sal_Int32>( nTopDockingAreaSize + ( nMaxDockingAreaHeight - aTrackingRect.getHeight() ),
+ nTopDockingAreaSize ));
+
+ sal_Int32 nSize = std::min( nMaxDockingAreaHeight, static_cast<sal_Int32>(aTrackingRect.getHeight()) );
+ sal_Int32 nDockWidth = std::max( static_cast<sal_Int32>(aDockingAreaRect.getWidth()), sal_Int32( 0 ));
+ if ( nDockWidth == 0 )
+ {
+ sal_Int32 nPosX( std::max( aDockingAreaRect.Left(), aDockingAreaRect.Right() ));
+ if ( eDockedArea == ui::DockingArea_DOCKINGAREA_RIGHT )
+ nPosX -= rTrackingRect.getWidth();
+ rTrackingRect.SetPos( Point( nPosX, nPosY ));
+ rUIElement.m_aDockedData.m_aPos.X = 0;
+ }
+ else if ( rMousePos.X() < ( aDockingAreaRect.Left() + ( nDockWidth / 2 )))
+ {
+ rTrackingRect.SetPos( Point( aDockingAreaRect.Left() - rTrackingRect.getWidth(), nPosY ));
+ if ( eDockedArea == ui::DockingArea_DOCKINGAREA_LEFT )
+ rUIElement.m_aDockedData.m_aPos.X = 0;
+ else
+ rUIElement.m_aDockedData.m_aPos.X = ( nMaxRowCol >= 0 ) ? nMaxRowCol+1 : 0;
+ rDockingOperation = DOCKOP_BEFORE_COLROW;
+ }
+ else
+ {
+ rTrackingRect.SetPos( Point( aDockingAreaRect.Right(), nPosY ));
+ if ( eDockedArea == ui::DockingArea_DOCKINGAREA_LEFT )
+ rUIElement.m_aDockedData.m_aPos.X = ( nMaxRowCol >= 0 ) ? nMaxRowCol+1 : 0;
+ else
+ rUIElement.m_aDockedData.m_aPos.X = 0;
+ rDockingOperation = DOCKOP_AFTER_COLROW;
+ }
+ rTrackingRect.setHeight( nSize );
+
+ {
+ SolarMutexGuard aGuard;
+ nPosY = pDockingAreaWindow->ScreenToOutputPixel(
+ pContainerWindow->OutputToScreenPixel( rTrackingRect.TopLeft() )).Y();
+ }
+ rUIElement.m_aDockedData.m_aPos.Y = nPosY;
+ }
+}
+
+framework::ToolbarLayoutManager::DockingOperation ToolbarLayoutManager::implts_determineDockingOperation(
+ ui::DockingArea DockingArea,
+ const ::tools::Rectangle& rRowColRect,
+ const Point& rMousePos )
+{
+ constexpr sal_Int32 nHorzVerticalRegionSize = 6;
+ constexpr sal_Int32 nHorzVerticalMoveRegion = 4;
+
+ if ( rRowColRect.Contains( rMousePos ))
+ {
+ if ( isHorizontalDockingArea( DockingArea ))
+ {
+ sal_Int32 nRegion = rRowColRect.getHeight() / nHorzVerticalRegionSize;
+ sal_Int32 nPosY = rRowColRect.Top() + nRegion;
+
+ if ( rMousePos.Y() < nPosY )
+ return ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) ? DOCKOP_BEFORE_COLROW : DOCKOP_AFTER_COLROW;
+ else if ( rMousePos.Y() < ( nPosY + nRegion*nHorzVerticalMoveRegion ))
+ return DOCKOP_ON_COLROW;
+ else
+ return ( DockingArea == ui::DockingArea_DOCKINGAREA_TOP ) ? DOCKOP_AFTER_COLROW : DOCKOP_BEFORE_COLROW;
+ }
+ else
+ {
+ sal_Int32 nRegion = rRowColRect.getWidth() / nHorzVerticalRegionSize;
+ sal_Int32 nPosX = rRowColRect.Left() + nRegion;
+
+ if ( rMousePos.X() < nPosX )
+ return ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) ? DOCKOP_BEFORE_COLROW : DOCKOP_AFTER_COLROW;
+ else if ( rMousePos.X() < ( nPosX + nRegion*nHorzVerticalMoveRegion ))
+ return DOCKOP_ON_COLROW;
+ else
+ return ( DockingArea == ui::DockingArea_DOCKINGAREA_LEFT ) ? DOCKOP_AFTER_COLROW : DOCKOP_BEFORE_COLROW;
+ }
+ }
+ else
+ return DOCKOP_ON_COLROW;
+}
+
+::tools::Rectangle ToolbarLayoutManager::implts_calcTrackingAndElementRect(
+ ui::DockingArea eDockingArea,
+ sal_Int32 nRowCol,
+ UIElement& rUIElement,
+ const ::tools::Rectangle& rTrackingRect,
+ const ::tools::Rectangle& rRowColumnRect,
+ const ::Size& rContainerWinSize )
+{
+ SolarMutexResettableGuard aReadGuard;
+ ::tools::Rectangle aDockingAreaOffsets( m_aDockingAreaOffsets );
+ aReadGuard.clear();
+
+ bool bHorizontalDockArea( isHorizontalDockingArea( eDockingArea ));
+
+ sal_Int32 nTopDockingAreaSize( implts_getTopBottomDockingAreaSizes().Width() );
+ sal_Int32 nBottomDockingAreaSize( implts_getTopBottomDockingAreaSizes().Height() );
+
+ sal_Int32 nMaxLeftRightDockAreaSize = rContainerWinSize.Height() -
+ nTopDockingAreaSize -
+ nBottomDockingAreaSize -
+ aDockingAreaOffsets.Top() -
+ aDockingAreaOffsets.Bottom();
+
+ ::tools::Rectangle aTrackingRect( rTrackingRect );
+ if ( bHorizontalDockArea )
+ {
+ sal_Int32 nPosX( std::max( sal_Int32( rTrackingRect.Left()), sal_Int32( 0 )));
+ if (( nPosX + rTrackingRect.getWidth()) > rContainerWinSize.Width() )
+ nPosX = std::min( nPosX,
+ std::max( sal_Int32( rContainerWinSize.Width() - rTrackingRect.getWidth() ),
+ sal_Int32( 0 )));
+
+ sal_Int32 nSize = std::min( rContainerWinSize.Width(), rTrackingRect.getWidth() );
+
+ aTrackingRect.SetPos( ::Point( nPosX, rRowColumnRect.Top() ));
+ aTrackingRect.setWidth( nSize );
+ aTrackingRect.setHeight( rRowColumnRect.getHeight() );
+
+ // Set virtual position
+ rUIElement.m_aDockedData.m_aPos.X = nPosX;
+ rUIElement.m_aDockedData.m_aPos.Y = nRowCol;
+ }
+ else
+ {
+ sal_Int32 nMaxDockingAreaHeight = std::max<sal_Int32>( 0, nMaxLeftRightDockAreaSize );
+
+ sal_Int32 nPosY( std::max<sal_Int32>( aTrackingRect.Top(), nTopDockingAreaSize ));
+ if (( nPosY + aTrackingRect.getHeight()) > ( nTopDockingAreaSize + nMaxDockingAreaHeight ))
+ nPosY = std::min( nPosY,
+ std::max<sal_Int32>( nTopDockingAreaSize + ( nMaxDockingAreaHeight - aTrackingRect.getHeight() ),
+ nTopDockingAreaSize ));
+
+ sal_Int32 nSize = std::min( nMaxDockingAreaHeight, static_cast<sal_Int32>(aTrackingRect.getHeight()) );
+
+ aTrackingRect.SetPos( ::Point( rRowColumnRect.Left(), nPosY ));
+ aTrackingRect.setWidth( rRowColumnRect.getWidth() );
+ aTrackingRect.setHeight( nSize );
+
+ aReadGuard.reset();
+ uno::Reference< awt::XWindow > xDockingAreaWindow( m_xDockAreaWindows[static_cast<int>(eDockingArea)] );
+ uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow );
+ aReadGuard.clear();
+
+ sal_Int32 nDockPosY( 0 );
+ {
+ SolarMutexGuard aGuard;
+ vcl::Window* pDockingAreaWindow = VCLUnoHelper::GetWindow( xDockingAreaWindow );
+ VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow );
+ nDockPosY = pDockingAreaWindow->ScreenToOutputPixel( pContainerWindow->OutputToScreenPixel( ::Point( 0, nPosY ))).Y();
+ }
+
+ // Set virtual position
+ rUIElement.m_aDockedData.m_aPos.X = nRowCol;
+ rUIElement.m_aDockedData.m_aPos.Y = nDockPosY;
+ }
+
+ return aTrackingRect;
+}
+
+void ToolbarLayoutManager::implts_setTrackingRect( ui::DockingArea eDockingArea, const ::Point& rMousePos, ::tools::Rectangle& rTrackingRect )
+{
+ ::Point aPoint( rTrackingRect.TopLeft());
+ if ( isHorizontalDockingArea( eDockingArea ))
+ aPoint.setX( rMousePos.X() );
+ else
+ aPoint.setY( rMousePos.Y() );
+ rTrackingRect.SetPos( aPoint );
+}
+
+void ToolbarLayoutManager::implts_renumberRowColumnData(
+ ui::DockingArea eDockingArea,
+ const UIElement& rUIElement )
+{
+ SolarMutexClearableGuard aReadLock;
+ uno::Reference< container::XNameAccess > xPersistentWindowState( m_xPersistentWindowState );
+ aReadLock.clear();
+
+ bool bHorzDockingArea( isHorizontalDockingArea( eDockingArea ));
+ sal_Int32 nRowCol( bHorzDockingArea ? rUIElement.m_aDockedData.m_aPos.Y : rUIElement.m_aDockedData.m_aPos.X );
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ {
+ SolarMutexGuard aWriteLock;
+ for (auto& elem : m_aUIElements)
+ {
+ if ((elem.m_aDockedData.m_nDockedArea == eDockingArea)
+ && (elem.m_aName != rUIElement.m_aName))
+ {
+ // Don't change toolbars without a valid docking position!
+ if (isDefaultPos(elem.m_aDockedData.m_aPos))
+ continue;
+
+ sal_Int32 nWindowRowCol
+ = bHorzDockingArea ? elem.m_aDockedData.m_aPos.Y : elem.m_aDockedData.m_aPos.X;
+ if (nWindowRowCol >= nRowCol)
+ {
+ if (bHorzDockingArea)
+ elem.m_aDockedData.m_aPos.Y += 1;
+ else
+ elem.m_aDockedData.m_aPos.X += 1;
+ }
+ }
+ }
+ }
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ // We have to change the persistent window state part
+ if ( !xPersistentWindowState.is() )
+ return;
+
+ try
+ {
+ const uno::Sequence< OUString > aWindowElements = xPersistentWindowState->getElementNames();
+ for ( OUString const & rWindowElementName : aWindowElements )
+ {
+ if ( rUIElement.m_aName != rWindowElementName )
+ {
+ try
+ {
+ uno::Sequence< beans::PropertyValue > aPropValueSeq;
+ awt::Point aDockedPos;
+ ui::DockingArea nDockedArea( ui::DockingArea_DOCKINGAREA_DEFAULT );
+
+ xPersistentWindowState->getByName( rWindowElementName ) >>= aPropValueSeq;
+ for ( beans::PropertyValue const & rProp : std::as_const(aPropValueSeq) )
+ {
+ if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKINGAREA )
+ rProp.Value >>= nDockedArea;
+ else if ( rProp.Name == WINDOWSTATE_PROPERTY_DOCKPOS )
+ rProp.Value >>= aDockedPos;
+ }
+
+ // Don't change toolbars without a valid docking position!
+ if ( isDefaultPos( aDockedPos ))
+ continue;
+
+ sal_Int32 nWindowRowCol = bHorzDockingArea ? aDockedPos.Y : aDockedPos.X;
+ if (( nDockedArea == eDockingArea ) && ( nWindowRowCol >= nRowCol ))
+ {
+ if ( bHorzDockingArea )
+ aDockedPos.Y += 1;
+ else
+ aDockedPos.X += 1;
+
+ uno::Reference< container::XNameReplace > xReplace( xPersistentWindowState, uno::UNO_QUERY );
+ xReplace->replaceByName( rWindowElementName, css::uno::Any( aPropValueSeq ));
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+// XWindowListener
+
+void SAL_CALL ToolbarLayoutManager::windowResized( const awt::WindowEvent& aEvent )
+{
+ SolarMutexClearableGuard aWriteLock;
+ bool bLocked( m_bDockingInProgress );
+ bool bLayoutInProgress( m_bLayoutInProgress );
+ aWriteLock.clear();
+
+ // Do not do anything if we are in the middle of a docking process. This would interfere all other
+ // operations. We will store the new position and size in the docking handlers.
+ // Do not do anything if we are in the middle of our layouting process. We will adapt the position
+ // and size of the user interface elements.
+ if ( bLocked || bLayoutInProgress )
+ return;
+
+ bool bNotify( false );
+ uno::Reference< awt::XWindow > xWindow( aEvent.Source, uno::UNO_QUERY );
+
+ UIElement aUIElement = implts_findToolbar( aEvent.Source );
+ if ( aUIElement.m_xUIElement.is() )
+ {
+ if ( aUIElement.m_bFloating )
+ {
+ uno::Reference< awt::XWindow2 > xWindow2( xWindow, uno::UNO_QUERY );
+
+ if( xWindow2.is() )
+ {
+ awt::Rectangle aPos = xWindow2->getPosSize();
+ awt::Size aSize = xWindow2->getOutputSize(); // always use output size for consistency
+ bool bVisible = xWindow2->isVisible();
+
+ // update element data
+ aUIElement.m_aFloatingData.m_aPos = awt::Point(aPos.X, aPos.Y);
+ aUIElement.m_aFloatingData.m_aSize = aSize;
+ aUIElement.m_bVisible = bVisible;
+ }
+
+ implts_writeWindowStateData( aUIElement );
+ }
+ else
+ {
+ implts_setLayoutDirty();
+ bNotify = true;
+ }
+ }
+
+ if ( bNotify )
+ m_pParentLayouter->requestLayout();
+}
+
+void SAL_CALL ToolbarLayoutManager::windowMoved( const awt::WindowEvent& /*aEvent*/ )
+{
+}
+
+void SAL_CALL ToolbarLayoutManager::windowShown( const lang::EventObject& /*aEvent*/ )
+{
+}
+
+void SAL_CALL ToolbarLayoutManager::windowHidden( const lang::EventObject& /*aEvent*/ )
+{
+}
+
+// XDockableWindowListener
+
+void SAL_CALL ToolbarLayoutManager::startDocking( const awt::DockingEvent& e )
+{
+ bool bWinFound( false );
+
+ SolarMutexClearableGuard aReadGuard;
+ uno::Reference< awt::XWindow2 > xWindow( e.Source, uno::UNO_QUERY );
+ aReadGuard.clear();
+
+ UIElement aUIElement = implts_findToolbar( e.Source );
+
+ if ( aUIElement.m_xUIElement.is() && xWindow.is() )
+ {
+ bWinFound = true;
+ uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY );
+ if ( xDockWindow->isFloating() )
+ {
+ awt::Rectangle aPos = xWindow->getPosSize();
+ awt::Size aSize = xWindow->getOutputSize();
+
+ aUIElement.m_aFloatingData.m_aPos = awt::Point(aPos.X, aPos.Y);
+ aUIElement.m_aFloatingData.m_aSize = aSize;
+
+ SolarMutexGuard aGuard;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX )
+ {
+ ToolBox* pToolBox = static_cast<ToolBox *>(pWindow.get());
+ aUIElement.m_aFloatingData.m_nLines = pToolBox->GetFloatingLines();
+ aUIElement.m_aFloatingData.m_bIsHorizontal = isToolboxHorizontalAligned( pToolBox );
+ }
+ }
+ }
+
+ SolarMutexGuard g;
+ m_bDockingInProgress = bWinFound;
+ m_aDockUIElement = aUIElement;
+ m_aDockUIElement.m_bUserActive = true;
+}
+
+awt::DockingData SAL_CALL ToolbarLayoutManager::docking( const awt::DockingEvent& e )
+{
+ constexpr sal_Int32 MAGNETIC_DISTANCE_UNDOCK = 25;
+ constexpr sal_Int32 MAGNETIC_DISTANCE_DOCK = 20;
+
+ SolarMutexClearableGuard aReadLock;
+ awt::DockingData aDockingData;
+ uno::Reference< awt::XDockableWindow > xDockWindow( e.Source, uno::UNO_QUERY );
+ uno::Reference< awt::XWindow > xWindow( e.Source, uno::UNO_QUERY );
+ uno::Reference< awt::XWindow > xTopDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_TOP)] );
+ uno::Reference< awt::XWindow > xLeftDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_LEFT)] );
+ uno::Reference< awt::XWindow > xRightDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_RIGHT)] );
+ uno::Reference< awt::XWindow > xBottomDockingWindow( m_xDockAreaWindows[int(ui::DockingArea_DOCKINGAREA_BOTTOM)] );
+ uno::Reference< awt::XWindow2 > xContainerWindow( m_xContainerWindow );
+ UIElement aUIDockingElement( m_aDockUIElement );
+
+ bool bDockingInProgress( m_bDockingInProgress );
+ aReadLock.clear();
+
+ if ( bDockingInProgress )
+ aDockingData.TrackingRectangle = e.TrackingRectangle;
+
+ if ( bDockingInProgress && xDockWindow.is() && xWindow.is() )
+ {
+ try
+ {
+ SolarMutexGuard aGuard;
+
+ DockingOperation eDockingOperation( DOCKOP_ON_COLROW );
+ ui::DockingArea eDockingArea( ui::DockingArea(-1) ); // none
+ sal_Int32 nMagneticZone( aUIDockingElement.m_bFloating ? MAGNETIC_DISTANCE_DOCK : MAGNETIC_DISTANCE_UNDOCK );
+ ::tools::Rectangle aTrackingRect( e.TrackingRectangle.X, e.TrackingRectangle.Y,
+ ( e.TrackingRectangle.X + e.TrackingRectangle.Width ),
+ ( e.TrackingRectangle.Y + e.TrackingRectangle.Height ));
+
+ awt::Rectangle aTmpRect = xTopDockingWindow->getPosSize();
+ ::tools::Rectangle aTopDockRect( aTmpRect.X, aTmpRect.Y, aTmpRect.Width, aTmpRect.Height );
+ ::tools::Rectangle aHotZoneTopDockRect( implts_calcHotZoneRect( aTopDockRect, nMagneticZone ));
+
+ aTmpRect = xBottomDockingWindow->getPosSize();
+ ::tools::Rectangle aBottomDockRect( aTmpRect.X, aTmpRect.Y, ( aTmpRect.X + aTmpRect.Width), ( aTmpRect.Y + aTmpRect.Height ));
+ ::tools::Rectangle aHotZoneBottomDockRect( implts_calcHotZoneRect( aBottomDockRect, nMagneticZone ));
+
+ aTmpRect = xLeftDockingWindow->getPosSize();
+ ::tools::Rectangle aLeftDockRect( aTmpRect.X, aTmpRect.Y, ( aTmpRect.X + aTmpRect.Width ), ( aTmpRect.Y + aTmpRect.Height ));
+ ::tools::Rectangle aHotZoneLeftDockRect( implts_calcHotZoneRect( aLeftDockRect, nMagneticZone ));
+
+ aTmpRect = xRightDockingWindow->getPosSize();
+ ::tools::Rectangle aRightDockRect( aTmpRect.X, aTmpRect.Y, ( aTmpRect.X + aTmpRect.Width ), ( aTmpRect.Y + aTmpRect.Height ));
+ ::tools::Rectangle aHotZoneRightDockRect( implts_calcHotZoneRect( aRightDockRect, nMagneticZone ));
+
+ VclPtr<vcl::Window> pContainerWindow( VCLUnoHelper::GetWindow( xContainerWindow ) );
+ ::Point aMousePos( pContainerWindow->ScreenToOutputPixel( ::Point( e.MousePos.X, e.MousePos.Y )));
+
+ if ( aHotZoneTopDockRect.Contains( aMousePos ))
+ eDockingArea = ui::DockingArea_DOCKINGAREA_TOP;
+ else if ( aHotZoneBottomDockRect.Contains( aMousePos ))
+ eDockingArea = ui::DockingArea_DOCKINGAREA_BOTTOM;
+ else if ( aHotZoneLeftDockRect.Contains( aMousePos ))
+ eDockingArea = ui::DockingArea_DOCKINGAREA_LEFT;
+ else if ( aHotZoneRightDockRect.Contains( aMousePos ))
+ eDockingArea = ui::DockingArea_DOCKINGAREA_RIGHT;
+
+ // Higher priority for movements inside the real docking area
+ if ( aTopDockRect.Contains( aMousePos ))
+ eDockingArea = ui::DockingArea_DOCKINGAREA_TOP;
+ else if ( aBottomDockRect.Contains( aMousePos ))
+ eDockingArea = ui::DockingArea_DOCKINGAREA_BOTTOM;
+ else if ( aLeftDockRect.Contains( aMousePos ))
+ eDockingArea = ui::DockingArea_DOCKINGAREA_LEFT;
+ else if ( aRightDockRect.Contains( aMousePos ))
+ eDockingArea = ui::DockingArea_DOCKINGAREA_RIGHT;
+
+ // Determine if we have a toolbar and set alignment according to the docking area!
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ ToolBox* pToolBox = nullptr;
+ if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX )
+ pToolBox = static_cast<ToolBox *>(pWindow.get());
+
+ if ( eDockingArea != ui::DockingArea(-1) )
+ {
+ if ( eDockingArea == ui::DockingArea_DOCKINGAREA_TOP )
+ {
+ aUIDockingElement.m_aDockedData.m_nDockedArea = ui::DockingArea_DOCKINGAREA_TOP;
+ aUIDockingElement.m_bFloating = false;
+ }
+ else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_BOTTOM )
+ {
+ aUIDockingElement.m_aDockedData.m_nDockedArea = ui::DockingArea_DOCKINGAREA_BOTTOM;
+ aUIDockingElement.m_bFloating = false;
+ }
+ else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_LEFT )
+ {
+ aUIDockingElement.m_aDockedData.m_nDockedArea = ui::DockingArea_DOCKINGAREA_LEFT;
+ aUIDockingElement.m_bFloating = false;
+ }
+ else if ( eDockingArea == ui::DockingArea_DOCKINGAREA_RIGHT )
+ {
+ aUIDockingElement.m_aDockedData.m_nDockedArea = ui::DockingArea_DOCKINGAREA_RIGHT;
+ aUIDockingElement.m_bFloating = false;
+ }
+
+ ::Point aOutputPos = pContainerWindow->ScreenToOutputPixel( aTrackingRect.TopLeft() );
+ aTrackingRect.SetPos( aOutputPos );
+
+ ::tools::Rectangle aNewDockingRect( aTrackingRect );
+
+ implts_calcDockingPosSize( aUIDockingElement, eDockingOperation, aNewDockingRect, aMousePos );
+
+ ::Point aScreenPos = pContainerWindow->OutputToScreenPixel( aNewDockingRect.TopLeft() );
+ aDockingData.TrackingRectangle = awt::Rectangle( aScreenPos.X(), aScreenPos.Y(),
+ aNewDockingRect.getWidth(), aNewDockingRect.getHeight() );
+ }
+ else if (pToolBox)
+ {
+ bool bIsHorizontal = isToolboxHorizontalAligned( pToolBox );
+ awt::Size aFloatSize = aUIDockingElement.m_aFloatingData.m_aSize;
+ if ( aFloatSize.Width > 0 && aFloatSize.Height > 0 )
+ {
+ aUIDockingElement.m_aFloatingData.m_aPos = AWTPoint(pContainerWindow->ScreenToOutputPixel(VCLPoint(e.MousePos)));
+ aDockingData.TrackingRectangle.Height = aFloatSize.Height;
+ aDockingData.TrackingRectangle.Width = aFloatSize.Width;
+ }
+ else
+ {
+ aFloatSize = AWTSize(pToolBox->CalcWindowSizePixel());
+ if ( !bIsHorizontal )
+ {
+ // Floating toolbars are always horizontal aligned! We have to swap
+ // width/height if we have a vertical aligned toolbar.
+ sal_Int32 nTemp = aFloatSize.Height;
+ aFloatSize.Height = aFloatSize.Width;
+ aFloatSize.Width = nTemp;
+ }
+
+ aDockingData.TrackingRectangle.Height = aFloatSize.Height;
+ aDockingData.TrackingRectangle.Width = aFloatSize.Width;
+
+ // For the first time we don't have any data about the floating size of a toolbar.
+ // We calculate it and store it for later use.
+ aUIDockingElement.m_aFloatingData.m_aPos = AWTPoint(pContainerWindow->ScreenToOutputPixel(VCLPoint(e.MousePos)));
+ aUIDockingElement.m_aFloatingData.m_aSize = aFloatSize;
+ aUIDockingElement.m_aFloatingData.m_nLines = pToolBox->GetFloatingLines();
+ aUIDockingElement.m_aFloatingData.m_bIsHorizontal = isToolboxHorizontalAligned( pToolBox );
+ }
+ aDockingData.TrackingRectangle.X = e.MousePos.X;
+ aDockingData.TrackingRectangle.Y = e.MousePos.Y;
+ }
+
+ aDockingData.bFloating = ( eDockingArea == ui::DockingArea(-1) );
+
+ // Write current data to the member docking progress data
+ SolarMutexGuard g;
+ m_aDockUIElement.m_bFloating = aDockingData.bFloating;
+ if ( !aDockingData.bFloating )
+ {
+ m_aDockUIElement.m_aDockedData = aUIDockingElement.m_aDockedData;
+
+ m_eDockOperation = eDockingOperation;
+ }
+ else
+ m_aDockUIElement.m_aFloatingData = aUIDockingElement.m_aFloatingData;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ return aDockingData;
+}
+
+void SAL_CALL ToolbarLayoutManager::endDocking( const awt::EndDockingEvent& e )
+{
+ if (e.bCancelled)
+ return;
+
+ bool bDockingInProgress( false );
+ bool bStartDockFloated( false );
+ bool bFloating( false );
+ UIElement aUIDockingElement;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexResettableGuard aWriteLock;
+ bDockingInProgress = m_bDockingInProgress;
+ aUIDockingElement = m_aDockUIElement;
+ bFloating = aUIDockingElement.m_bFloating;
+
+ UIElement& rUIElement = impl_findToolbar( aUIDockingElement.m_aName );
+ if ( rUIElement.m_aName == aUIDockingElement.m_aName )
+ {
+ if ( aUIDockingElement.m_bFloating )
+ {
+ // Write last position into position data
+ uno::Reference< awt::XWindow > xWindow( aUIDockingElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY );
+ rUIElement.m_aFloatingData = aUIDockingElement.m_aFloatingData;
+ awt::Rectangle aTmpRect = xWindow->getPosSize();
+ rUIElement.m_aFloatingData.m_aPos = awt::Point(aTmpRect.X, aTmpRect.Y);
+ // make changes also for our local data as we use it to make data persistent
+ aUIDockingElement.m_aFloatingData = rUIElement.m_aFloatingData;
+ }
+ else
+ {
+ rUIElement.m_aDockedData = aUIDockingElement.m_aDockedData;
+ rUIElement.m_aFloatingData.m_aSize = aUIDockingElement.m_aFloatingData.m_aSize;
+
+ if ( m_eDockOperation != DOCKOP_ON_COLROW )
+ {
+ // we have to renumber our row/column data to insert a new row/column
+ implts_renumberRowColumnData(aUIDockingElement.m_aDockedData.m_nDockedArea, aUIDockingElement );
+ }
+ }
+
+ bStartDockFloated = rUIElement.m_bFloating;
+ rUIElement.m_bFloating = m_aDockUIElement.m_bFloating;
+ rUIElement.m_bUserActive = true;
+ }
+
+ // reset member for next docking operation
+ m_aDockUIElement.m_xUIElement.clear();
+ m_eDockOperation = DOCKOP_ON_COLROW;
+ aWriteLock.clear();
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ implts_writeWindowStateData( aUIDockingElement );
+
+ if ( bDockingInProgress )
+ {
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( uno::Reference< awt::XWindow >( e.Source, uno::UNO_QUERY ));
+ ToolBox* pToolBox = nullptr;
+ if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX )
+ pToolBox = static_cast<ToolBox *>(pWindow.get());
+
+ if ( pToolBox )
+ {
+ if( e.bFloating )
+ {
+ if ( aUIDockingElement.m_aFloatingData.m_bIsHorizontal )
+ pToolBox->SetAlign();
+ else
+ pToolBox->SetAlign( WindowAlign::Left );
+ }
+ else
+ {
+ ::Size aSize;
+
+ pToolBox->SetAlign( ImplConvertAlignment( aUIDockingElement.m_aDockedData.m_nDockedArea) );
+
+ // Docked toolbars have always one line
+ aSize = pToolBox->CalcWindowSizePixel( 1 );
+
+ // Lock layouting updates as our listener would be called due to SetSizePixel
+ pToolBox->SetOutputSizePixel( aSize );
+ }
+ }
+ }
+
+ implts_sortUIElements();
+
+ aWriteLock.reset();
+ m_bDockingInProgress = false;
+ m_bLayoutDirty = !bStartDockFloated || !bFloating;
+ bool bNotify = m_bLayoutDirty;
+ aWriteLock.clear();
+
+ if ( bNotify )
+ m_pParentLayouter->requestLayout();
+}
+
+sal_Bool SAL_CALL ToolbarLayoutManager::prepareToggleFloatingMode( const lang::EventObject& e )
+{
+ SolarMutexClearableGuard aReadLock;
+ bool bDockingInProgress = m_bDockingInProgress;
+ aReadLock.clear();
+
+ UIElement aUIDockingElement = implts_findToolbar( e.Source );
+ bool bWinFound( !aUIDockingElement.m_aName.isEmpty() );
+ uno::Reference< awt::XWindow > xWindow( e.Source, uno::UNO_QUERY );
+
+ if ( !bWinFound || !xWindow.is() )
+ return true;
+
+ if ( bDockingInProgress )
+ return true;
+
+ uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY );
+ if ( !xDockWindow->isFloating() )
+ return true;
+
+ {
+ SolarMutexGuard aGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX )
+ {
+ ToolBox* pToolBox = static_cast< ToolBox *>( pWindow.get() );
+ aUIDockingElement.m_aFloatingData.m_aPos = AWTPoint(pToolBox->GetPosPixel());
+ aUIDockingElement.m_aFloatingData.m_aSize = AWTSize(pToolBox->GetOutputSizePixel());
+ aUIDockingElement.m_aFloatingData.m_nLines = pToolBox->GetFloatingLines();
+ aUIDockingElement.m_aFloatingData.m_bIsHorizontal = isToolboxHorizontalAligned( pToolBox );
+ }
+ }
+
+ UIElement aUIElement = implts_findToolbar( aUIDockingElement.m_aName );
+ if ( aUIElement.m_aName == aUIDockingElement.m_aName )
+ implts_setToolbar( aUIDockingElement );
+
+ return true;
+}
+
+void SAL_CALL ToolbarLayoutManager::toggleFloatingMode( const lang::EventObject& e )
+{
+ UIElement aUIDockingElement;
+
+ SolarMutexResettableGuard aReadLock;
+ bool bDockingInProgress( m_bDockingInProgress );
+ if ( bDockingInProgress )
+ aUIDockingElement = m_aDockUIElement;
+ aReadLock.clear();
+
+ vcl::Window* pWindow( nullptr );
+ ToolBox* pToolBox( nullptr );
+ uno::Reference< awt::XWindow2 > xWindow;
+
+ {
+ SolarMutexGuard aGuard;
+ xWindow.set( e.Source, uno::UNO_QUERY );
+ pWindow = VCLUnoHelper::GetWindow( xWindow );
+
+ if ( pWindow && pWindow->GetType() == WindowType::TOOLBOX )
+ pToolBox = static_cast<ToolBox *>(pWindow);
+ }
+
+ if ( !bDockingInProgress )
+ {
+ aUIDockingElement = implts_findToolbar( e.Source );
+ bool bWinFound = !aUIDockingElement.m_aName.isEmpty();
+
+ if ( bWinFound && xWindow.is() )
+ {
+ aUIDockingElement.m_bFloating = !aUIDockingElement.m_bFloating;
+ aUIDockingElement.m_bUserActive = true;
+
+ implts_setLayoutInProgress();
+ if ( aUIDockingElement.m_bFloating )
+ {
+ SolarMutexGuard aGuard;
+ if ( pToolBox )
+ {
+ pToolBox->SetLineCount( aUIDockingElement.m_aFloatingData.m_nLines );
+ if ( aUIDockingElement.m_aFloatingData.m_bIsHorizontal )
+ pToolBox->SetAlign();
+ else
+ pToolBox->SetAlign( WindowAlign::Left );
+ }
+
+ bool bUndefPos = hasDefaultPosValue( aUIDockingElement.m_aFloatingData.m_aPos );
+ bool bSetSize = !hasEmptySize( aUIDockingElement.m_aFloatingData.m_aSize );
+
+ if ( bUndefPos )
+ aUIDockingElement.m_aFloatingData.m_aPos = implts_findNextCascadeFloatingPos();
+
+ if ( !bSetSize )
+ {
+ if ( pToolBox )
+ aUIDockingElement.m_aFloatingData.m_aSize = AWTSize(pToolBox->CalcFloatingWindowSizePixel());
+ else if ( pWindow )
+ aUIDockingElement.m_aFloatingData.m_aSize = AWTSize(pWindow->GetOutputSizePixel());
+ }
+
+ xWindow->setPosSize( aUIDockingElement.m_aFloatingData.m_aPos.X,
+ aUIDockingElement.m_aFloatingData.m_aPos.Y,
+ 0, 0, awt::PosSize::POS );
+ xWindow->setOutputSize(aUIDockingElement.m_aFloatingData.m_aSize);
+ }
+ else
+ {
+ if ( isDefaultPos( aUIDockingElement.m_aDockedData.m_aPos ))
+ {
+ // Docking on its default position without a preset position -
+ // we have to find a good place for it.
+ ::Point aPixelPos;
+ awt::Point aDockPos;
+ ::Size aSize;
+
+ {
+ SolarMutexGuard aGuard;
+ if ( pToolBox )
+ aSize = pToolBox->CalcWindowSizePixel( 1, ImplConvertAlignment( aUIDockingElement.m_aDockedData.m_nDockedArea ) );
+ else if ( pWindow )
+ aSize = pWindow->GetSizePixel();
+ }
+
+ implts_findNextDockingPos(aUIDockingElement.m_aDockedData.m_nDockedArea, aSize, aDockPos, aPixelPos );
+ aUIDockingElement.m_aDockedData.m_aPos = aDockPos;
+ }
+
+ SolarMutexGuard aGuard;
+ if ( pToolBox )
+ {
+ pToolBox->SetAlign( ImplConvertAlignment( aUIDockingElement.m_aDockedData.m_nDockedArea) );
+ ::Size aSize = pToolBox->CalcWindowSizePixel( 1 );
+ awt::Rectangle aRect = xWindow->getPosSize();
+ xWindow->setPosSize( aRect.X, aRect.Y, 0, 0, awt::PosSize::POS );
+ xWindow->setOutputSize( AWTSize( aSize ) );
+ }
+ }
+
+ implts_setLayoutInProgress( false );
+ implts_setToolbar( aUIDockingElement );
+ implts_writeWindowStateData( aUIDockingElement );
+ implts_sortUIElements();
+ implts_setLayoutDirty();
+
+ aReadLock.reset();
+ LayoutManager* pParentLayouter( m_pParentLayouter );
+ aReadLock.clear();
+
+ if ( pParentLayouter )
+ pParentLayouter->requestLayout();
+ }
+ }
+ else
+ {
+ SolarMutexGuard aGuard;
+ if ( pToolBox )
+ {
+ if ( aUIDockingElement.m_bFloating )
+ {
+ if ( aUIDockingElement.m_aFloatingData.m_bIsHorizontal )
+ pToolBox->SetAlign();
+ else
+ pToolBox->SetAlign( WindowAlign::Left );
+ }
+ else
+ pToolBox->SetAlign( ImplConvertAlignment( aUIDockingElement.m_aDockedData.m_nDockedArea) );
+ }
+ }
+}
+
+void SAL_CALL ToolbarLayoutManager::closed( const lang::EventObject& e )
+{
+ OUString aName;
+ UIElement aUIElement;
+
+ {
+ SolarMutexGuard aWriteLock;
+ for (auto& elem : m_aUIElements)
+ {
+ uno::Reference<ui::XUIElement> xUIElement(elem.m_xUIElement);
+ if (xUIElement.is())
+ {
+ uno::Reference<uno::XInterface> xIfac(xUIElement->getRealInterface(),
+ uno::UNO_QUERY);
+ if (xIfac == e.Source)
+ {
+ aName = elem.m_aName;
+
+ // user closes a toolbar =>
+ // context sensitive toolbar: only destroy toolbar and store state.
+ // non context sensitive toolbar: make it invisible, store state and destroy it.
+ if (!elem.m_bContextSensitive)
+ elem.m_bVisible = false;
+
+ aUIElement = elem;
+ break;
+ }
+ }
+ }
+ }
+
+ // destroy element
+ if ( aName.isEmpty() )
+ return;
+
+ implts_writeWindowStateData( aUIElement );
+ destroyToolbar( aName );
+
+ SolarMutexClearableGuard aReadLock;
+ bool bLayoutDirty = m_bLayoutDirty;
+ LayoutManager* pParentLayouter( m_pParentLayouter );
+ aReadLock.clear();
+
+ if ( bLayoutDirty && pParentLayouter )
+ pParentLayouter->requestLayout();
+}
+
+void SAL_CALL ToolbarLayoutManager::endPopupMode( const awt::EndPopupModeEvent& /*e*/ )
+{
+}
+
+// XUIConfigurationListener
+
+void SAL_CALL ToolbarLayoutManager::elementInserted( const ui::ConfigurationEvent& rEvent )
+{
+ UIElement aUIElement = implts_findToolbar( rEvent.ResourceURL );
+
+ uno::Reference< ui::XUIElementSettings > xElementSettings( aUIElement.m_xUIElement, uno::UNO_QUERY );
+ if ( xElementSettings.is() )
+ {
+ uno::Reference< beans::XPropertySet > xPropSet( xElementSettings, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ if ( rEvent.Source == uno::Reference< uno::XInterface >( m_xDocCfgMgr, uno::UNO_QUERY ))
+ xPropSet->setPropertyValue( "ConfigurationSource", css::uno::Any( m_xDocCfgMgr ));
+ }
+ xElementSettings->updateSettings();
+ }
+ else
+ {
+ OUString aElementType;
+ OUString aElementName;
+ parseResourceURL( rEvent.ResourceURL, aElementType, aElementName );
+ if ( aElementName.indexOf( "custom_" ) != -1 )
+ {
+ // custom toolbar must be directly created, shown and layouted!
+ createToolbar( rEvent.ResourceURL );
+ uno::Reference< ui::XUIElement > xUIElement = getToolbar( rEvent.ResourceURL );
+ if ( xUIElement.is() )
+ {
+ OUString aUIName;
+ uno::Reference< ui::XUIConfigurationManager > xCfgMgr;
+ uno::Reference< beans::XPropertySet > xPropSet;
+
+ try
+ {
+ xCfgMgr.set( rEvent.Source, uno::UNO_QUERY );
+ xPropSet.set( xCfgMgr->getSettings( rEvent.ResourceURL, false ), uno::UNO_QUERY );
+
+ if ( xPropSet.is() )
+ xPropSet->getPropertyValue("UIName") >>= aUIName;
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ }
+ catch (const lang::WrappedTargetException&)
+ {
+ }
+
+ {
+ SolarMutexGuard aGuard;
+ vcl::Window* pWindow = getWindowFromXUIElement( xUIElement );
+ if ( pWindow )
+ pWindow->SetText( aUIName );
+ }
+
+ showToolbar( rEvent.ResourceURL );
+ }
+ }
+ }
+}
+
+void SAL_CALL ToolbarLayoutManager::elementRemoved( const ui::ConfigurationEvent& rEvent )
+{
+ SolarMutexClearableGuard aReadLock;
+ uno::Reference< awt::XWindow > xContainerWindow = m_xContainerWindow;
+ uno::Reference< ui::XUIConfigurationManager > xModuleCfgMgr( m_xModuleCfgMgr );
+ uno::Reference< ui::XUIConfigurationManager > xDocCfgMgr( m_xDocCfgMgr );
+ aReadLock.clear();
+
+ UIElement aUIElement = implts_findToolbar( rEvent.ResourceURL );
+ uno::Reference< ui::XUIElementSettings > xElementSettings( aUIElement.m_xUIElement, uno::UNO_QUERY );
+ if ( !xElementSettings.is() )
+ return;
+
+ bool bNoSettings( false );
+ OUString aConfigSourcePropName( "ConfigurationSource" );
+ uno::Reference< uno::XInterface > xElementCfgMgr;
+ uno::Reference< beans::XPropertySet > xPropSet( xElementSettings, uno::UNO_QUERY );
+
+ if ( xPropSet.is() )
+ xPropSet->getPropertyValue( aConfigSourcePropName ) >>= xElementCfgMgr;
+
+ if ( !xElementCfgMgr.is() )
+ return;
+
+ // Check if the same UI configuration manager has changed => check further
+ if ( rEvent.Source == xElementCfgMgr )
+ {
+ // Same UI configuration manager where our element has its settings
+ if ( rEvent.Source == uno::Reference< uno::XInterface >( xDocCfgMgr, uno::UNO_QUERY ))
+ {
+ // document settings removed
+ if ( xModuleCfgMgr->hasSettings( rEvent.ResourceURL ))
+ {
+ xPropSet->setPropertyValue( aConfigSourcePropName, css::uno::Any( xModuleCfgMgr ));
+ xElementSettings->updateSettings();
+ return;
+ }
+ }
+
+ bNoSettings = true;
+ }
+
+ // No settings anymore, element must be destroyed
+ if ( xContainerWindow.is() && bNoSettings )
+ destroyToolbar( rEvent.ResourceURL );
+}
+
+void SAL_CALL ToolbarLayoutManager::elementReplaced( const ui::ConfigurationEvent& rEvent )
+{
+ UIElement aUIElement = implts_findToolbar( rEvent.ResourceURL );
+
+ uno::Reference< ui::XUIElementSettings > xElementSettings( aUIElement.m_xUIElement, uno::UNO_QUERY );
+ if ( !xElementSettings.is() )
+ return;
+
+ uno::Reference< uno::XInterface > xElementCfgMgr;
+ uno::Reference< beans::XPropertySet > xPropSet( xElementSettings, uno::UNO_QUERY );
+
+ if ( xPropSet.is() )
+ xPropSet->getPropertyValue( "ConfigurationSource" ) >>= xElementCfgMgr;
+
+ if ( !xElementCfgMgr.is() )
+ return;
+
+ // Check if the same UI configuration manager has changed => update settings
+ if ( rEvent.Source != xElementCfgMgr )
+ return;
+
+ xElementSettings->updateSettings();
+
+ SolarMutexClearableGuard aWriteLock;
+ bool bNotify = !aUIElement.m_bFloating;
+ m_bLayoutDirty = bNotify;
+ LayoutManager* pParentLayouter( m_pParentLayouter );
+ aWriteLock.clear();
+
+ if ( bNotify && pParentLayouter )
+ pParentLayouter->requestLayout();
+}
+
+uno::Reference< ui::XUIElement > ToolbarLayoutManager::getToolbar( std::u16string_view aName )
+{
+ return implts_findToolbar( aName ).m_xUIElement;
+}
+
+uno::Sequence< uno::Reference< ui::XUIElement > > ToolbarLayoutManager::getToolbars()
+{
+ uno::Sequence< uno::Reference< ui::XUIElement > > aSeq;
+
+ SolarMutexGuard g;
+ if ( !m_aUIElements.empty() )
+ {
+ sal_uInt32 nCount(0);
+ for (auto const& elem : m_aUIElements)
+ {
+ if ( elem.m_xUIElement.is() )
+ {
+ ++nCount;
+ aSeq.realloc( nCount );
+ aSeq.getArray()[nCount-1] = elem.m_xUIElement;
+ }
+ }
+ }
+
+ return aSeq;
+}
+
+bool ToolbarLayoutManager::floatToolbar( std::u16string_view rResourceURL )
+{
+ UIElement aUIElement = implts_findToolbar( rResourceURL );
+ if ( !aUIElement.m_xUIElement.is() )
+ return false;
+
+ try
+ {
+ uno::Reference< awt::XDockableWindow > xDockWindow( aUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY );
+ if ( xDockWindow.is() && !xDockWindow->isFloating() )
+ {
+ aUIElement.m_bFloating = true;
+ implts_writeWindowStateData( aUIElement );
+ xDockWindow->setFloatingMode( true );
+
+ implts_setLayoutDirty();
+ implts_setToolbar( aUIElement );
+ return true;
+ }
+ }
+ catch (const lang::DisposedException&)
+ {
+ }
+
+ return false;
+}
+
+bool ToolbarLayoutManager::lockToolbar( std::u16string_view rResourceURL )
+{
+ UIElement aUIElement = implts_findToolbar( rResourceURL );
+ if ( !aUIElement.m_xUIElement.is() )
+ return false;
+
+ try
+ {
+ uno::Reference< awt::XDockableWindow > xDockWindow( aUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY );
+ if ( xDockWindow.is() && !xDockWindow->isFloating() && !xDockWindow->isLocked() )
+ {
+ aUIElement.m_aDockedData.m_bLocked = true;
+ implts_writeWindowStateData( aUIElement );
+ xDockWindow->lock();
+
+ implts_setLayoutDirty();
+ implts_setToolbar( aUIElement );
+ return true;
+ }
+ }
+ catch (const lang::DisposedException&)
+ {
+ }
+
+ return false;
+}
+
+bool ToolbarLayoutManager::unlockToolbar( std::u16string_view rResourceURL )
+{
+ UIElement aUIElement = implts_findToolbar( rResourceURL );
+ if ( !aUIElement.m_xUIElement.is() )
+ return false;
+
+ try
+ {
+ uno::Reference< awt::XDockableWindow > xDockWindow( aUIElement.m_xUIElement->getRealInterface(), uno::UNO_QUERY );
+ if ( xDockWindow.is() && !xDockWindow->isFloating() && xDockWindow->isLocked() )
+ {
+ aUIElement.m_aDockedData.m_bLocked = false;
+ implts_writeWindowStateData( aUIElement );
+ xDockWindow->unlock();
+
+ implts_setLayoutDirty();
+ implts_setToolbar( aUIElement );
+ return true;
+ }
+ }
+ catch (const lang::DisposedException&)
+ {
+ }
+
+ return false;
+}
+
+bool ToolbarLayoutManager::isToolbarVisible( std::u16string_view rResourceURL )
+{
+ uno::Reference< awt::XWindow2 > xWindow2( implts_getXWindow( rResourceURL ), uno::UNO_QUERY );
+ return ( xWindow2.is() && xWindow2->isVisible() );
+}
+
+bool ToolbarLayoutManager::isToolbarFloating( std::u16string_view rResourceURL )
+{
+ uno::Reference< awt::XDockableWindow > xDockWindow( implts_getXWindow( rResourceURL ), uno::UNO_QUERY );
+ return ( xDockWindow.is() && xDockWindow->isFloating() );
+}
+
+bool ToolbarLayoutManager::isToolbarDocked( std::u16string_view rResourceURL )
+{
+ return !isToolbarFloating( rResourceURL );
+}
+
+bool ToolbarLayoutManager::isToolbarLocked( std::u16string_view rResourceURL )
+{
+ uno::Reference< awt::XDockableWindow > xDockWindow( implts_getXWindow( rResourceURL ), uno::UNO_QUERY );
+ return ( xDockWindow.is() && xDockWindow->isLocked() );
+}
+
+awt::Size ToolbarLayoutManager::getToolbarSize( std::u16string_view rResourceURL )
+{
+ vcl::Window* pWindow = implts_getWindow( rResourceURL );
+
+ SolarMutexGuard aGuard;
+ if ( pWindow )
+ {
+ ::Size aSize = pWindow->GetSizePixel();
+ awt::Size aWinSize;
+ aWinSize.Width = aSize.Width();
+ aWinSize.Height = aSize.Height();
+ return aWinSize;
+ }
+
+ return awt::Size();
+}
+
+awt::Point ToolbarLayoutManager::getToolbarPos( std::u16string_view rResourceURL )
+{
+ awt::Point aPos;
+ UIElement aUIElement = implts_findToolbar( rResourceURL );
+
+ uno::Reference< awt::XWindow > xWindow( implts_getXWindow( rResourceURL ));
+ if ( xWindow.is() )
+ {
+ if ( aUIElement.m_bFloating )
+ {
+ awt::Rectangle aRect = xWindow->getPosSize();
+ aPos.X = aRect.X;
+ aPos.Y = aRect.Y;
+ }
+ else
+ aPos = aUIElement.m_aDockedData.m_aPos;
+ }
+
+ return aPos;
+}
+
+void ToolbarLayoutManager::setToolbarSize( std::u16string_view rResourceURL, const awt::Size& aSize )
+{
+ uno::Reference< awt::XWindow2 > xWindow( implts_getXWindow( rResourceURL ), uno::UNO_QUERY );
+ uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY );
+ UIElement aUIElement = implts_findToolbar( rResourceURL );
+
+ if ( xWindow.is() && xDockWindow.is() && xDockWindow->isFloating() )
+ {
+ xWindow->setOutputSize( aSize );
+ aUIElement.m_aFloatingData.m_aSize = aSize;
+ implts_setToolbar( aUIElement );
+ implts_writeWindowStateData( aUIElement );
+ implts_sortUIElements();
+ }
+}
+
+void ToolbarLayoutManager::setToolbarPos( std::u16string_view rResourceURL, const awt::Point& aPos )
+{
+ uno::Reference< awt::XWindow > xWindow( implts_getXWindow( rResourceURL ));
+ uno::Reference< awt::XDockableWindow > xDockWindow( xWindow, uno::UNO_QUERY );
+ UIElement aUIElement = implts_findToolbar( rResourceURL );
+
+ if ( xWindow.is() && xDockWindow.is() && xDockWindow->isFloating() )
+ {
+ xWindow->setPosSize( aPos.X, aPos.Y, 0, 0, awt::PosSize::POS );
+ aUIElement.m_aFloatingData.m_aPos = aPos;
+ implts_setToolbar( aUIElement );
+ implts_writeWindowStateData( aUIElement );
+ implts_sortUIElements();
+ }
+}
+
+void ToolbarLayoutManager::setToolbarPosSize( std::u16string_view rResourceURL, const awt::Point& aPos, const awt::Size& aSize )
+{
+ setToolbarPos( rResourceURL, aPos );
+ setToolbarSize( rResourceURL, aSize );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/layoutmanager/toolbarlayoutmanager.hxx b/framework/source/layoutmanager/toolbarlayoutmanager.hxx
new file mode 100644
index 000000000..3e7b631c6
--- /dev/null
+++ b/framework/source/layoutmanager/toolbarlayoutmanager.hxx
@@ -0,0 +1,283 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+#include <vector>
+
+#include <uiconfiguration/globalsettings.hxx>
+#include <framework/addonsoptions.hxx>
+#include <uielement/uielement.hxx>
+#include <services/layoutmanager.hxx>
+
+#include <com/sun/star/ui/XUIConfigurationManager.hpp>
+#include <com/sun/star/awt/XWindowListener.hpp>
+#include <com/sun/star/ui/XUIElementFactory.hpp>
+#include <com/sun/star/ui/DockingArea.hpp>
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/awt/XDockableWindow.hpp>
+#include <com/sun/star/awt/XDockableWindowListener.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+namespace framework
+{
+
+class ToolbarLayoutManager : public ::cppu::WeakImplHelper< css::awt::XDockableWindowListener,
+ css::ui::XUIConfigurationListener,
+ css::awt::XWindowListener >
+{
+ public:
+ enum { DOCKINGAREAS_COUNT = 4 };
+
+ enum PreviewFrameDetection
+ {
+ PREVIEWFRAME_UNKNOWN,
+ PREVIEWFRAME_NO,
+ PREVIEWFRAME_YES
+ };
+
+ ToolbarLayoutManager( css::uno::Reference< css::uno::XComponentContext > xContext,
+ css::uno::Reference< css::ui::XUIElementFactory > xUIElementFactory,
+ LayoutManager* pParentLayouter );
+ virtual ~ToolbarLayoutManager() override;
+
+ void reset();
+ void attach( const css::uno::Reference< css::frame::XFrame >& xFrame,
+ const css::uno::Reference< css::ui::XUIConfigurationManager >& xModuleCfgMgr,
+ const css::uno::Reference< css::ui::XUIConfigurationManager >& xDocCfgMgr,
+ const css::uno::Reference< css::container::XNameAccess >& xPersistentWindowState );
+
+ void setParentWindow( const css::uno::Reference< css::awt::XWindowPeer >& xParentWindow );
+ void setDockingAreaOffsets(const ::tools::Rectangle& rOffsets);
+
+ void resetDockingArea();
+
+ css::awt::Rectangle getDockingArea();
+ void setDockingArea( const css::awt::Rectangle& rDockingArea );
+
+ bool isPreviewFrame();
+
+ // layouting
+ bool isLayoutDirty() const { return m_bLayoutDirty;}
+ void doLayout(const ::Size& aContainerSize);
+
+ // creation/destruction
+ void createStaticToolbars();
+ void destroyToolbars();
+
+ bool requestToolbar( const OUString& rResourceURL );
+ bool createToolbar( const OUString& rResourceURL );
+ bool destroyToolbar( std::u16string_view rResourceURL );
+
+ // visibility
+ bool showToolbar( std::u16string_view rResourceURL );
+ bool hideToolbar( std::u16string_view rResourceURL );
+
+ void refreshToolbarsVisibility( bool bAutomaticToolbars );
+ void setFloatingToolbarsVisibility( bool bVisible );
+ void setVisible(bool bVisible);
+
+ // docking and further functions
+ bool dockToolbar( std::u16string_view rResourceURL, css::ui::DockingArea eDockingArea, const css::awt::Point& aPos );
+ bool dockAllToolbars();
+ bool floatToolbar( std::u16string_view rResourceURL );
+ bool lockToolbar( std::u16string_view rResourceURL );
+ bool unlockToolbar( std::u16string_view rResourceURL );
+ void setToolbarPos( std::u16string_view rResourceURL, const css::awt::Point& aPos );
+ void setToolbarSize( std::u16string_view rResourceURL, const css::awt::Size& aSize );
+ void setToolbarPosSize( std::u16string_view rResourceURL, const css::awt::Point& aPos, const css::awt::Size& aSize );
+ bool isToolbarVisible( std::u16string_view rResourceURL );
+ bool isToolbarFloating( std::u16string_view rResourceURL );
+ bool isToolbarDocked( std::u16string_view rResourceURL );
+ bool isToolbarLocked( std::u16string_view rResourceURL );
+ css::awt::Point getToolbarPos( std::u16string_view rResourceURL );
+ css::awt::Size getToolbarSize( std::u16string_view rResourceURL );
+ css::uno::Reference< css::ui::XUIElement > getToolbar( std::u16string_view aName );
+ css::uno::Sequence< css::uno::Reference< css::ui::XUIElement > > getToolbars();
+
+ // child window notifications
+ void childWindowEvent( VclSimpleEvent const * pEvent );
+
+ // XInterface
+
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override;
+
+ // XWindowListener
+ virtual void SAL_CALL windowResized( const css::awt::WindowEvent& aEvent ) override;
+ virtual void SAL_CALL windowMoved( const css::awt::WindowEvent& aEvent ) override;
+ virtual void SAL_CALL windowShown( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL windowHidden( const css::lang::EventObject& aEvent ) override;
+
+ // XDockableWindowListener
+ virtual void SAL_CALL startDocking( const css::awt::DockingEvent& e ) override;
+ virtual css::awt::DockingData SAL_CALL docking( const css::awt::DockingEvent& e ) override;
+ virtual void SAL_CALL endDocking( const css::awt::EndDockingEvent& e ) override;
+ virtual sal_Bool SAL_CALL prepareToggleFloatingMode( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL toggleFloatingMode( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL closed( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL endPopupMode( const css::awt::EndPopupModeEvent& e ) override;
+
+ // XUIConfigurationListener
+ virtual void SAL_CALL elementInserted( const css::ui::ConfigurationEvent& Event ) override;
+ virtual void SAL_CALL elementRemoved( const css::ui::ConfigurationEvent& Event ) override;
+ virtual void SAL_CALL elementReplaced( const css::ui::ConfigurationEvent& Event ) override;
+
+ private:
+ enum DockingOperation
+ {
+ DOCKOP_BEFORE_COLROW,
+ DOCKOP_ON_COLROW,
+ DOCKOP_AFTER_COLROW
+ };
+
+ typedef std::vector< UIElement > UIElementVector;
+ struct SingleRowColumnWindowData
+ {
+ SingleRowColumnWindowData()
+ : nVarSize(0)
+ , nStaticSize(0)
+ , nSpace(0)
+ , nRowColumn(0)
+ {}
+
+ std::vector< OUString > aUIElementNames;
+ std::vector< css::uno::Reference< css::awt::XWindow > > aRowColumnWindows;
+ std::vector< css::awt::Rectangle > aRowColumnWindowSizes;
+ std::vector< sal_Int32 > aRowColumnSpace;
+ css::awt::Rectangle aRowColumnRect;
+ sal_Int32 nVarSize;
+ sal_Int32 nStaticSize;
+ sal_Int32 nSpace;
+ sal_Int32 nRowColumn;
+ };
+
+ // internal helper methods
+
+ bool implts_isParentWindowVisible() const;
+ ::tools::Rectangle implts_calcDockingArea();
+ void implts_sortUIElements();
+ void implts_reparentToolbars();
+ OUString implts_generateGenericAddonToolbarTitle( sal_Int32 nNumber ) const;
+ void implts_setElementData( UIElement& rUIElement, const css::uno::Reference< css::awt::XDockableWindow >& rDockWindow );
+ void implts_destroyDockingAreaWindows();
+
+ // layout methods
+
+ void implts_setDockingAreaWindowSizes( const css::awt::Rectangle& rBorderSpace );
+ css::awt::Point implts_findNextCascadeFloatingPos();
+ void implts_renumberRowColumnData( css::ui::DockingArea eDockingArea, const UIElement& rUIElement );
+ void implts_calcWindowPosSizeOnSingleRowColumn( sal_Int32 nDockingArea,
+ sal_Int32 nOffset,
+ SingleRowColumnWindowData& rRowColumnWindowData,
+ const ::Size& rContainerSize );
+ void implts_setLayoutDirty();
+ void implts_setLayoutInProgress( bool bInProgress = true );
+
+ // lookup/container methods
+
+ UIElement implts_findToolbar( std::u16string_view aName );
+ UIElement implts_findToolbar( const css::uno::Reference< css::uno::XInterface >& xToolbar );
+ UIElement& impl_findToolbar( std::u16string_view aName );
+ css::uno::Reference< css::awt::XWindow > implts_getXWindow( std::u16string_view aName );
+ vcl::Window* implts_getWindow( std::u16string_view aName );
+ bool implts_insertToolbar( const UIElement& rUIElement );
+ void implts_setToolbar( const UIElement& rUIElement );
+ ::Size implts_getTopBottomDockingAreaSizes();
+ void implts_getUIElementVectorCopy( UIElementVector& rCopy );
+
+ // internal docking methods
+
+ ::tools::Rectangle implts_calcHotZoneRect( const ::tools::Rectangle& rRect, sal_Int32 nHotZoneOffset );
+ void implts_calcDockingPosSize( UIElement& aUIElement, DockingOperation& eDockOperation, ::tools::Rectangle& rTrackingRect, const Point& rMousePos );
+ DockingOperation implts_determineDockingOperation( css::ui::DockingArea DockingArea, const ::tools::Rectangle& rRowColRect, const Point& rMousePos );
+ ::tools::Rectangle implts_getWindowRectFromRowColumn( css::ui::DockingArea DockingArea, const SingleRowColumnWindowData& rRowColumnWindowData, const ::Point& rMousePos, std::u16string_view rExcludeElementName );
+ ::tools::Rectangle implts_determineFrontDockingRect( css::ui::DockingArea eDockingArea,
+ sal_Int32 nRowCol,
+ const ::tools::Rectangle& rDockedElementRect,
+ std::u16string_view rMovedElementName,
+ const ::tools::Rectangle& rMovedElementRect );
+ ::tools::Rectangle implts_calcTrackingAndElementRect( css::ui::DockingArea eDockingArea,
+ sal_Int32 nRowCol,
+ UIElement& rUIElement,
+ const ::tools::Rectangle& rTrackingRect,
+ const ::tools::Rectangle& rRowColumnRect,
+ const ::Size& rContainerWinSize );
+
+ void implts_getDockingAreaElementInfos( css::ui::DockingArea DockingArea, std::vector< SingleRowColumnWindowData >& rRowColumnsWindowData );
+ void implts_getDockingAreaElementInfoOnSingleRowCol( css::ui::DockingArea, sal_Int32 nRowCol, SingleRowColumnWindowData& rRowColumnWindowData );
+ void implts_findNextDockingPos( css::ui::DockingArea DockingArea, const ::Size& aUIElementSize, css::awt::Point& rVirtualPos, ::Point& rPixelPos );
+ void implts_setTrackingRect( css::ui::DockingArea eDockingArea, const ::Point& rMousePos, ::tools::Rectangle& rTrackingRect );
+
+ // creation methods
+
+ void implts_createAddonsToolBars();
+ void implts_createCustomToolBars();
+ void implts_createNonContextSensitiveToolBars();
+ void implts_createCustomToolBars( const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& aCustomTbxSeq );
+ void implts_createCustomToolBar( const OUString& aTbxResName, const OUString& aTitle );
+ void implts_setToolbarCreation( bool bStart = true );
+ bool implts_isToolbarCreationActive();
+
+ // persistence methods
+
+ bool implts_readWindowStateData( const OUString& aName, UIElement& rElementData );
+ void implts_writeWindowStateData( const UIElement& rElementData );
+
+ // members
+
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::frame::XFrame > m_xFrame;
+ css::uno::Reference< css::awt::XWindow2 > m_xContainerWindow;
+ css::uno::Reference< css::awt::XWindow > m_xDockAreaWindows[DOCKINGAREAS_COUNT];
+ css::uno::Reference< css::ui::XUIElementFactory > m_xUIElementFactoryManager;
+ css::uno::Reference< css::ui::XUIConfigurationManager > m_xModuleCfgMgr;
+ css::uno::Reference< css::ui::XUIConfigurationManager > m_xDocCfgMgr;
+ css::uno::Reference< css::container::XNameAccess > m_xPersistentWindowState;
+ LayoutManager* m_pParentLayouter;
+
+ UIElementVector m_aUIElements;
+ UIElement m_aDockUIElement;
+ tools::Rectangle m_aDockingArea;
+ tools::Rectangle m_aDockingAreaOffsets;
+ DockingOperation m_eDockOperation;
+ PreviewFrameDetection m_ePreviewDetection;
+
+ std::unique_ptr<AddonsOptions> m_pAddonOptions;
+ std::unique_ptr<GlobalSettings> m_pGlobalSettings;
+
+ bool m_bComponentAttached;
+ bool m_bLayoutDirty;
+ bool m_bGlobalSettings;
+ bool m_bDockingInProgress;
+ bool m_bLayoutInProgress;
+ bool m_bToolbarCreation;
+};
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/layoutmanager/uielement.cxx b/framework/source/layoutmanager/uielement.cxx
new file mode 100644
index 000000000..582187515
--- /dev/null
+++ b/framework/source/layoutmanager/uielement.cxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/uielement.hxx>
+
+#include <com/sun/star/ui/DockingArea.hpp>
+
+using namespace ::com::sun::star;
+
+namespace framework
+{
+
+ bool UIElement::operator< ( const ::framework::UIElement& aUIElement ) const
+{
+ if ( !m_xUIElement.is() && aUIElement.m_xUIElement.is() )
+ return false;
+ else if ( m_xUIElement.is() && !aUIElement.m_xUIElement.is() )
+ return true;
+ else if ( !m_bVisible && aUIElement.m_bVisible )
+ return false;
+ else if ( m_bVisible && !aUIElement.m_bVisible )
+ return true;
+ else if ( !m_bFloating && aUIElement.m_bFloating )
+ return true;
+ else if ( m_bFloating && !aUIElement.m_bFloating )
+ return false;
+ else
+ {
+ if ( m_bFloating )
+ {
+ bool bEqual = ( m_aFloatingData.m_aPos.Y == aUIElement.m_aFloatingData.m_aPos.Y );
+ if ( bEqual )
+ return ( m_aFloatingData.m_aPos.X < aUIElement.m_aFloatingData.m_aPos.X );
+ else
+ return ( m_aFloatingData.m_aPos.Y < aUIElement.m_aFloatingData.m_aPos.Y );
+ }
+ else
+ {
+ if ( m_aDockedData.m_nDockedArea < aUIElement.m_aDockedData.m_nDockedArea )
+ return true;
+ else if ( m_aDockedData.m_nDockedArea > aUIElement.m_aDockedData.m_nDockedArea )
+ return false;
+ else
+ {
+ if ( m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_TOP ||
+ m_aDockedData.m_nDockedArea == ui::DockingArea_DOCKINGAREA_BOTTOM )
+ {
+ if ( m_aDockedData.m_aPos.Y != aUIElement.m_aDockedData.m_aPos.Y )
+ return ( m_aDockedData.m_aPos.Y < aUIElement.m_aDockedData.m_aPos.Y );
+ else
+ {
+ bool bEqual = ( m_aDockedData.m_aPos.X == aUIElement.m_aDockedData.m_aPos.X );
+ if ( bEqual )
+ {
+ return m_bUserActive && !aUIElement.m_bUserActive;
+ }
+ else
+ return ( m_aDockedData.m_aPos.X <= aUIElement.m_aDockedData.m_aPos.X );
+ }
+ }
+ else
+ {
+ if ( m_aDockedData.m_aPos.X != aUIElement.m_aDockedData.m_aPos.X )
+ return ( m_aDockedData.m_aPos.X < aUIElement.m_aDockedData.m_aPos.X );
+ else
+ {
+ bool bEqual = ( m_aDockedData.m_aPos.Y == aUIElement.m_aDockedData.m_aPos.Y );
+ if ( bEqual )
+ {
+ return m_bUserActive && !aUIElement.m_bUserActive;
+ }
+ else
+ return ( m_aDockedData.m_aPos.Y <= aUIElement.m_aDockedData.m_aPos.Y );
+ }
+ }
+ }
+ }
+ }
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/loadenv/loadenv.cxx b/framework/source/loadenv/loadenv.cxx
new file mode 100644
index 000000000..76116ad98
--- /dev/null
+++ b/framework/source/loadenv/loadenv.cxx
@@ -0,0 +1,1808 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <loadenv/loadenv.hxx>
+
+#include <loadenv/loadenvexception.hxx>
+#include <loadenv/targethelper.hxx>
+#include <framework/framelistanalyzer.hxx>
+
+#include <interaction/quietinteraction.hxx>
+#include <properties.h>
+#include <protocols.h>
+#include <services.h>
+#include <targets.h>
+#include <comphelper/interaction.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <framework/interaction.hxx>
+#include <comphelper/processfactory.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Setup.hxx>
+
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <com/sun/star/document/XActionLockable.hpp>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/OfficeFrameLoader.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XFrameLoader.hpp>
+#include <com/sun/star/frame/XSynchronousFrameLoader.hpp>
+#include <com/sun/star/frame/XNotifyingDispatch.hpp>
+#include <com/sun/star/frame/FrameLoaderFactory.hpp>
+#include <com/sun/star/frame/ContentHandlerFactory.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/ErrorCodeRequest.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/task/XStatusIndicatorFactory.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+
+#include <utility>
+#include <vcl/window.hxx>
+#include <vcl/wrkwin.hxx>
+#include <vcl/syswin.hxx>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <svtools/sfxecode.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <comphelper/configurationhelper.hxx>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+#include <vcl/errcode.hxx>
+#include <vcl/svapp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/profilezone.hxx>
+#include <classes/taskcreator.hxx>
+#include <tools/fileutil.hxx>
+
+constexpr OUStringLiteral PROP_TYPES = u"Types";
+constexpr OUStringLiteral PROP_NAME = u"Name";
+
+namespace framework {
+
+using namespace com::sun::star;
+
+namespace {
+
+class LoadEnvListener : public ::cppu::WeakImplHelper< css::frame::XLoadEventListener ,
+ css::frame::XDispatchResultListener >
+{
+ private:
+ osl::Mutex m_mutex;
+ bool m_bWaitingResult;
+ LoadEnv* m_pLoadEnv;
+
+ public:
+
+ explicit LoadEnvListener(LoadEnv* pLoadEnv)
+ : m_bWaitingResult(true)
+ , m_pLoadEnv(pLoadEnv)
+ {
+ }
+
+ // frame.XLoadEventListener
+ virtual void SAL_CALL loadFinished(const css::uno::Reference< css::frame::XFrameLoader >& xLoader) override;
+
+ virtual void SAL_CALL loadCancelled(const css::uno::Reference< css::frame::XFrameLoader >& xLoader) override;
+
+ // frame.XDispatchResultListener
+ virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& aEvent) override;
+
+ // lang.XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override;
+};
+
+}
+
+LoadEnv::LoadEnv(css::uno::Reference< css::uno::XComponentContext > xContext)
+ : m_xContext(std::move(xContext))
+ , m_nSearchFlags(0)
+ , m_eFeature(LoadEnvFeatures::NONE)
+ , m_eContentType(E_UNSUPPORTED_CONTENT)
+ , m_bCloseFrameOnError(false)
+ , m_bReactivateControllerOnError(false)
+ , m_bLoaded( false )
+{
+}
+
+LoadEnv::~LoadEnv()
+{
+}
+
+css::uno::Reference< css::lang::XComponent > LoadEnv::loadComponentFromURL(const css::uno::Reference< css::frame::XComponentLoader >& xLoader,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext ,
+ const OUString& sURL ,
+ const OUString& sTarget,
+ sal_Int32 nSearchFlags ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArgs )
+{
+ css::uno::Reference< css::lang::XComponent > xComponent;
+ comphelper::ProfileZone aZone("loadComponentFromURL");
+
+ try
+ {
+ LoadEnv aEnv(xContext);
+
+ LoadEnvFeatures loadEnvFeatures = LoadEnvFeatures::WorkWithUI;
+ // tdf#118238 Only disable UI interaction when loading as hidden
+ if (comphelper::NamedValueCollection::get(lArgs, u"Hidden") == uno::Any(true) || Application::IsHeadlessModeEnabled())
+ loadEnvFeatures = LoadEnvFeatures::NONE;
+
+ aEnv.startLoading(sURL,
+ lArgs,
+ css::uno::Reference< css::frame::XFrame >(xLoader, css::uno::UNO_QUERY),
+ sTarget,
+ nSearchFlags,
+ loadEnvFeatures);
+ aEnv.waitWhileLoading(); // wait for ever!
+
+ xComponent = aEnv.getTargetComponent();
+ }
+ catch(const LoadEnvException& ex)
+ {
+ switch(ex.m_nID)
+ {
+ case LoadEnvException::ID_INVALID_MEDIADESCRIPTOR:
+ throw css::lang::IllegalArgumentException(
+ "Optional list of arguments seem to be corrupted.", xLoader, 4);
+
+ case LoadEnvException::ID_UNSUPPORTED_CONTENT:
+ throw css::lang::IllegalArgumentException(
+ "Unsupported URL <" + sURL + ">: \"" + ex.m_sMessage + "\"",
+ xLoader, 1);
+
+ default:
+ SAL_WARN(
+ "fwk.loadenv",
+ "caught LoadEnvException " << +ex.m_nID << " \""
+ << ex.m_sMessage << "\""
+ << (ex.m_exOriginal.has<css::uno::Exception>()
+ ? (", " + ex.m_exOriginal.getValueTypeName() + " \""
+ + (ex.m_exOriginal.get<css::uno::Exception>().
+ Message)
+ + "\"")
+ : OUString())
+ << " while loading <" << sURL << ">");
+ xComponent.clear();
+ break;
+ }
+ }
+
+ return xComponent;
+}
+
+namespace {
+
+utl::MediaDescriptor addModelArgs(const uno::Sequence<beans::PropertyValue>& rDescriptor)
+{
+ utl::MediaDescriptor rResult(rDescriptor);
+ uno::Reference<frame::XModel> xModel(rResult.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_MODEL, uno::Reference<frame::XModel>()));
+
+ if (xModel.is())
+ {
+ utl::MediaDescriptor aModelArgs(xModel->getArgs());
+ utl::MediaDescriptor::iterator pIt = aModelArgs.find( utl::MediaDescriptor::PROP_MACROEXECUTIONMODE);
+ if (pIt != aModelArgs.end())
+ rResult[utl::MediaDescriptor::PROP_MACROEXECUTIONMODE] = pIt->second;
+ }
+
+ return rResult;
+}
+
+}
+
+void LoadEnv::startLoading(const OUString& sURL, const uno::Sequence<beans::PropertyValue>& lMediaDescriptor,
+ const uno::Reference<frame::XFrame>& xBaseFrame, const OUString& sTarget,
+ sal_Int32 nSearchFlags, LoadEnvFeatures eFeature)
+{
+ osl::MutexGuard g(m_mutex);
+
+ // Handle still running processes!
+ if (m_xAsynchronousJob.is())
+ throw LoadEnvException(LoadEnvException::ID_STILL_RUNNING);
+
+ // take over all new parameters.
+ m_xTargetFrame.clear();
+ m_xBaseFrame = xBaseFrame;
+ m_lMediaDescriptor = addModelArgs(lMediaDescriptor);
+ m_sTarget = sTarget;
+ m_nSearchFlags = nSearchFlags;
+ m_eFeature = eFeature;
+ m_eContentType = E_UNSUPPORTED_CONTENT;
+ m_bCloseFrameOnError = false;
+ m_bReactivateControllerOnError = false;
+ m_bLoaded = false;
+
+ OUString aRealURL;
+ if (!officecfg::Office::Common::Load::DetectWebDAVRedirection::get()
+ || !tools::IsMappedWebDAVPath(sURL, &aRealURL))
+ aRealURL = sURL;
+
+ // try to find out, if it's really a content, which can be loaded or must be "handled"
+ // We use a default value for this in-parameter. Then we have to start a complex check method
+ // internally. But if this check was already done outside it can be suppressed to perform
+ // the load request. We take over the result then!
+ m_eContentType = LoadEnv::classifyContent(aRealURL, lMediaDescriptor);
+ if (m_eContentType == E_UNSUPPORTED_CONTENT)
+ throw LoadEnvException(LoadEnvException::ID_UNSUPPORTED_CONTENT, "from LoadEnv::startLoading");
+
+ // make URL part of the MediaDescriptor
+ // It doesn't matter if it is already an item of it.
+ // It must be the same value... so we can overwrite it :-)
+ m_lMediaDescriptor[utl::MediaDescriptor::PROP_URL] <<= aRealURL;
+
+ // parse it - because some following code require that
+ m_aURL.Complete = aRealURL;
+ uno::Reference<util::XURLTransformer> xParser(util::URLTransformer::create(m_xContext));
+ xParser->parseStrict(m_aURL);
+
+ // BTW: Split URL and JumpMark ...
+ // Because such mark is an explicit value of the media descriptor!
+ if (!m_aURL.Mark.isEmpty())
+ m_lMediaDescriptor[utl::MediaDescriptor::PROP_JUMPMARK] <<= m_aURL.Mark;
+
+ // By the way: remove the old and deprecated value "FileName" from the descriptor!
+ utl::MediaDescriptor::iterator pIt = m_lMediaDescriptor.find(utl::MediaDescriptor::PROP_FILENAME);
+ if (pIt != m_lMediaDescriptor.end())
+ m_lMediaDescriptor.erase(pIt);
+
+ // patch the MediaDescriptor, so it fulfil the outside requirements
+ // Means especially items like e.g. UI InteractionHandler, Status Indicator,
+ // MacroExecutionMode, etc.
+
+ /*TODO progress is bound to a frame ... How can we set it here? */
+
+ // UI mode
+ const bool bUIMode =
+ (m_eFeature & LoadEnvFeatures::WorkWithUI) &&
+ !m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false) &&
+ !m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW, false);
+
+ initializeUIDefaults(m_xContext, m_lMediaDescriptor, bUIMode, &m_pQuietInteraction);
+
+ start();
+}
+
+void LoadEnv::initializeUIDefaults( const css::uno::Reference< css::uno::XComponentContext >& i_rxContext,
+ utl::MediaDescriptor& io_lMediaDescriptor, const bool i_bUIMode,
+ rtl::Reference<QuietInteraction>* o_ppQuietInteraction )
+{
+ css::uno::Reference< css::task::XInteractionHandler > xInteractionHandler;
+ sal_Int16 nMacroMode;
+ sal_Int16 nUpdateMode;
+
+ if ( i_bUIMode )
+ {
+ nMacroMode = css::document::MacroExecMode::USE_CONFIG;
+ nUpdateMode = css::document::UpdateDocMode::ACCORDING_TO_CONFIG;
+ try
+ {
+ xInteractionHandler.set( css::task::InteractionHandler::createWithParent( i_rxContext, nullptr ), css::uno::UNO_QUERY_THROW );
+ }
+ catch(const css::uno::RuntimeException&) {throw;}
+ catch(const css::uno::Exception& ) { }
+ }
+ // hidden mode
+ else
+ {
+ nMacroMode = css::document::MacroExecMode::NEVER_EXECUTE;
+ nUpdateMode = css::document::UpdateDocMode::NO_UPDATE;
+ rtl::Reference<QuietInteraction> pQuietInteraction = new QuietInteraction();
+ xInteractionHandler = pQuietInteraction.get();
+ if ( o_ppQuietInteraction != nullptr )
+ {
+ *o_ppQuietInteraction = pQuietInteraction;
+ }
+ }
+
+ if ( xInteractionHandler.is() )
+ {
+ if( io_lMediaDescriptor.find(utl::MediaDescriptor::PROP_INTERACTIONHANDLER) == io_lMediaDescriptor.end() )
+ {
+ io_lMediaDescriptor[utl::MediaDescriptor::PROP_INTERACTIONHANDLER] <<= xInteractionHandler;
+ }
+ if( io_lMediaDescriptor.find(utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER) == io_lMediaDescriptor.end() )
+ {
+ io_lMediaDescriptor[utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER] <<= xInteractionHandler;
+ }
+ }
+
+ if (io_lMediaDescriptor.find(utl::MediaDescriptor::PROP_MACROEXECUTIONMODE) == io_lMediaDescriptor.end())
+ io_lMediaDescriptor[utl::MediaDescriptor::PROP_MACROEXECUTIONMODE] <<= nMacroMode;
+
+ if (io_lMediaDescriptor.find(utl::MediaDescriptor::PROP_UPDATEDOCMODE) == io_lMediaDescriptor.end())
+ io_lMediaDescriptor[utl::MediaDescriptor::PROP_UPDATEDOCMODE] <<= nUpdateMode;
+}
+
+void LoadEnv::start()
+{
+ // SAFE ->
+ {
+ osl::MutexGuard aReadLock(m_mutex);
+
+ // Handle still running processes!
+ if (m_xAsynchronousJob.is())
+ throw LoadEnvException(LoadEnvException::ID_STILL_RUNNING);
+
+ // content can not be loaded or handled
+ // check "classifyContent()" failed before ...
+ if (m_eContentType == E_UNSUPPORTED_CONTENT)
+ throw LoadEnvException(LoadEnvException::ID_UNSUPPORTED_CONTENT,
+ "from LoadEnv::start");
+ }
+ // <- SAFE
+
+ // detect its type/filter etc.
+ // This information will be available by the
+ // used descriptor member afterwards and is needed
+ // for all following operations!
+ // Note: An exception will be thrown, in case operation was not successfully ...
+ if (m_eContentType != E_CAN_BE_SET)/* Attention: special feature to set existing component on a frame must ignore type detection! */
+ impl_detectTypeAndFilter();
+
+ // start loading the content...
+ // Attention: Don't check m_eContentType deeper then UNSUPPORTED/SUPPORTED!
+ // Because it was made in the easiest way... may a flat detection was made only.
+ // And such simple detection can fail sometimes .-)
+ // Use another strategy here. Try it and let it run into the case "loading not possible".
+ bool bStarted = false;
+ if (
+ (m_eFeature & LoadEnvFeatures::AllowContentHandler) &&
+ (m_eContentType != E_CAN_BE_SET ) /* Attention: special feature to set existing component on a frame must ignore type detection! */
+ )
+ {
+ bStarted = impl_handleContent();
+ }
+
+ if (!bStarted)
+ bStarted = impl_loadContent();
+
+ // not started => general error
+ // We can't say - what was the reason for.
+ if (!bStarted)
+ throw LoadEnvException(
+ LoadEnvException::ID_GENERAL_ERROR, "not started");
+}
+
+/*-----------------------------------------------
+ TODO
+ First draft does not implement timeout using [ms].
+ Current implementation counts yield calls only ...
+-----------------------------------------------*/
+bool LoadEnv::waitWhileLoading(sal_uInt32 nTimeout)
+{
+ // Because it's not a good idea to block the main thread
+ // (and we can't be sure that we are currently not used inside the
+ // main thread!), we can't use conditions here really. We must yield
+ // in an intelligent manner :-)
+
+ sal_Int32 nTime = nTimeout;
+ while(!Application::IsQuit())
+ {
+ // SAFE -> ------------------------------
+ {
+ osl::MutexGuard aReadLock1(m_mutex);
+ if (!m_xAsynchronousJob.is())
+ break;
+ }
+ // <- SAFE ------------------------------
+
+ Application::Yield();
+
+ // forever!
+ if (nTimeout==0)
+ continue;
+
+ // timed out?
+ --nTime;
+ if (nTime<1)
+ break;
+ }
+
+ osl::MutexGuard g(m_mutex);
+ return !m_xAsynchronousJob.is();
+}
+
+css::uno::Reference< css::lang::XComponent > LoadEnv::getTargetComponent() const
+{
+ osl::MutexGuard g(m_mutex);
+
+ if (!m_xTargetFrame.is())
+ return css::uno::Reference< css::lang::XComponent >();
+
+ css::uno::Reference< css::frame::XController > xController = m_xTargetFrame->getController();
+ if (!xController.is())
+ return m_xTargetFrame->getComponentWindow();
+
+ css::uno::Reference< css::frame::XModel > xModel = xController->getModel();
+ if (!xModel.is())
+ return xController;
+
+ return xModel;
+}
+
+void SAL_CALL LoadEnvListener::loadFinished(const css::uno::Reference< css::frame::XFrameLoader >&)
+{
+ osl::MutexGuard g(m_mutex);
+ if (m_bWaitingResult)
+ m_pLoadEnv->impl_setResult(true);
+ m_bWaitingResult = false;
+}
+
+void SAL_CALL LoadEnvListener::loadCancelled(const css::uno::Reference< css::frame::XFrameLoader >&)
+{
+ osl::MutexGuard g(m_mutex);
+ if (m_bWaitingResult)
+ m_pLoadEnv->impl_setResult(false);
+ m_bWaitingResult = false;
+}
+
+void SAL_CALL LoadEnvListener::dispatchFinished(const css::frame::DispatchResultEvent& aEvent)
+{
+ osl::MutexGuard g(m_mutex);
+
+ if (!m_bWaitingResult)
+ return;
+
+ switch(aEvent.State)
+ {
+ case css::frame::DispatchResultState::FAILURE :
+ m_pLoadEnv->impl_setResult(false);
+ break;
+
+ case css::frame::DispatchResultState::SUCCESS :
+ m_pLoadEnv->impl_setResult(false);
+ break;
+
+ case css::frame::DispatchResultState::DONTKNOW :
+ m_pLoadEnv->impl_setResult(false);
+ break;
+ }
+ m_bWaitingResult = false;
+}
+
+void SAL_CALL LoadEnvListener::disposing(const css::lang::EventObject&)
+{
+ osl::MutexGuard g(m_mutex);
+ if (m_bWaitingResult)
+ m_pLoadEnv->impl_setResult(false);
+ m_bWaitingResult = false;
+}
+
+void LoadEnv::impl_setResult(bool bResult)
+{
+ osl::MutexGuard g(m_mutex);
+
+ m_bLoaded = bResult;
+
+ impl_reactForLoadingState();
+
+ // clearing of this reference will unblock waitWhileLoading()!
+ // So we must be sure, that loading process was really finished.
+ // => do it as last operation of this method ...
+ m_xAsynchronousJob.clear();
+}
+
+/*-----------------------------------------------
+ TODO: Is it a good idea to change Sequence<>
+ parameter to stl-adapter?
+-----------------------------------------------*/
+LoadEnv::EContentType LoadEnv::classifyContent(const OUString& sURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lMediaDescriptor)
+{
+
+ // (i) Filter some special well known URL protocols,
+ // which can not be handled or loaded in general.
+ // Of course an empty URL must be ignored here too.
+ // Note: These URL schemata are fix and well known ...
+ // But there can be some additional ones, which was not
+ // defined at implementation time of this class :-(
+ // So we have to make sure, that the following code
+ // can detect such protocol schemata too :-)
+
+ if(
+ (sURL.isEmpty() ) ||
+ (ProtocolCheck::isProtocol(sURL,EProtocol::Uno )) ||
+ (ProtocolCheck::isProtocol(sURL,EProtocol::Slot )) ||
+ (ProtocolCheck::isProtocol(sURL,EProtocol::Macro )) ||
+ (ProtocolCheck::isProtocol(sURL,EProtocol::Service)) ||
+ (ProtocolCheck::isProtocol(sURL,EProtocol::MailTo )) ||
+ (ProtocolCheck::isProtocol(sURL,EProtocol::News ))
+ )
+ {
+ return E_UNSUPPORTED_CONTENT;
+ }
+
+ // (ii) Some special URLs indicates a given input stream,
+ // a full featured document model directly or
+ // specify a request for opening an empty document.
+ // Such contents are loadable in general.
+ // But we have to check, if the media descriptor contains
+ // all needed resources. If they are missing - the following
+ // load request will fail.
+
+ /* Attention: The following code can't work on such special URLs!
+ It should not break the office... but it makes no sense
+ to start expensive object creations and complex search
+ algorithm if it's clear, that such URLs must be handled
+ in a special way .-)
+ */
+
+ // creation of new documents
+ if (ProtocolCheck::isProtocol(sURL,EProtocol::PrivateFactory))
+ return E_CAN_BE_LOADED;
+
+ // using of an existing input stream
+ utl::MediaDescriptor stlMediaDescriptor(lMediaDescriptor);
+ utl::MediaDescriptor::const_iterator pIt;
+ if (ProtocolCheck::isProtocol(sURL,EProtocol::PrivateStream))
+ {
+ pIt = stlMediaDescriptor.find(utl::MediaDescriptor::PROP_INPUTSTREAM);
+ css::uno::Reference< css::io::XInputStream > xStream;
+ if (pIt != stlMediaDescriptor.end())
+ pIt->second >>= xStream;
+ if (xStream.is())
+ return E_CAN_BE_LOADED;
+ SAL_INFO("fwk.loadenv", "LoadEnv::classifyContent(): loading from stream with right URL but invalid stream detected");
+ return E_UNSUPPORTED_CONTENT;
+ }
+
+ // using of a full featured document
+ if (ProtocolCheck::isProtocol(sURL,EProtocol::PrivateObject))
+ {
+ pIt = stlMediaDescriptor.find(utl::MediaDescriptor::PROP_MODEL);
+ css::uno::Reference< css::frame::XModel > xModel;
+ if (pIt != stlMediaDescriptor.end())
+ pIt->second >>= xModel;
+ if (xModel.is())
+ return E_CAN_BE_SET;
+ SAL_INFO("fwk.loadenv", "LoadEnv::classifyContent(): loading with object with right URL but invalid object detected");
+ return E_UNSUPPORTED_CONTENT;
+ }
+
+ // following operations can work on an internal type name only :-(
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ css::uno::Reference< css::document::XTypeDetection > xDetect(
+ xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.document.TypeDetection", xContext),
+ css::uno::UNO_QUERY_THROW);
+
+ OUString sType = xDetect->queryTypeByURL(sURL);
+
+ css::uno::Reference< css::frame::XLoaderFactory > xLoaderFactory;
+ css::uno::Reference< css::container::XEnumeration > xSet;
+
+ // (iii) If a FrameLoader service (or at least
+ // a Filter) can be found, which supports
+ // this URL - it must be a loadable content.
+ // Because both items are registered for types
+ // it's enough to check for frame loaders only.
+ // Most of our filters are handled by our global
+ // default loader. But there exist some specialized
+ // loader, which does not work on top of filters!
+ // So it's not enough to search on the filter configuration.
+ // Further it's not enough to search for types!
+ // Because there exist some types, which are referenced by
+ // other objects... but neither by filters nor frame loaders!
+ css::uno::Sequence< OUString > lTypesReg { sType };
+ css::uno::Sequence< css::beans::NamedValue > lQuery
+ {
+ css::beans::NamedValue(PROP_TYPES, css::uno::Any(lTypesReg))
+ };
+
+ xLoaderFactory = css::frame::FrameLoaderFactory::create(xContext);
+ xSet = xLoaderFactory->createSubSetEnumerationByProperties(lQuery);
+ // at least one registered frame loader is enough!
+ if (xSet->hasMoreElements())
+ return E_CAN_BE_LOADED;
+
+ // (iv) Some URL protocols are supported by special services.
+ // E.g. ContentHandler.
+ // Such contents can be handled ... but not loaded.
+
+ xLoaderFactory = css::frame::ContentHandlerFactory::create(xContext);
+ xSet = xLoaderFactory->createSubSetEnumerationByProperties(lQuery);
+ // at least one registered content handler is enough!
+ if (xSet->hasMoreElements())
+ return E_CAN_BE_HANDLED;
+
+ // (v) Last but not least the UCB is used inside office to
+ // load contents. He has a special configuration to know
+ // which URL schemata can be used inside office.
+ css::uno::Reference< css::ucb::XUniversalContentBroker > xUCB(css::ucb::UniversalContentBroker::create(xContext));
+ if (xUCB->queryContentProvider(sURL).is())
+ return E_CAN_BE_LOADED;
+
+ // (TODO) At this point, we have no idea .-)
+ // But it seems to be better, to break all
+ // further requests for this URL. Otherwise
+ // we can run into some trouble.
+ return E_UNSUPPORTED_CONTENT;
+}
+
+namespace {
+
+bool queryOrcusTypeAndFilter(const uno::Sequence<beans::PropertyValue>& rDescriptor, OUString& rType, OUString& rFilter)
+{
+ OUString aURL;
+ sal_Int32 nSize = rDescriptor.getLength();
+ for (sal_Int32 i = 0; i < nSize; ++i)
+ {
+ const beans::PropertyValue& rProp = rDescriptor[i];
+ if (rProp.Name == "URL")
+ {
+ rProp.Value >>= aURL;
+ break;
+ }
+ }
+
+ if (aURL.isEmpty() || o3tl::equalsIgnoreAsciiCase(aURL.subView(0,8), u"private:"))
+ return false;
+
+ // TODO : Type must be set to be generic_Text (or any other type that
+ // exists) in order to find a usable loader. Exploit it as a temporary
+ // hack.
+
+ // depending on the experimental mode
+ if (!officecfg::Office::Common::Misc::ExperimentalMode::get())
+ {
+ return false;
+ }
+
+ OUString aUseOrcus;
+ rtl::Bootstrap::get("LIBO_USE_ORCUS", aUseOrcus);
+ bool bUseOrcus = (aUseOrcus == "YES");
+
+ if (!bUseOrcus)
+ return false;
+
+ if (aURL.endsWith(".xlsx"))
+ {
+ rType = "generic_Text";
+ rFilter = "xlsx";
+ return true;
+ }
+ else if (aURL.endsWith(".ods"))
+ {
+ rType = "generic_Text";
+ rFilter = "ods";
+ return true;
+ }
+ else if (aURL.endsWith(".csv"))
+ {
+ rType = "generic_Text";
+ rFilter = "csv";
+ return true;
+ }
+
+ return false;
+}
+
+}
+
+void LoadEnv::impl_detectTypeAndFilter()
+{
+ static const sal_Int32 FILTERFLAG_TEMPLATEPATH = 16;
+
+ // SAFE ->
+ osl::ClearableMutexGuard aReadLock(m_mutex);
+
+ // Attention: Because our stl media descriptor is a copy of a uno sequence
+ // we can't use as an in/out parameter here. Copy it before and don't forget to
+ // update structure afterwards again!
+ css::uno::Sequence< css::beans::PropertyValue > lDescriptor = m_lMediaDescriptor.getAsConstPropertyValueList();
+ css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext;
+
+ aReadLock.clear();
+ // <- SAFE
+
+ OUString sType, sFilter;
+
+ if (queryOrcusTypeAndFilter(lDescriptor, sType, sFilter) && !sType.isEmpty() && !sFilter.isEmpty())
+ {
+ // SAFE ->
+ osl::MutexGuard aWriteLock(m_mutex);
+
+ // Orcus type detected. Skip the normal type detection process.
+ m_lMediaDescriptor << lDescriptor;
+ m_lMediaDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sType;
+ m_lMediaDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter;
+ m_lMediaDescriptor[utl::MediaDescriptor::PROP_FILTERPROVIDER] <<= OUString("orcus");
+ m_lMediaDescriptor[utl::MediaDescriptor::PROP_DOCUMENTSERVICE] <<= OUString("com.sun.star.sheet.SpreadsheetDocument");
+ return;
+ // <- SAFE
+ }
+
+ css::uno::Reference< css::document::XTypeDetection > xDetect(
+ xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.document.TypeDetection", xContext),
+ css::uno::UNO_QUERY_THROW);
+ sType = xDetect->queryTypeByDescriptor(lDescriptor, true); /*TODO should deep detection be able for enable/disable it from outside? */
+
+ // no valid content -> loading not possible
+ if (sType.isEmpty())
+ throw LoadEnvException(
+ LoadEnvException::ID_UNSUPPORTED_CONTENT, "type detection failed");
+
+ // SAFE ->
+ osl::ResettableMutexGuard aWriteLock(m_mutex);
+
+ // detection was successful => update the descriptor member of this class
+ m_lMediaDescriptor << lDescriptor;
+ m_lMediaDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sType;
+ // Is there an already detected (may be preselected) filter?
+ // see below ...
+ sFilter = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME, OUString());
+
+ aWriteLock.clear();
+ // <- SAFE
+
+ // We do have potentially correct type, but the detection process was aborted.
+ if (m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ABORTED, false))
+ throw LoadEnvException(
+ LoadEnvException::ID_UNSUPPORTED_CONTENT, "type detection aborted");
+
+ // But the type isn't enough. For loading sometimes we need more information.
+ // E.g. for our "_default" feature, where we recycle any frame which contains
+ // and "Untitled" document, we must know if the new document is based on a template!
+ // But this information is available as a filter property only.
+ // => We must try(!) to detect the right filter for this load request.
+ // On the other side ... if no filter is available .. ignore it.
+ // Then the type information must be enough.
+ if (sFilter.isEmpty())
+ {
+ // no -> try to find a preferred filter for the detected type.
+ // Don't forget to update the media descriptor.
+ css::uno::Reference< css::container::XNameAccess > xTypeCont(xDetect, css::uno::UNO_QUERY_THROW);
+ try
+ {
+ ::comphelper::SequenceAsHashMap lTypeProps(xTypeCont->getByName(sType));
+ sFilter = lTypeProps.getUnpackedValueOrDefault("PreferredFilter", OUString());
+ if (!sFilter.isEmpty())
+ {
+ // SAFE ->
+ aWriteLock.reset();
+ m_lMediaDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= sFilter;
+ aWriteLock.clear();
+ // <- SAFE
+ }
+ }
+ catch(const css::container::NoSuchElementException&)
+ {}
+ }
+
+ // check if the filter (if one exists) points to a template format filter.
+ // Then we have to add the property "AsTemplate".
+ // We need this information to decide afterwards if we can use a "recycle frame"
+ // for target "_default" or has to create a new one every time.
+ // On the other side we have to suppress that, if this property already exists
+ // and should trigger a special handling. Then the outside call of this method here,
+ // has to know, what he is doing .-)
+
+ bool bIsOwnTemplate = false;
+ if (!sFilter.isEmpty())
+ {
+ css::uno::Reference< css::container::XNameAccess > xFilterCont(xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_FILTERFACTORY, xContext), css::uno::UNO_QUERY_THROW);
+ try
+ {
+ ::comphelper::SequenceAsHashMap lFilterProps(xFilterCont->getByName(sFilter));
+ sal_Int32 nFlags = lFilterProps.getUnpackedValueOrDefault("Flags", sal_Int32(0));
+ bIsOwnTemplate = ((nFlags & FILTERFLAG_TEMPLATEPATH) == FILTERFLAG_TEMPLATEPATH);
+ }
+ catch(const css::container::NoSuchElementException&)
+ {}
+ }
+ if (bIsOwnTemplate)
+ {
+ // SAFE ->
+ aWriteLock.reset();
+ // Don't overwrite external decisions! See comments before ...
+ utl::MediaDescriptor::const_iterator pAsTemplateItem = m_lMediaDescriptor.find(utl::MediaDescriptor::PROP_ASTEMPLATE);
+ if (pAsTemplateItem == m_lMediaDescriptor.end())
+ m_lMediaDescriptor[utl::MediaDescriptor::PROP_ASTEMPLATE] <<= true;
+ aWriteLock.clear();
+ // <- SAFE
+ }
+}
+
+bool LoadEnv::impl_handleContent()
+{
+ // SAFE -> -----------------------------------
+ osl::ClearableMutexGuard aReadLock(m_mutex);
+
+ // the type must exist inside the descriptor ... otherwise this class is implemented wrong :-)
+ OUString sType = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_TYPENAME, OUString());
+ if (sType.isEmpty())
+ throw LoadEnvException(LoadEnvException::ID_INVALID_MEDIADESCRIPTOR);
+
+ // convert media descriptor and URL to right format for later interface call!
+ css::uno::Sequence< css::beans::PropertyValue > lDescriptor;
+ m_lMediaDescriptor >> lDescriptor;
+ css::util::URL aURL = m_aURL;
+
+ // get necessary container to query for a handler object
+ css::uno::Reference< css::frame::XLoaderFactory > xLoaderFactory = css::frame::ContentHandlerFactory::create(m_xContext);
+
+ aReadLock.clear();
+ // <- SAFE -----------------------------------
+
+ // query
+ css::uno::Sequence< OUString > lTypeReg { sType };
+
+ css::uno::Sequence< css::beans::NamedValue > lQuery { { PROP_TYPES, css::uno::Any(lTypeReg) } };
+
+ css::uno::Reference< css::container::XEnumeration > xSet = xLoaderFactory->createSubSetEnumerationByProperties(lQuery);
+ while(xSet->hasMoreElements())
+ {
+ ::comphelper::SequenceAsHashMap lProps (xSet->nextElement());
+ OUString sHandler = lProps.getUnpackedValueOrDefault(PROP_NAME, OUString());
+
+ css::uno::Reference< css::frame::XNotifyingDispatch > xHandler;
+ try
+ {
+ xHandler.set(xLoaderFactory->createInstance(sHandler), css::uno::UNO_QUERY);
+ if (!xHandler.is())
+ continue;
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { continue; }
+
+ // SAFE -> -----------------------------------
+ osl::ClearableMutexGuard aWriteLock(m_mutex);
+ m_xAsynchronousJob = xHandler;
+ rtl::Reference<LoadEnvListener> xListener = new LoadEnvListener(this);
+ aWriteLock.clear();
+ // <- SAFE -----------------------------------
+
+ xHandler->dispatchWithNotification(aURL, lDescriptor, xListener);
+
+ return true;
+ }
+
+ return false;
+}
+
+bool LoadEnv::impl_furtherDocsAllowed()
+{
+ // SAFE ->
+ osl::ResettableMutexGuard aReadLock(m_mutex);
+ css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext;
+ aReadLock.clear();
+ // <- SAFE
+
+ bool bAllowed = true;
+
+ try
+ {
+ std::optional<sal_Int32> x(officecfg::Office::Common::Misc::MaxOpenDocuments::get());
+
+ // NIL means: count of allowed documents = infinite !
+ // => return true
+ if ( !x)
+ bAllowed = true;
+ else
+ {
+ sal_Int32 nMaxOpenDocuments(*x);
+
+ css::uno::Reference< css::frame::XFramesSupplier > xDesktop(
+ css::frame::Desktop::create(xContext),
+ css::uno::UNO_QUERY_THROW);
+
+ FrameListAnalyzer aAnalyzer(xDesktop,
+ css::uno::Reference< css::frame::XFrame >(),
+ FrameAnalyzerFlags::Help |
+ FrameAnalyzerFlags::BackingComponent |
+ FrameAnalyzerFlags::Hidden);
+
+ sal_Int32 nOpenDocuments = aAnalyzer.m_lOtherVisibleFrames.size();
+ bAllowed = (nOpenDocuments < nMaxOpenDocuments);
+ }
+ }
+ catch(const css::uno::Exception&)
+ { bAllowed = true; } // !! internal errors are no reason to disturb the office from opening documents .-)
+
+ if ( ! bAllowed )
+ {
+ // SAFE ->
+ aReadLock.reset();
+ css::uno::Reference< css::task::XInteractionHandler > xInteraction = m_lMediaDescriptor.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_INTERACTIONHANDLER,
+ css::uno::Reference< css::task::XInteractionHandler >());
+ aReadLock.clear();
+ // <- SAFE
+
+ if (xInteraction.is())
+ {
+ css::uno::Any aInteraction;
+
+ rtl::Reference<comphelper::OInteractionAbort> pAbort = new comphelper::OInteractionAbort();
+ rtl::Reference<comphelper::OInteractionApprove> pApprove = new comphelper::OInteractionApprove();
+
+ css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations{
+ pAbort, pApprove
+ };
+
+ css::task::ErrorCodeRequest aErrorCode;
+ aErrorCode.ErrCode = sal_uInt32(ERRCODE_SFX_NOMOREDOCUMENTSALLOWED);
+ aInteraction <<= aErrorCode;
+ xInteraction->handle( InteractionRequest::CreateRequest(aInteraction, lContinuations) );
+ }
+ }
+
+ return bAllowed;
+}
+
+bool LoadEnv::impl_filterHasInteractiveDialog() const
+{
+ //show the frame now so it can be the parent for any message dialogs shown during import
+
+ //unless (tdf#114648) an Interactive case such as the new database wizard
+ if (m_aURL.Arguments == "Interactive")
+ return true;
+
+ // unless (tdf#116277) it's the labels/business cards slave frame
+ if (m_aURL.Arguments.indexOf("slot=") != -1)
+ return true;
+
+ OUString sFilter = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME, OUString());
+ if (sFilter.isEmpty())
+ return false;
+
+ // unless (tdf#115683) the filter has a UIComponent
+ OUString sUIComponent;
+ css::uno::Reference<css::container::XNameAccess> xFilterCont(m_xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_FILTERFACTORY, m_xContext),
+ css::uno::UNO_QUERY_THROW);
+ try
+ {
+ ::comphelper::SequenceAsHashMap lFilterProps(xFilterCont->getByName(sFilter));
+ sUIComponent = lFilterProps.getUnpackedValueOrDefault("UIComponent", OUString());
+ }
+ catch(const css::container::NoSuchElementException&)
+ {
+ }
+
+ return !sUIComponent.isEmpty();
+}
+
+bool LoadEnv::impl_loadContent()
+{
+ // SAFE -> -----------------------------------
+ osl::ClearableMutexGuard aWriteLock(m_mutex);
+
+ // search or create right target frame
+ OUString sTarget = m_sTarget;
+ if (TargetHelper::matchSpecialTarget(sTarget, TargetHelper::ESpecialTarget::Default))
+ {
+ m_xTargetFrame = impl_searchAlreadyLoaded();
+ if (m_xTargetFrame.is())
+ {
+ impl_setResult(true);
+ return true;
+ }
+ m_xTargetFrame = impl_searchRecycleTarget();
+ }
+
+ if (! m_xTargetFrame.is())
+ {
+ if (
+ (TargetHelper::matchSpecialTarget(sTarget, TargetHelper::ESpecialTarget::Blank )) ||
+ (TargetHelper::matchSpecialTarget(sTarget, TargetHelper::ESpecialTarget::Default))
+ )
+ {
+ if (! impl_furtherDocsAllowed())
+ return false;
+ TaskCreator aCreator(m_xContext);
+ m_xTargetFrame = aCreator.createTask(SPECIALTARGET_BLANK, m_lMediaDescriptor);
+ m_bCloseFrameOnError = m_xTargetFrame.is();
+ }
+ else
+ {
+ sal_Int32 nSearchFlags = m_nSearchFlags & ~css::frame::FrameSearchFlag::CREATE;
+ m_xTargetFrame = m_xBaseFrame->findFrame(sTarget, nSearchFlags);
+ if (! m_xTargetFrame.is())
+ {
+ if (! impl_furtherDocsAllowed())
+ return false;
+ m_xTargetFrame = m_xBaseFrame->findFrame(SPECIALTARGET_BLANK, 0);
+ m_bCloseFrameOnError = m_xTargetFrame.is();
+ }
+ }
+ }
+
+ // If we couldn't find a valid frame or the frame has no container window
+ // we have to throw an exception.
+ if (
+ ( ! m_xTargetFrame.is() ) ||
+ ( ! m_xTargetFrame->getContainerWindow().is() )
+ )
+ throw LoadEnvException(LoadEnvException::ID_NO_TARGET_FOUND);
+
+ css::uno::Reference< css::frame::XFrame > xTargetFrame = m_xTargetFrame;
+
+ // Now we have a valid frame ... and type detection was already done.
+ // We should apply the module dependent window position and size to the
+ // frame window.
+ impl_applyPersistentWindowState(xTargetFrame->getContainerWindow());
+
+ // Don't forget to lock task for following load process. Otherwise it could die
+ // during this operation runs by terminating the office or closing this task via api.
+ // If we set this lock "close()" will return false and closing will be broken.
+ // Attention: Don't forget to reset this lock again after finishing operation.
+ // Otherwise task AND office couldn't die!!!
+ // This includes gracefully handling of Exceptions (Runtime!) too ...
+ // That's why we use a specialized guard, which will reset the lock
+ // if it will be run out of scope.
+
+ // Note further: ignore if this internal guard already contains a resource.
+ // Might impl_searchRecycleTarget() set it before. But in case this impl-method wasn't used
+ // and the target frame was new created ... this lock here must be set!
+ css::uno::Reference< css::document::XActionLockable > xTargetLock(xTargetFrame, css::uno::UNO_QUERY);
+ m_aTargetLock.setResource(xTargetLock);
+
+ // Add status indicator to descriptor. Loader can show a progress then.
+ // But don't do it, if loading should be hidden or preview is used...!
+ // So we prevent our code against wrong using. Why?
+ // It could be, that using of this progress could make trouble. e.g. He makes window visible...
+ // but shouldn't do that. But if no indicator is available... nobody has a chance to do that!
+ bool bHidden = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false);
+ bool bMinimized = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_MINIMIZED, false);
+ bool bPreview = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW, false);
+
+ if (!bHidden && !bMinimized && !bPreview)
+ {
+ css::uno::Reference<css::task::XStatusIndicator> xProgress = m_lMediaDescriptor.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_STATUSINDICATOR, css::uno::Reference<css::task::XStatusIndicator>());
+ if (!xProgress.is())
+ {
+ // Note: it's an optional interface!
+ css::uno::Reference< css::task::XStatusIndicatorFactory > xProgressFactory(xTargetFrame, css::uno::UNO_QUERY);
+ if (xProgressFactory.is())
+ {
+ xProgress = xProgressFactory->createStatusIndicator();
+ if (xProgress.is())
+ m_lMediaDescriptor[utl::MediaDescriptor::PROP_STATUSINDICATOR] <<= xProgress;
+ }
+ }
+
+ // Now that we have a target window into which we can load, reinit the interaction handler to have this
+ // window as its parent for modal dialogs and ensure the window is visible
+ css::uno::Reference< css::task::XInteractionHandler > xInteraction = m_lMediaDescriptor.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_INTERACTIONHANDLER,
+ css::uno::Reference< css::task::XInteractionHandler >());
+ css::uno::Reference<css::lang::XInitialization> xHandler(xInteraction, css::uno::UNO_QUERY);
+ if (xHandler.is())
+ {
+ css::uno::Reference<css::awt::XWindow> xWindow = xTargetFrame->getContainerWindow();
+ uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence(
+ {
+ {"Parent", uno::Any(xWindow)}
+ }));
+ xHandler->initialize(aArguments);
+ //show the frame as early as possible to make it the parent of any message dialogs
+ if (!impl_filterHasInteractiveDialog())
+ impl_makeFrameWindowVisible(xWindow, false);
+ }
+ }
+
+ // convert media descriptor and URL to right format for later interface call!
+ css::uno::Sequence< css::beans::PropertyValue > lDescriptor;
+ m_lMediaDescriptor >> lDescriptor;
+ OUString sURL = m_aURL.Complete;
+
+ // try to locate any interested frame loader
+ css::uno::Reference< css::uno::XInterface > xLoader = impl_searchLoader();
+ css::uno::Reference< css::frame::XFrameLoader > xAsyncLoader(xLoader, css::uno::UNO_QUERY);
+ css::uno::Reference< css::frame::XSynchronousFrameLoader > xSyncLoader (xLoader, css::uno::UNO_QUERY);
+
+ if (xAsyncLoader.is())
+ {
+ m_xAsynchronousJob = xAsyncLoader;
+ rtl::Reference<LoadEnvListener> xListener = new LoadEnvListener(this);
+ aWriteLock.clear();
+ // <- SAFE -----------------------------------
+
+ xAsyncLoader->load(xTargetFrame, sURL, lDescriptor, xListener);
+
+ return true;
+ }
+ else if (xSyncLoader.is())
+ {
+ uno::Reference<beans::XPropertySet> xTargetFrameProps(xTargetFrame, uno::UNO_QUERY);
+ if (xTargetFrameProps.is())
+ {
+ // Set the URL on the frame itself, for the duration of the load, when it has no
+ // controller.
+ xTargetFrameProps->setPropertyValue("URL", uno::Any(sURL));
+ }
+ bool bResult = xSyncLoader->load(lDescriptor, xTargetFrame);
+ // react for the result here, so the outside waiting
+ // code can ask for it later.
+ impl_setResult(bResult);
+ // But the return value indicates a valid started(!) operation.
+ // And that's true every time we reach this line :-)
+ return true;
+ }
+
+ aWriteLock.clear();
+ // <- SAFE
+
+ return false;
+}
+
+css::uno::Reference< css::uno::XInterface > LoadEnv::impl_searchLoader()
+{
+ // SAFE -> -----------------------------------
+ osl::ClearableMutexGuard aReadLock(m_mutex);
+
+ // special mode to set an existing component on this frame
+ // In such case the loader is fix. It must be the SFX based implementation,
+ // which can create a view on top of such xModel components :-)
+ if (m_eContentType == E_CAN_BE_SET)
+ {
+ try
+ {
+ return css::frame::OfficeFrameLoader::create(m_xContext);
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ {}
+ throw LoadEnvException(LoadEnvException::ID_INVALID_ENVIRONMENT);
+ }
+
+ // Otherwise...
+ // We need this type information to locate a registered frame loader
+ // Without such information we can't work!
+ OUString sType = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_TYPENAME, OUString());
+ if (sType.isEmpty())
+ throw LoadEnvException(LoadEnvException::ID_INVALID_MEDIADESCRIPTOR);
+
+ // try to locate any interested frame loader
+ css::uno::Reference< css::frame::XLoaderFactory > xLoaderFactory = css::frame::FrameLoaderFactory::create(m_xContext);
+
+ aReadLock.clear();
+ // <- SAFE -----------------------------------
+
+ css::uno::Sequence< OUString > lTypesReg { sType };
+
+ css::uno::Sequence< css::beans::NamedValue > lQuery { { PROP_TYPES, css::uno::Any(lTypesReg) } };
+
+ css::uno::Reference< css::container::XEnumeration > xSet = xLoaderFactory->createSubSetEnumerationByProperties(lQuery);
+ while(xSet->hasMoreElements())
+ {
+ try
+ {
+ // try everyone ...
+ // Ignore any loader, which makes trouble :-)
+ ::comphelper::SequenceAsHashMap lLoaderProps(xSet->nextElement());
+ OUString sLoader = lLoaderProps.getUnpackedValueOrDefault(PROP_NAME, OUString());
+ css::uno::Reference< css::uno::XInterface > xLoader = xLoaderFactory->createInstance(sLoader);
+ if (xLoader.is())
+ return xLoader;
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { continue; }
+ }
+
+ return css::uno::Reference< css::uno::XInterface >();
+}
+
+void LoadEnv::impl_jumpToMark(const css::uno::Reference< css::frame::XFrame >& xFrame,
+ const css::util::URL& aURL )
+{
+ if (aURL.Mark.isEmpty())
+ return;
+
+ css::uno::Reference< css::frame::XDispatchProvider > xProvider(xFrame, css::uno::UNO_QUERY);
+ if (! xProvider.is())
+ return;
+
+ // SAFE ->
+ osl::ClearableMutexGuard aReadLock(m_mutex);
+ css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext;
+ aReadLock.clear();
+ // <- SAFE
+
+ css::util::URL aCmd;
+ aCmd.Complete = ".uno:JumpToMark";
+
+ css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(xContext));
+ xParser->parseStrict(aCmd);
+
+ css::uno::Reference< css::frame::XDispatch > xDispatcher = xProvider->queryDispatch(aCmd, SPECIALTARGET_SELF, 0);
+ if (! xDispatcher.is())
+ return;
+
+ ::comphelper::SequenceAsHashMap lArgs;
+ lArgs[OUString("Bookmark")] <<= aURL.Mark;
+ xDispatcher->dispatch(aCmd, lArgs.getAsConstPropertyValueList());
+}
+
+css::uno::Reference< css::frame::XFrame > LoadEnv::impl_searchAlreadyLoaded()
+{
+ osl::MutexGuard g(m_mutex);
+
+ // such search is allowed for special requests only ...
+ // or better it's not allowed for some requests in general :-)
+ if (
+ ( ! TargetHelper::matchSpecialTarget(m_sTarget, TargetHelper::ESpecialTarget::Default) ) ||
+ m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ASTEMPLATE , false) ||
+// (m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN() , false) == sal_True) ||
+ m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_OPENNEWVIEW, false)
+ )
+ {
+ return css::uno::Reference< css::frame::XFrame >();
+ }
+
+ // check URL
+ // May it's not useful to start expensive document search, if it
+ // can fail only .. because we load from a stream or model directly!
+ if (
+ (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateStream )) ||
+ (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateObject ))
+ /*TODO should be private:factory here tested too? */
+ )
+ {
+ return css::uno::Reference< css::frame::XFrame >();
+ }
+
+ // otherwise - iterate through the tasks of the desktop container
+ // to find out, which of them might contains the requested document
+ css::uno::Reference< css::frame::XDesktop2 > xSupplier = css::frame::Desktop::create( m_xContext );
+ css::uno::Reference< css::container::XIndexAccess > xTaskList = xSupplier->getFrames();
+
+ if (!xTaskList.is())
+ return css::uno::Reference< css::frame::XFrame >(); // task list can be empty!
+
+ // Note: To detect if a document was already loaded before
+ // we check URLs here only. But might the existing and the required
+ // document has different versions! Then its URLs are the same...
+ sal_Int16 nNewVersion = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_VERSION, sal_Int16(-1));
+
+ // will be used to save the first hidden frame referring the searched model
+ // Normally we are interested on visible frames... but if there is no such visible
+ // frame we refer to any hidden frame also (but as fallback only).
+ css::uno::Reference< css::frame::XFrame > xHiddenTask;
+ css::uno::Reference< css::frame::XFrame > xTask;
+
+ sal_Int32 count = xTaskList->getCount();
+ for (sal_Int32 i=0; i<count; ++i)
+ {
+ try
+ {
+ // locate model of task
+ // Note: Without a model there is no chance to decide if
+ // this task contains the searched document or not!
+ xTaskList->getByIndex(i) >>= xTask;
+ if (!xTask.is())
+ continue;
+
+ OUString sURL;
+ css::uno::Reference< css::frame::XController > xController = xTask->getController();
+ if (!xController.is())
+ {
+ // If we have no controller, then perhaps there is a load in progress. The frame
+ // itself has the URL in this case.
+ uno::Reference<beans::XPropertySet> xTaskProps(xTask, uno::UNO_QUERY);
+ if (xTaskProps.is())
+ {
+ xTaskProps->getPropertyValue("URL") >>= sURL;
+ }
+ if (sURL.isEmpty())
+ {
+ xTask.clear();
+ continue;
+ }
+ }
+
+ uno::Reference<frame::XModel> xModel;
+ if (sURL.isEmpty())
+ {
+ xModel = xController->getModel();
+ if (!xModel.is())
+ {
+ xTask.clear();
+ continue;
+ }
+
+ // don't check the complete URL here.
+ // use its main part - ignore optional jumpmarks!
+ sURL = xModel->getURL();
+ }
+ if (!::utl::UCBContentHelper::EqualURLs( m_aURL.Main, sURL ))
+ {
+ xTask.clear ();
+ continue;
+ }
+
+ // get the original load arguments from the current document
+ // and decide if it's really the same then the one will be.
+ // It must be visible and must use the same file revision ...
+ // or must not have any file revision set (-1 == -1!)
+ utl::MediaDescriptor lOldDocDescriptor;
+ if (xModel.is())
+ {
+ lOldDocDescriptor = xModel->getArgs();
+
+ if (lOldDocDescriptor.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_VERSION, sal_Int32(-1))
+ != nNewVersion)
+ {
+ xTask.clear();
+ continue;
+ }
+ }
+
+ // Hidden frames are special.
+ // They will be used as "last chance" if there is no visible frame pointing to the same model.
+ // Safe the result but continue with current loop might be looking for other visible frames.
+ bool bIsHidden = lOldDocDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false);
+ if ( bIsHidden && ! xHiddenTask.is() )
+ {
+ xHiddenTask = xTask;
+ xTask.clear ();
+ continue;
+ }
+
+ // We found a visible task pointing to the right model ...
+ // Break search.
+ break;
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { continue; }
+ }
+
+ css::uno::Reference< css::frame::XFrame > xResult;
+ if (xTask.is())
+ xResult = xTask;
+ else if (xHiddenTask.is())
+ xResult = xHiddenTask;
+
+ if (xResult.is())
+ {
+ // Now we are sure, that this task includes the searched document.
+ // It's time to activate it. As special feature we try to jump internally
+ // if an optional jumpmark is given too.
+ if (!m_aURL.Mark.isEmpty())
+ impl_jumpToMark(xResult, m_aURL);
+
+ // bring it to front and make sure it's visible...
+ impl_makeFrameWindowVisible(xResult->getContainerWindow(), true);
+ }
+
+ return xResult;
+}
+
+bool LoadEnv::impl_isFrameAlreadyUsedForLoading(const css::uno::Reference< css::frame::XFrame >& xFrame) const
+{
+ css::uno::Reference< css::document::XActionLockable > xLock(xFrame, css::uno::UNO_QUERY);
+
+ // ? no lock interface ?
+ // Maybe it's an external written frame implementation :-(
+ // Allowing using of it... but it can fail if it's not synchronized with our processes!
+ if (!xLock.is())
+ return false;
+
+ // Otherwise we have to look for any other existing lock.
+ return xLock->isActionLocked();
+}
+
+css::uno::Reference< css::frame::XFrame > LoadEnv::impl_searchRecycleTarget()
+{
+ // SAFE -> ..................................
+ osl::ClearableMutexGuard aReadLock(m_mutex);
+
+ // The special backing mode frame will be recycled by definition!
+ // It doesn't matter if somewhere wants to create a new view
+ // or open a new untitled document...
+ // The only exception from that - hidden frames!
+ if (m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false))
+ return css::uno::Reference< css::frame::XFrame >();
+
+ css::uno::Reference< css::frame::XFramesSupplier > xSupplier = css::frame::Desktop::create( m_xContext );
+ FrameListAnalyzer aTasksAnalyzer(xSupplier, css::uno::Reference< css::frame::XFrame >(), FrameAnalyzerFlags::BackingComponent);
+ if (aTasksAnalyzer.m_xBackingComponent.is())
+ {
+ if (!impl_isFrameAlreadyUsedForLoading(aTasksAnalyzer.m_xBackingComponent))
+ {
+ // bring it to front...
+ impl_makeFrameWindowVisible(aTasksAnalyzer.m_xBackingComponent->getContainerWindow(), true);
+ m_bReactivateControllerOnError = true;
+ return aTasksAnalyzer.m_xBackingComponent;
+ }
+ }
+
+ // These states indicates a wish for creation of a new view in general.
+ if (
+ m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ASTEMPLATE , false) ||
+ m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_OPENNEWVIEW, false)
+ )
+ {
+ return css::uno::Reference< css::frame::XFrame >();
+ }
+
+ // On the other side some special URLs will open a new frame every time (expecting
+ // they can use the backing-mode frame!)
+ if (
+ (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateFactory )) ||
+ (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateStream )) ||
+ (ProtocolCheck::isProtocol(m_aURL.Complete, EProtocol::PrivateObject ))
+ )
+ {
+ return css::uno::Reference< css::frame::XFrame >();
+ }
+
+ // No backing frame! No special URL => recycle active task - if possible.
+ // Means - if it does not already contains a modified document, or
+ // use another office module.
+ css::uno::Reference< css::frame::XFrame > xTask = xSupplier->getActiveFrame();
+
+ // not a real error - but might a focus problem!
+ if (!xTask.is())
+ return css::uno::Reference< css::frame::XFrame >();
+
+ // not a real error - may it's a view only
+ css::uno::Reference< css::frame::XController > xController = xTask->getController();
+ if (!xController.is())
+ return css::uno::Reference< css::frame::XFrame >();
+
+ // not a real error - may it's a db component instead of a full featured office document
+ css::uno::Reference< css::frame::XModel > xModel = xController->getModel();
+ if (!xModel.is())
+ return css::uno::Reference< css::frame::XFrame >();
+
+ // get some more information ...
+
+ // A valid set URL means: there is already a location for this document.
+ // => it was saved there or opened from there. Such Documents can not be used here.
+ // We search for empty document ... created by a private:factory/ URL!
+ if (xModel->getURL().getLength()>0)
+ return css::uno::Reference< css::frame::XFrame >();
+
+ // The old document must be unmodified ...
+ css::uno::Reference< css::util::XModifiable > xModified(xModel, css::uno::UNO_QUERY);
+ if (xModified->isModified())
+ return css::uno::Reference< css::frame::XFrame >();
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xTask->getContainerWindow());
+ if (pWindow && pWindow->IsInModalMode())
+ return css::uno::Reference< css::frame::XFrame >();
+
+ // find out the application type of this document
+ // We can recycle only documents, which uses the same application
+ // then the new one.
+ SvtModuleOptions::EFactory eOldApp = SvtModuleOptions::ClassifyFactoryByModel(xModel);
+ SvtModuleOptions::EFactory eNewApp = SvtModuleOptions::ClassifyFactoryByURL (m_aURL.Complete, m_lMediaDescriptor.getAsConstPropertyValueList());
+
+ aReadLock.clear();
+ // <- SAFE ..................................
+
+ if (eOldApp != eNewApp)
+ return css::uno::Reference< css::frame::XFrame >();
+
+ // OK this task seems to be usable for recycling
+ // But we should mark it as such - means set an action lock.
+ // Otherwise it would be used more than ones or will be destroyed
+ // by a close() or terminate() request.
+ // But if such lock already exist ... it means this task is used for
+ // any other operation already. Don't use it then.
+ if (impl_isFrameAlreadyUsedForLoading(xTask))
+ return css::uno::Reference< css::frame::XFrame >();
+
+ // OK - there is a valid target frame.
+ // But may be it contains already a document.
+ // Then we have to ask it, if it allows recycling of this frame .-)
+ bool bReactivateOldControllerOnError = false;
+ css::uno::Reference< css::frame::XController > xOldDoc = xTask->getController();
+ if (xOldDoc.is())
+ {
+ utl::MediaDescriptor lOldDocDescriptor(xModel->getArgs());
+
+ // replaceable document
+ if (!lOldDocDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_REPLACEABLE, false))
+ return css::uno::Reference< css::frame::XFrame >();
+
+ bReactivateOldControllerOnError = xOldDoc->suspend(true);
+ if (! bReactivateOldControllerOnError)
+ return css::uno::Reference< css::frame::XFrame >();
+ }
+
+ // SAFE -> ..................................
+ {
+ osl::MutexGuard aWriteLock(m_mutex);
+
+ css::uno::Reference< css::document::XActionLockable > xLock(xTask, css::uno::UNO_QUERY);
+ if (!m_aTargetLock.setResource(xLock))
+ return css::uno::Reference< css::frame::XFrame >();
+
+ m_bReactivateControllerOnError = bReactivateOldControllerOnError;
+ }
+ // <- SAFE ..................................
+
+ // bring it to front ...
+ impl_makeFrameWindowVisible(xTask->getContainerWindow(), true);
+
+ return xTask;
+}
+
+void LoadEnv::impl_reactForLoadingState()
+{
+ /*TODO reset action locks */
+
+ // SAFE -> ----------------------------------
+ osl::ClearableMutexGuard aReadLock(m_mutex);
+
+ if (m_bLoaded)
+ {
+ // Bring the new loaded document to front (if allowed!).
+ // Note: We show new created frames here only.
+ // We don't hide already visible frames here ...
+ css::uno::Reference< css::awt::XWindow > xWindow = m_xTargetFrame->getContainerWindow();
+ bool bHidden = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN, false);
+ bool bMinimized = m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_MINIMIZED, false);
+
+ if (bMinimized)
+ {
+ SolarMutexGuard aSolarGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ // check for system window is necessary to guarantee correct pointer cast!
+ if (pWindow && pWindow->IsSystemWindow())
+ static_cast<WorkWindow*>(pWindow.get())->Minimize();
+ }
+ else if (!bHidden)
+ {
+ // show frame ... if it's not still visible ...
+ // But do nothing if it's already visible!
+ impl_makeFrameWindowVisible(xWindow, false);
+ }
+
+ // Note: Only if an existing property "FrameName" is given by this media descriptor,
+ // it should be used. Otherwise we should do nothing. May be the outside code has already
+ // set a frame name on the target!
+ utl::MediaDescriptor::const_iterator pFrameName = m_lMediaDescriptor.find(utl::MediaDescriptor::PROP_FRAMENAME);
+ if (pFrameName != m_lMediaDescriptor.end())
+ {
+ OUString sFrameName;
+ pFrameName->second >>= sFrameName;
+ // Check the name again. e.g. "_default" isn't allowed.
+ // On the other side "_beamer" is a valid name :-)
+ if (TargetHelper::isValidNameForFrame(sFrameName))
+ m_xTargetFrame->setName(sFrameName);
+ }
+ }
+ else if (m_bReactivateControllerOnError)
+ {
+ // Try to reactivate the old document (if any exists!)
+ css::uno::Reference< css::frame::XController > xOldDoc = m_xTargetFrame->getController();
+ // clear does not depend from reactivation state of a might existing old document!
+ // We must make sure, that a might following getTargetComponent() call does not return
+ // the old document!
+ m_xTargetFrame.clear();
+ if (xOldDoc.is())
+ {
+ bool bReactivated = xOldDoc->suspend(false);
+ if (!bReactivated)
+ throw LoadEnvException(LoadEnvException::ID_COULD_NOT_REACTIVATE_CONTROLLER);
+ m_bReactivateControllerOnError = false;
+ }
+ }
+ else if (m_bCloseFrameOnError)
+ {
+ // close empty frames
+ css::uno::Reference< css::util::XCloseable > xCloseable (m_xTargetFrame, css::uno::UNO_QUERY);
+
+ try
+ {
+ if (xCloseable.is())
+ xCloseable->close(true);
+ else if (m_xTargetFrame.is())
+ m_xTargetFrame->dispose();
+ }
+ catch(const css::util::CloseVetoException&)
+ {}
+ catch(const css::lang::DisposedException&)
+ {}
+ m_xTargetFrame.clear();
+ }
+
+ // This max force an implicit closing of our target frame ...
+ // e.g. in case close(sal_True) was called before and the frame
+ // kill itself if our external use-lock is released here!
+ // That's why we release this lock AFTER ALL OPERATIONS on this frame
+ // are finished. The frame itself must handle then
+ // this situation gracefully.
+ m_aTargetLock.freeResource();
+
+ // Last but not least :-)
+ // We have to clear the current media descriptor.
+ // Otherwise it hold a might existing stream open!
+ m_lMediaDescriptor.clear();
+
+ css::uno::Any aRequest;
+ bool bThrow = false;
+ if ( !m_bLoaded && m_pQuietInteraction.is() && m_pQuietInteraction->wasUsed() )
+ {
+ aRequest = m_pQuietInteraction->getRequest();
+ m_pQuietInteraction.clear();
+ bThrow = true;
+ }
+
+ aReadLock.clear();
+
+ if (bThrow)
+ {
+ if ( aRequest.isExtractableTo( ::cppu::UnoType< css::uno::Exception >::get() ) )
+ throw LoadEnvException(
+ LoadEnvException::ID_GENERAL_ERROR, "interaction request",
+ aRequest);
+ }
+
+ // <- SAFE ----------------------------------
+}
+
+void LoadEnv::impl_makeFrameWindowVisible(const css::uno::Reference< css::awt::XWindow >& xWindow ,
+ bool bForceToFront)
+{
+ SolarMutexGuard aSolarGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ if ( !pWindow )
+ return;
+
+ bool const preview( m_lMediaDescriptor.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_PREVIEW, false) );
+
+ bool bForceFrontAndFocus(false);
+ if ( !preview )
+ {
+ bForceFrontAndFocus = officecfg::Office::Common::View::NewDocumentHandling::ForceFocusAndToFront::get();
+ }
+
+ if( pWindow->IsVisible() && (bForceFrontAndFocus || bForceToFront) )
+ pWindow->ToTop( ToTopFlags::RestoreWhenMin | ToTopFlags::ForegroundTask );
+ else
+ pWindow->Show(true, (bForceFrontAndFocus || bForceToFront) ? ShowFlags::ForegroundTask : ShowFlags::NONE );
+}
+
+void LoadEnv::impl_applyPersistentWindowState(const css::uno::Reference< css::awt::XWindow >& xWindow)
+{
+ // no window -> action not possible
+ if (!xWindow.is())
+ return;
+
+ // window already visible -> do nothing! If we use a "recycle frame" for loading ...
+ // the current position and size must be used.
+ css::uno::Reference< css::awt::XWindow2 > xVisibleCheck(xWindow, css::uno::UNO_QUERY);
+ if (
+ (xVisibleCheck.is() ) &&
+ (xVisibleCheck->isVisible())
+ )
+ return;
+
+ // SOLAR SAFE ->
+ {
+ SolarMutexGuard aSolarGuard1;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ if (!pWindow)
+ return;
+
+ bool bSystemWindow = pWindow->IsSystemWindow();
+ bool bWorkWindow = (pWindow->GetType() == WindowType::WORKWINDOW);
+
+ if (!bSystemWindow && !bWorkWindow)
+ return;
+
+ // don't overwrite this special state!
+ WorkWindow* pWorkWindow = static_cast<WorkWindow*>(pWindow.get());
+ if (pWorkWindow->IsMinimized())
+ return;
+ }
+ // <- SOLAR SAFE
+
+ // SAFE ->
+ osl::ClearableMutexGuard aReadLock(m_mutex);
+
+ // no filter -> no module -> no persistent window state
+ OUString sFilter = m_lMediaDescriptor.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_FILTERNAME,
+ OUString());
+ if (sFilter.isEmpty())
+ return;
+
+ css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext;
+
+ aReadLock.clear();
+ // <- SAFE
+
+ try
+ {
+ // retrieve the module name from the filter configuration
+ css::uno::Reference< css::container::XNameAccess > xFilterCfg(
+ xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_FILTERFACTORY, xContext),
+ css::uno::UNO_QUERY_THROW);
+ ::comphelper::SequenceAsHashMap lProps (xFilterCfg->getByName(sFilter));
+ OUString sModule = lProps.getUnpackedValueOrDefault(FILTER_PROPNAME_ASCII_DOCUMENTSERVICE, OUString());
+
+ // get access to the configuration of this office module
+ css::uno::Reference< css::container::XNameAccess > xModuleCfg(officecfg::Setup::Office::Factories::get());
+
+ // read window state from the configuration
+ // and apply it on the window.
+ // Do nothing, if no configuration entry exists!
+ OUString sWindowState;
+
+ // Don't look for persistent window attributes when used through LibreOfficeKit
+ if( !comphelper::LibreOfficeKit::isActive() )
+ comphelper::ConfigurationHelper::readRelativeKey(xModuleCfg, sModule, "ooSetupFactoryWindowAttributes") >>= sWindowState;
+
+ if (!sWindowState.isEmpty())
+ {
+ // SOLAR SAFE ->
+ SolarMutexGuard aSolarGuard;
+
+ // We have to retrieve the window pointer again. Because nobody can guarantee
+ // that the XWindow was not disposed in between .-)
+ // But if we get a valid pointer we can be sure, that it's the system window pointer
+ // we already checked and used before. Because nobody recycle the same uno reference for
+ // a new internal c++ implementation ... hopefully .-))
+ VclPtr<vcl::Window> pWindowCheck = VCLUnoHelper::GetWindow(xWindow);
+ if (! pWindowCheck)
+ return;
+
+ SystemWindow* pSystemWindow = static_cast<SystemWindow*>(pWindowCheck.get());
+ pSystemWindow->SetWindowState(OUStringToOString(sWindowState,RTL_TEXTENCODING_UTF8));
+ // <- SOLAR SAFE
+ }
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ {}
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/loadenv/targethelper.cxx b/framework/source/loadenv/targethelper.cxx
new file mode 100644
index 000000000..7c06521da
--- /dev/null
+++ b/framework/source/loadenv/targethelper.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <loadenv/targethelper.hxx>
+#include <targets.h>
+
+namespace framework{
+
+bool TargetHelper::matchSpecialTarget(std::u16string_view sCheckTarget ,
+ ESpecialTarget eSpecialTarget)
+{
+ switch(eSpecialTarget)
+ {
+ case ESpecialTarget::Blank :
+ return sCheckTarget == SPECIALTARGET_BLANK;
+
+ case ESpecialTarget::Default :
+ return sCheckTarget == SPECIALTARGET_DEFAULT;
+
+ case ESpecialTarget::Beamer :
+ return sCheckTarget == SPECIALTARGET_BEAMER;
+
+ case ESpecialTarget::HelpTask :
+ return sCheckTarget == SPECIALTARGET_HELPTASK;
+ default:
+ return false;
+ }
+}
+
+bool TargetHelper::isValidNameForFrame(std::u16string_view sName)
+{
+ // some special targets are really special ones :-)
+ // E.g. the are really used to locate one frame inside the frame tree.
+ if (
+ (sName.empty() ) ||
+ (TargetHelper::matchSpecialTarget(sName, ESpecialTarget::HelpTask)) ||
+ (TargetHelper::matchSpecialTarget(sName, ESpecialTarget::Beamer) )
+ )
+ return true;
+
+ // all other names must be checked more general
+ // special targets starts with a "_".
+ return (sName.find('_') != 0);
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/recording/dispatchrecorder.cxx b/framework/source/recording/dispatchrecorder.cxx
new file mode 100644
index 000000000..2afa8926d
--- /dev/null
+++ b/framework/source/recording/dispatchrecorder.cxx
@@ -0,0 +1,439 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <recording/dispatchrecorder.hxx>
+#include <com/sun/star/frame/DispatchStatement.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <com/sun/star/script/Converter.hpp>
+#include <o3tl/any.hxx>
+#include <osl/diagnose.h>
+#include <vcl/svapp.hxx>
+#include <typelib/typedescription.h>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace ::com::sun::star::uno;
+
+namespace framework{
+
+// used to mark a dispatch as comment (mostly it indicates an error) Changing of this define will impact all using of such comments...
+constexpr OUStringLiteral REM_AS_COMMENT = u"rem ";
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL DispatchRecorder::getImplementationName()
+{
+ return "com.sun.star.comp.framework.DispatchRecorder";
+}
+
+sal_Bool SAL_CALL DispatchRecorder::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL DispatchRecorder::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.DispatchRecorder" };
+}
+
+
+
+static void flatten_struct_members(
+ ::std::vector< Any > * vec, void const * data,
+ typelib_CompoundTypeDescription * pTD )
+{
+ if (pTD->pBaseTypeDescription)
+ {
+ flatten_struct_members( vec, data, pTD->pBaseTypeDescription );
+ }
+ for ( sal_Int32 nPos = 0; nPos < pTD->nMembers; ++nPos )
+ {
+ vec->push_back(
+ Any( static_cast<char const *>(data) + pTD->pMemberOffsets[ nPos ], pTD->ppTypeRefs[ nPos ] ) );
+ }
+}
+
+static Sequence< Any > make_seq_out_of_struct(
+ Any const & val )
+{
+ Type const & type = val.getValueType();
+ TypeClass eTypeClass = type.getTypeClass();
+ if (TypeClass_STRUCT != eTypeClass && TypeClass_EXCEPTION != eTypeClass)
+ {
+ throw RuntimeException(
+ type.getTypeName() + "is no struct or exception!" );
+ }
+ typelib_TypeDescription * pTD = nullptr;
+ TYPELIB_DANGER_GET( &pTD, type.getTypeLibType() );
+ OSL_ASSERT( pTD );
+ if (! pTD)
+ {
+ throw RuntimeException(
+ "cannot get type descr of type " + type.getTypeName() );
+ }
+
+ ::std::vector< Any > vec;
+ vec.reserve( reinterpret_cast<typelib_CompoundTypeDescription *>(pTD)->nMembers ); // good guess
+ flatten_struct_members( &vec, val.getValue(), reinterpret_cast<typelib_CompoundTypeDescription *>(pTD) );
+ TYPELIB_DANGER_RELEASE( pTD );
+ return Sequence< Any >( vec.data(), vec.size() );
+}
+
+DispatchRecorder::DispatchRecorder( const css::uno::Reference< css::uno::XComponentContext >& xContext )
+ : m_nRecordingID(0)
+ , m_xConverter(css::script::Converter::create(xContext))
+{
+}
+
+DispatchRecorder::~DispatchRecorder()
+{
+}
+
+// generate header
+void SAL_CALL DispatchRecorder::startRecording( const css::uno::Reference< css::frame::XFrame >& )
+{
+ /* SAFE{ */
+ /* } */
+}
+
+void SAL_CALL DispatchRecorder::recordDispatch( const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
+{
+ css::frame::DispatchStatement aStatement( aURL.Complete, OUString(), lArguments, 0, false );
+ m_aStatements.push_back( aStatement );
+}
+
+void SAL_CALL DispatchRecorder::recordDispatchAsComment( const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
+{
+ // last parameter must be set to true -> it's a comment
+ css::frame::DispatchStatement aStatement( aURL.Complete, OUString(), lArguments, 0, true );
+ m_aStatements.push_back( aStatement );
+}
+
+void SAL_CALL DispatchRecorder::endRecording()
+{
+ SolarMutexGuard g;
+ m_aStatements.clear();
+}
+
+OUString SAL_CALL DispatchRecorder::getRecordedMacro()
+{
+ SolarMutexGuard g;
+
+ if ( m_aStatements.empty() )
+ return OUString();
+
+ OUStringBuffer aScriptBuffer;
+ aScriptBuffer.ensureCapacity(10000);
+ m_nRecordingID = 1;
+
+ aScriptBuffer.append("rem ----------------------------------------------------------------------\n");
+ aScriptBuffer.append("rem define variables\n");
+ aScriptBuffer.append("dim document as object\n");
+ aScriptBuffer.append("dim dispatcher as object\n");
+ aScriptBuffer.append("rem ----------------------------------------------------------------------\n");
+ aScriptBuffer.append("rem get access to the document\n");
+ aScriptBuffer.append("document = ThisComponent.CurrentController.Frame\n");
+ aScriptBuffer.append("dispatcher = createUnoService(\"com.sun.star.frame.DispatchHelper\")\n\n");
+
+ for (auto const& statement : m_aStatements)
+ implts_recordMacro( statement.aCommand, statement.aArgs, statement.bIsComment, aScriptBuffer );
+ OUString sScript = aScriptBuffer.makeStringAndClear();
+ return sScript;
+}
+
+void DispatchRecorder::AppendToBuffer( const css::uno::Any& aValue, OUStringBuffer& aArgumentBuffer )
+{
+ // if value == bool
+ if (aValue.getValueTypeClass() == css::uno::TypeClass_STRUCT )
+ {
+ // structs are recorded as arrays, convert to "Sequence of any"
+ Sequence< Any > aSeq = make_seq_out_of_struct( aValue );
+ aArgumentBuffer.append("Array(");
+ for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ )
+ {
+ AppendToBuffer( aSeq[nAny], aArgumentBuffer );
+ if ( nAny+1 < aSeq.getLength() )
+ // not last argument
+ aArgumentBuffer.append(",");
+ }
+
+ aArgumentBuffer.append(")");
+ }
+ else if (aValue.getValueTypeClass() == css::uno::TypeClass_SEQUENCE )
+ {
+ // convert to "Sequence of any"
+ css::uno::Sequence < css::uno::Any > aSeq;
+ css::uno::Any aNew;
+ try { aNew = m_xConverter->convertTo( aValue, cppu::UnoType<css::uno::Sequence < css::uno::Any >>::get() ); }
+ catch (const css::uno::Exception&) {}
+
+ aNew >>= aSeq;
+ aArgumentBuffer.append("Array(");
+ for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ )
+ {
+ AppendToBuffer( aSeq[nAny], aArgumentBuffer );
+ if ( nAny+1 < aSeq.getLength() )
+ // not last argument
+ aArgumentBuffer.append(",");
+ }
+
+ aArgumentBuffer.append(")");
+ }
+ else if (aValue.getValueTypeClass() == css::uno::TypeClass_STRING )
+ {
+ // strings need \"
+ OUString sVal;
+ aValue >>= sVal;
+
+ // encode non printable characters or '"' by using the CHR$ function
+ if ( !sVal.isEmpty() )
+ {
+ const sal_Unicode* pChars = sVal.getStr();
+ bool bInString = false;
+ for ( sal_Int32 nChar=0; nChar<sVal.getLength(); nChar ++ )
+ {
+ if ( pChars[nChar] < 32 || pChars[nChar] == '"' )
+ {
+ // problematic character detected
+ if ( bInString )
+ {
+ // close current string
+ aArgumentBuffer.append("\"");
+ bInString = false;
+ }
+
+ if ( nChar>0 )
+ // if this is not the first character, parts of the string have already been added
+ aArgumentBuffer.append("+");
+
+ // add the character constant
+ aArgumentBuffer.append("CHR$(");
+ aArgumentBuffer.append( static_cast<sal_Int32>(pChars[nChar]) );
+ aArgumentBuffer.append(")");
+ }
+ else
+ {
+ if ( !bInString )
+ {
+ if ( nChar>0 )
+ // if this is not the first character, parts of the string have already been added
+ aArgumentBuffer.append("+");
+
+ // start a new string
+ aArgumentBuffer.append("\"");
+ bInString = true;
+ }
+
+ aArgumentBuffer.append( pChars[nChar] );
+ }
+ }
+
+ // close string
+ if ( bInString )
+ aArgumentBuffer.append("\"");
+ }
+ else
+ aArgumentBuffer.append("\"\"");
+ }
+ else if (auto nVal = o3tl::tryAccess<sal_Unicode>(aValue))
+ {
+ // character variables are recorded as strings, back conversion must be handled in client code
+ aArgumentBuffer.append("\"");
+ if ( *nVal == '\"' )
+ // encode \" to \"\"
+ aArgumentBuffer.append(*nVal);
+ aArgumentBuffer.append(*nVal);
+ aArgumentBuffer.append("\"");
+ }
+ else
+ {
+ css::uno::Any aNew;
+ try
+ {
+ aNew = m_xConverter->convertToSimpleType( aValue, css::uno::TypeClass_STRING );
+ }
+ catch (const css::script::CannotConvertException&) {}
+ catch (const css::uno::Exception&) {}
+ OUString sVal;
+ aNew >>= sVal;
+
+ if (aValue.getValueTypeClass() == css::uno::TypeClass_ENUM )
+ {
+ OUString aName = aValue.getValueType().getTypeName();
+ aArgumentBuffer.append( aName );
+ aArgumentBuffer.append(".");
+ }
+
+ aArgumentBuffer.append(sVal);
+ }
+}
+
+void DispatchRecorder::implts_recordMacro( std::u16string_view aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
+ bool bAsComment, OUStringBuffer& aScriptBuffer )
+{
+ OUStringBuffer aArgumentBuffer(1000);
+ // this value is used to name the arrays of aArgumentBuffer
+ OUString sArrayName = "args" + OUString::number(m_nRecordingID);
+
+ aScriptBuffer.append("rem ----------------------------------------------------------------------\n");
+
+ sal_Int32 nLength = lArguments.getLength();
+ sal_Int32 nValidArgs = 0;
+ for( sal_Int32 i=0; i<nLength; ++i )
+ {
+ if(!lArguments[i].Value.hasValue())
+ continue;
+
+ OUStringBuffer sValBuffer(100);
+ try
+ {
+ AppendToBuffer(lArguments[i].Value, sValBuffer);
+ }
+ catch(const css::uno::Exception&)
+ {
+ sValBuffer.setLength(0);
+ }
+ if (sValBuffer.isEmpty())
+ continue;
+
+ {
+ // add arg().Name
+ if(bAsComment)
+ aArgumentBuffer.append(REM_AS_COMMENT);
+ aArgumentBuffer.append (sArrayName);
+ aArgumentBuffer.append("(");
+ aArgumentBuffer.append (nValidArgs);
+ aArgumentBuffer.append(").Name = \"");
+ aArgumentBuffer.append (lArguments[i].Name);
+ aArgumentBuffer.append("\"\n");
+
+ // add arg().Value
+ if(bAsComment)
+ aArgumentBuffer.append(REM_AS_COMMENT);
+ aArgumentBuffer.append (sArrayName);
+ aArgumentBuffer.append("(");
+ aArgumentBuffer.append (nValidArgs);
+ aArgumentBuffer.append(").Value = ");
+ aArgumentBuffer.append (sValBuffer.makeStringAndClear());
+ aArgumentBuffer.append("\n");
+
+ ++nValidArgs;
+ }
+ }
+
+ // if aArgumentBuffer exist - pack it into the aScriptBuffer
+ if(nValidArgs>0)
+ {
+ if(bAsComment)
+ aScriptBuffer.append(REM_AS_COMMENT);
+ aScriptBuffer.append("dim ");
+ aScriptBuffer.append (sArrayName);
+ aScriptBuffer.append("(");
+ aScriptBuffer.append (static_cast<sal_Int32>(nValidArgs-1)); // 0 based!
+ aScriptBuffer.append(") as new com.sun.star.beans.PropertyValue\n");
+ aScriptBuffer.append (aArgumentBuffer.makeStringAndClear());
+ aScriptBuffer.append("\n");
+ }
+
+ // add code for dispatches
+ if(bAsComment)
+ aScriptBuffer.append(REM_AS_COMMENT);
+ aScriptBuffer.append("dispatcher.executeDispatch(document, \"");
+ aScriptBuffer.append(aURL);
+ aScriptBuffer.append("\", \"\", 0, ");
+ if(nValidArgs<1)
+ aScriptBuffer.append("Array()");
+ else
+ {
+ aScriptBuffer.append( sArrayName );
+ aScriptBuffer.append("()");
+ }
+ aScriptBuffer.append(")\n\n");
+
+ /* SAFE { */
+ m_nRecordingID++;
+ /* } */
+}
+
+css::uno::Type SAL_CALL DispatchRecorder::getElementType()
+{
+ return cppu::UnoType<css::frame::DispatchStatement>::get();
+}
+
+sal_Bool SAL_CALL DispatchRecorder::hasElements()
+{
+ return (! m_aStatements.empty());
+}
+
+sal_Int32 SAL_CALL DispatchRecorder::getCount()
+{
+ return m_aStatements.size();
+}
+
+css::uno::Any SAL_CALL DispatchRecorder::getByIndex(sal_Int32 idx)
+{
+ if (idx >= static_cast<sal_Int32>(m_aStatements.size()))
+ throw css::lang::IndexOutOfBoundsException( "Dispatch recorder out of bounds" );
+
+ Any element(&m_aStatements[idx],
+ cppu::UnoType<css::frame::DispatchStatement>::get());
+
+ return element;
+}
+
+void SAL_CALL DispatchRecorder::replaceByIndex(sal_Int32 idx, const css::uno::Any& element)
+{
+ if (element.getValueType() !=
+ cppu::UnoType<css::frame::DispatchStatement>::get()) {
+ throw css::lang::IllegalArgumentException(
+ "Illegal argument in dispatch recorder",
+ Reference< XInterface >(), 2 );
+ }
+
+ if (idx >= static_cast<sal_Int32>(m_aStatements.size()))
+ throw css::lang::IndexOutOfBoundsException(
+ "Dispatch recorder out of bounds" );
+
+ auto pStatement = o3tl::doAccess<css::frame::DispatchStatement>(element);
+
+ css::frame::DispatchStatement aStatement(
+ pStatement->aCommand,
+ pStatement->aTarget,
+ pStatement->aArgs,
+ pStatement->nFlags,
+ pStatement->bIsComment);
+
+ m_aStatements[idx] = aStatement;
+}
+
+} // namespace framework
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_DispatchRecorder_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::DispatchRecorder(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/recording/dispatchrecordersupplier.cxx b/framework/source/recording/dispatchrecordersupplier.cxx
new file mode 100644
index 000000000..39707aca6
--- /dev/null
+++ b/framework/source/recording/dispatchrecordersupplier.cxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <recording/dispatchrecordersupplier.hxx>
+
+#include <com/sun/star/frame/XRecordableDispatch.hpp>
+
+#include <vcl/svapp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+namespace framework{
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL DispatchRecorderSupplier::getImplementationName()
+{
+ return "com.sun.star.comp.framework.DispatchRecorderSupplier";
+}
+
+sal_Bool SAL_CALL DispatchRecorderSupplier::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL DispatchRecorderSupplier::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.DispatchRecorderSupplier" };
+}
+
+
+DispatchRecorderSupplier::DispatchRecorderSupplier()
+{
+}
+
+/**
+ @short standard destructor
+ @descr We are a helper and not a real service. So we don't provide
+ dispose() functionality. This supplier dies by ref count mechanism
+ and should release all internal used ones too.
+ */
+DispatchRecorderSupplier::~DispatchRecorderSupplier()
+{
+ m_xDispatchRecorder = nullptr;
+}
+
+/**
+ @short set a new dispatch recorder on this supplier
+ @descr Because there can exist more than one recorder implementations
+ (to generate java/basic/... scripts from recorded data) it must
+ be possible to set it on a supplier.
+
+ @see getDispatchRecorder()
+
+ @param xRecorder
+ the new recorder to set it
+ <br><NULL/> isn't recommended, because recording without a
+ valid recorder can't work. But it's not checked here. So user
+ of this supplier can decide that without changing this
+ implementation.
+
+ @change 09.04.2002 by Andreas Schluens
+ */
+void SAL_CALL DispatchRecorderSupplier::setDispatchRecorder( const css::uno::Reference< css::frame::XDispatchRecorder >& xRecorder )
+{
+ SolarMutexGuard g;
+ m_xDispatchRecorder=xRecorder;
+}
+
+/**
+ @short provides access to the dispatch recorder of this supplier
+ @descr Such recorder can be used outside to record dispatches.
+ But normally he is used internally only. Of course he must used
+ from outside to get the recorded data e.g. for saving it as a
+ script.
+
+ @see setDispatchRecorder()
+
+ @return the internal used dispatch recorder
+ <br>May it can be <NULL/> if no one was set before.
+
+ @change 09.04.2002 by Andreas Schluens
+ */
+css::uno::Reference< css::frame::XDispatchRecorder > SAL_CALL DispatchRecorderSupplier::getDispatchRecorder()
+{
+ SolarMutexGuard g;
+ return m_xDispatchRecorder;
+}
+
+/**
+ @short execute a dispatch request and record it
+ @descr If given dispatch object provides right recording interface it
+ will be used. If it's not supported it record the pure dispatch
+ parameters only. There is no code neither the possibility to
+ check if recording is enabled or not.
+
+ @param aURL the command URL
+ @param lArguments optional arguments (see com.sun.star.document.MediaDescriptor for further information)
+ @param xDispatcher the original dispatch object which should be recorded
+
+ @change 09.04.2002 by Andreas Schluens
+ */
+void SAL_CALL DispatchRecorderSupplier::dispatchAndRecord( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments ,
+ const css::uno::Reference< css::frame::XDispatch >& xDispatcher )
+{
+ SolarMutexClearableGuard aReadLock;
+ css::uno::Reference< css::frame::XDispatchRecorder > xRecorder = m_xDispatchRecorder;
+ aReadLock.clear();
+
+ // clear unspecific situations
+ if (!xDispatcher.is())
+ throw css::uno::RuntimeException("specification violation: dispatcher is NULL", static_cast< ::cppu::OWeakObject* >(this));
+
+ if (!xRecorder.is())
+ throw css::uno::RuntimeException("specification violation: no valid dispatch recorder available", static_cast< ::cppu::OWeakObject* >(this));
+
+ // check, if given dispatch supports record functionality by itself ...
+ // or must be wrapped.
+ css::uno::Reference< css::frame::XRecordableDispatch > xRecordable(
+ xDispatcher,
+ css::uno::UNO_QUERY);
+
+ if (xRecordable.is())
+ xRecordable->dispatchAndRecord(aURL,lArguments,xRecorder);
+ else
+ {
+ // There is no reason to wait for information about success
+ // of this request. Because status information of a dispatch
+ // are not guaranteed. So we execute it and record used
+ // parameters only.
+ xDispatcher->dispatch(aURL,lArguments);
+ xRecorder->recordDispatch(aURL,lArguments);
+ }
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_DispatchRecorderSupplier_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::DispatchRecorderSupplier());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/services/ContextChangeEventMultiplexer.cxx b/framework/source/services/ContextChangeEventMultiplexer.cxx
new file mode 100644
index 000000000..b2f6db457
--- /dev/null
+++ b/framework/source/services/ContextChangeEventMultiplexer.cxx
@@ -0,0 +1,366 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/mischelper.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/ui/XContextChangeEventMultiplexer.hpp>
+#include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <comphelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/ref.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+#include <map>
+#include <vector>
+
+using namespace css;
+using namespace css::uno;
+
+namespace {
+
+typedef comphelper::WeakComponentImplHelper <
+ css::ui::XContextChangeEventMultiplexer,
+ css::lang::XServiceInfo,
+ css::lang::XEventListener
+ > ContextChangeEventMultiplexerInterfaceBase;
+
+class ContextChangeEventMultiplexer
+ : public ContextChangeEventMultiplexerInterfaceBase
+{
+public:
+ ContextChangeEventMultiplexer();
+ ContextChangeEventMultiplexer(const ContextChangeEventMultiplexer&) = delete;
+ ContextChangeEventMultiplexer& operator=(const ContextChangeEventMultiplexer&) = delete;
+
+ virtual void disposing(std::unique_lock<std::mutex>&) override;
+
+ // XContextChangeEventMultiplexer
+ virtual void SAL_CALL addContextChangeEventListener (
+ const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener,
+ const css::uno::Reference<css::uno::XInterface>& rxEventFocus) override;
+ virtual void SAL_CALL removeContextChangeEventListener (
+ const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener,
+ const css::uno::Reference<css::uno::XInterface>& rxEventFocus) override;
+ virtual void SAL_CALL removeAllContextChangeEventListeners (
+ const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener) override;
+ virtual void SAL_CALL broadcastContextChangeEvent (
+ const css::ui::ContextChangeEventObject& rContextChangeEventObject,
+ const css::uno::Reference<css::uno::XInterface>& rxEventFocus) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService (
+ const OUString& rsServiceName) override;
+ virtual css::uno::Sequence< OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing (
+ const css::lang::EventObject& rEvent) override;
+
+ typedef ::std::vector<css::uno::Reference<css::ui::XContextChangeEventListener> > ListenerContainer;
+ class FocusDescriptor
+ {
+ public:
+ ListenerContainer maListeners;
+ OUString msCurrentApplicationName;
+ OUString msCurrentContextName;
+ };
+ typedef ::std::map<css::uno::Reference<css::uno::XInterface>, FocusDescriptor> ListenerMap;
+ ListenerMap maListeners;
+
+ /** Notify all listeners in the container that is associated with
+ the given event focus.
+
+ Typically called twice from broadcastEvent(), once for the
+ given event focus and once for NULL.
+ */
+ void BroadcastEventToSingleContainer (
+ const css::ui::ContextChangeEventObject& rEventObject,
+ const css::uno::Reference<css::uno::XInterface>& rxEventFocus);
+ FocusDescriptor* GetFocusDescriptor (
+ const css::uno::Reference<css::uno::XInterface>& rxEventFocus,
+ const bool bCreateWhenMissing);
+};
+
+ContextChangeEventMultiplexer::ContextChangeEventMultiplexer()
+{
+}
+
+void ContextChangeEventMultiplexer::disposing(std::unique_lock<std::mutex>& rGuard)
+{
+ ListenerMap aListeners;
+ aListeners.swap(maListeners);
+
+ rGuard.unlock();
+
+ css::uno::Reference<css::uno::XInterface> xThis (static_cast<XWeak*>(this));
+ css::lang::EventObject aEvent (xThis);
+ for (auto const& container : aListeners)
+ {
+ // Unregister from the focus object.
+ Reference<lang::XComponent> xComponent (container.first, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->removeEventListener(this);
+
+ // Tell all listeners that we are being disposed.
+ const FocusDescriptor& rFocusDescriptor (container.second);
+ for (auto const& listener : rFocusDescriptor.maListeners)
+ {
+ listener->disposing(aEvent);
+ }
+ }
+}
+
+// XContextChangeEventMultiplexer
+void SAL_CALL ContextChangeEventMultiplexer::addContextChangeEventListener (
+ const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener,
+ const css::uno::Reference<css::uno::XInterface>& rxEventFocus)
+{
+ if ( ! rxListener.is())
+ throw css::lang::IllegalArgumentException(
+ "can not add an empty reference",
+ static_cast<XWeak*>(this),
+ 0);
+
+ FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, true);
+ if (pFocusDescriptor != nullptr)
+ {
+ ListenerContainer& rContainer (pFocusDescriptor->maListeners);
+ if (::std::find(rContainer.begin(), rContainer.end(), rxListener) != rContainer.end())
+ {
+ // The listener was added for the same event focus
+ // previously. That is an error.
+ throw css::lang::IllegalArgumentException("listener added twice", static_cast<XWeak*>(this), 0);
+ }
+ rContainer.push_back(rxListener);
+ }
+
+ // Send out an initial event that informs the new listener about
+ // the current context.
+ if (!(rxEventFocus.is() && pFocusDescriptor!=nullptr))
+ return;
+
+ css::ui::ContextChangeEventObject aEvent (
+ nullptr,
+ pFocusDescriptor->msCurrentApplicationName,
+ pFocusDescriptor->msCurrentContextName);
+ rxListener->notifyContextChangeEvent(aEvent);
+}
+
+void SAL_CALL ContextChangeEventMultiplexer::removeContextChangeEventListener (
+ const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener,
+ const css::uno::Reference<css::uno::XInterface>& rxEventFocus)
+{
+ if ( ! rxListener.is())
+ throw css::lang::IllegalArgumentException(
+ "can not remove an empty reference",
+ static_cast<XWeak*>(this), 0);
+
+ FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, false);
+ if (pFocusDescriptor == nullptr)
+ return;
+
+ ListenerContainer& rContainer (pFocusDescriptor->maListeners);
+ const ListenerContainer::iterator iListener (
+ ::std::find(rContainer.begin(), rContainer.end(), rxListener));
+ if (iListener != rContainer.end())
+ {
+ rContainer.erase(iListener);
+
+ // We hold on to the focus descriptor even when the last listener has been removed.
+ // This allows us to keep track of the current context and send it to new listeners.
+ }
+
+}
+
+void SAL_CALL ContextChangeEventMultiplexer::removeAllContextChangeEventListeners (
+ const css::uno::Reference<css::ui::XContextChangeEventListener>& rxListener)
+{
+ if ( ! rxListener.is())
+ throw css::lang::IllegalArgumentException(
+ "can not remove an empty reference",
+ static_cast<XWeak*>(this), 0);
+
+ for (auto& rContainer : maListeners)
+ {
+ const ListenerContainer::iterator iListener (
+ ::std::find(rContainer.second.maListeners.begin(), rContainer.second.maListeners.end(), rxListener));
+ if (iListener != rContainer.second.maListeners.end())
+ {
+ rContainer.second.maListeners.erase(iListener);
+
+ // We hold on to the focus descriptor even when the last listener has been removed.
+ // This allows us to keep track of the current context and send it to new listeners.
+ }
+ }
+}
+
+void SAL_CALL ContextChangeEventMultiplexer::broadcastContextChangeEvent (
+ const css::ui::ContextChangeEventObject& rEventObject,
+ const css::uno::Reference<css::uno::XInterface>& rxEventFocus)
+{
+ // Remember the current context.
+ if (rxEventFocus.is())
+ {
+ FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, true);
+ if (pFocusDescriptor != nullptr)
+ {
+ pFocusDescriptor->msCurrentApplicationName = rEventObject.ApplicationName;
+ pFocusDescriptor->msCurrentContextName = rEventObject.ContextName;
+ }
+ }
+
+ BroadcastEventToSingleContainer(rEventObject, rxEventFocus);
+ if (rxEventFocus.is())
+ BroadcastEventToSingleContainer(rEventObject, nullptr);
+}
+
+void ContextChangeEventMultiplexer::BroadcastEventToSingleContainer (
+ const css::ui::ContextChangeEventObject& rEventObject,
+ const css::uno::Reference<css::uno::XInterface>& rxEventFocus)
+{
+ FocusDescriptor* pFocusDescriptor = GetFocusDescriptor(rxEventFocus, false);
+ if (pFocusDescriptor != nullptr)
+ {
+ // Create a copy of the listener container to avoid problems
+ // when one of the called listeners calls add... or remove...
+ ListenerContainer aContainer (pFocusDescriptor->maListeners);
+ for (auto const& listener : aContainer)
+ {
+ listener->notifyContextChangeEvent(rEventObject);
+ }
+ }
+}
+
+ContextChangeEventMultiplexer::FocusDescriptor* ContextChangeEventMultiplexer::GetFocusDescriptor (
+ const css::uno::Reference<css::uno::XInterface>& rxEventFocus,
+ const bool bCreateWhenMissing)
+{
+ ListenerMap::iterator iDescriptor (maListeners.find(rxEventFocus));
+ if (iDescriptor == maListeners.end() && bCreateWhenMissing)
+ {
+ // Listen for the focus being disposed.
+ Reference<lang::XComponent> xComponent (rxEventFocus, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->addEventListener(this);
+
+ // Create a new listener container for the event focus.
+ iDescriptor = maListeners.emplace(
+ rxEventFocus,
+ FocusDescriptor()).first;
+ }
+ if (iDescriptor != maListeners.end())
+ return &iDescriptor->second;
+ else
+ return nullptr;
+}
+
+OUString SAL_CALL ContextChangeEventMultiplexer::getImplementationName()
+{
+ return "org.apache.openoffice.comp.framework.ContextChangeEventMultiplexer";
+}
+
+sal_Bool SAL_CALL ContextChangeEventMultiplexer::supportsService ( const OUString& rsServiceName)
+{
+ return cppu::supportsService(this, rsServiceName);
+}
+
+css::uno::Sequence<OUString> SAL_CALL ContextChangeEventMultiplexer::getSupportedServiceNames()
+{
+ // it's a singleton, not a service
+ return css::uno::Sequence<OUString>();
+}
+
+void SAL_CALL ContextChangeEventMultiplexer::disposing ( const css::lang::EventObject& rEvent)
+{
+ ListenerMap::iterator iDescriptor (maListeners.find(rEvent.Source));
+
+ if (iDescriptor == maListeners.end())
+ {
+ OSL_ASSERT(iDescriptor != maListeners.end());
+ return;
+ }
+
+ // Should we notify the remaining listeners?
+
+ maListeners.erase(iDescriptor);
+}
+
+}
+
+namespace framework {
+
+// right now we assume there's one matching listener
+static uno::Reference<ui::XContextChangeEventListener> GetFirstListenerWith_ImplImpl(
+ css::uno::Reference<css::uno::XComponentContext> const & xComponentContext,
+ uno::Reference<uno::XInterface> const& xEventFocus,
+ std::function<bool (uno::Reference<ui::XContextChangeEventListener> const&)> const& rPredicate)
+{
+ assert(xEventFocus.is()); // in current usage it's a bug if the XController is null here
+ uno::Reference<ui::XContextChangeEventListener> xRet;
+
+ rtl::Reference<ContextChangeEventMultiplexer> pMultiplexer =
+ dynamic_cast<ContextChangeEventMultiplexer *>(ui::ContextChangeEventMultiplexer::get(xComponentContext).get());
+ assert(pMultiplexer);
+
+ ContextChangeEventMultiplexer::FocusDescriptor const*const pFocusDescriptor(
+ pMultiplexer->GetFocusDescriptor(xEventFocus, false));
+ if (!pFocusDescriptor)
+ return xRet;
+
+ for (auto & xListener : pFocusDescriptor->maListeners)
+ {
+ if (rPredicate(xListener))
+ {
+ assert(!xRet.is()); // generalize this if it is used for more than 1:1 mapping?
+ xRet = xListener;
+ }
+ }
+ return xRet;
+}
+
+namespace {
+
+struct Hook
+{
+ Hook() { g_pGetMultiplexerListener = &GetFirstListenerWith_ImplImpl; }
+ ~Hook() { g_pGetMultiplexerListener = nullptr; }
+};
+
+Hook g_hook;
+
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+org_apache_openoffice_comp_framework_ContextChangeEventMultiplexer_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ContextChangeEventMultiplexer());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/services/autorecovery.cxx b/framework/source/services/autorecovery.cxx
new file mode 100644
index 000000000..073f7094c
--- /dev/null
+++ b/framework/source/services/autorecovery.cxx
@@ -0,0 +1,4243 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <loadenv/loadenv.hxx>
+
+#include <strings.hrc>
+#include <classes/fwkresid.hxx>
+#include <properties.h>
+#include <targets.h>
+
+#include <helper/mischelper.hxx>
+
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
+#include <com/sun/star/frame/XLoadable.hpp>
+#include <com/sun/star/frame/XModel3.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XTitle.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/frame/XDesktop.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/util/XChangesNotifier.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentRecovery.hpp>
+#include <com/sun/star/document/XExtendedFilterDetection.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/task/XStatusIndicatorFactory.hpp>
+#include <com/sun/star/task/ErrorCodeIOException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/document/XDocumentEventListener.hpp>
+#include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
+#include <com/sun/star/util/XChangesListener.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+
+#include <comphelper/configuration.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/propshlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <o3tl/string_view.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <comphelper/multiinterfacecontainer3.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/sequence.hxx>
+#include <utility>
+#include <vcl/evntpost.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/timer.hxx>
+#include <unotools/pathoptions.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/tempfile.hxx>
+#include <ucbhelper/content.hxx>
+#include <svtools/sfxecode.hxx>
+
+#include <vcl/weld.hxx>
+#include <osl/file.hxx>
+#include <sal/log.hxx>
+#include <unotools/bootstrap.hxx>
+#include <unotools/configmgr.hxx>
+#include <svl/documentlockfile.hxx>
+#include <tools/urlobj.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Office/Recovery.hxx>
+#include <officecfg/Setup.hxx>
+
+using namespace css::uno;
+using namespace css::document;
+using namespace css::frame;
+using namespace css::lang;
+using namespace framework;
+
+namespace {
+
+/** @short hold all needed information for an asynchronous dispatch alive.
+
+ @descr Because some operations are forced to be executed asynchronously
+ (e.g. requested by our CrashSave/Recovery dialog) ... we must make sure
+ that this information won't be set as "normal" members of our AutoRecovery
+ instance. Otherwise they can disturb our normal AutoSave-timer handling.
+ e.g. it can be unclear then, which progress has to be used for storing documents...
+ */
+struct DispatchParams
+{
+public:
+ DispatchParams();
+ DispatchParams(const ::comphelper::SequenceAsHashMap& lArgs ,
+ const css::uno::Reference< css::uno::XInterface >& xOwner);
+
+ void forget();
+
+public:
+
+ /** @short can be set from outside and is provided to
+ our internal started operations.
+
+ @descr Normally we use the normal status indicator
+ of the document windows to show a progress.
+ But in case we are used by any special UI,
+ it can provide its own status indicator object
+ to us - so we use it instead of the normal one.
+ */
+ css::uno::Reference< css::task::XStatusIndicator > m_xProgress;
+
+ /** TODO document me */
+ OUString m_sSavePath;
+
+ /** @short define the current cache entry, which should be used for current
+ backup or cleanUp operation ... which is may be done asynchronous */
+ sal_Int32 m_nWorkingEntryID;
+
+ /** @short used for async operations, to prevent us from dying.
+
+ @descr If our dispatch() method was forced to start the
+ internal operation asynchronous... we send an event
+ to start and return immediately. But we must be sure that
+ our instance live if the event callback reach us.
+ So we hold a uno reference to ourself.
+ */
+ css::uno::Reference< css::uno::XInterface > m_xHoldRefForAsyncOpAlive;
+};
+
+/** These values are used as flags and represent the current state of a document.
+ Every state of the life time of a document has to be recognized here.
+
+ @attention Do not change (means reorganize) already used numbers.
+ There exists some code inside SVX, which uses the same numbers,
+ to analyze such document states.
+ Not the best design ... but may be it will be changed later .-)
+*/
+enum class DocState: sal_Int32
+{
+ /* TEMP STATES */
+
+ /// default state, if a document was new created or loaded
+ Unknown = 0,
+ /// modified against the original file
+ Modified = 1,
+ /// an active document can be postponed to be saved later.
+ Postponed = 2,
+ /// was already handled during one AutoSave/Recovery session.
+ Handled = 4,
+ /** an action was started (saving/loading) ... Can be interesting later if the process may be was interrupted by an exception. */
+ TrySave = 8,
+ TryLoadBackup = 16,
+ TryLoadOriginal = 32,
+
+ /* FINAL STATES */
+
+ /// the Auto/Emergency saved document is not usable any longer
+ Damaged = 64,
+ /// the Auto/Emergency saved document is not really up-to-date (some changes can be missing)
+ Incomplete = 128,
+ /// the Auto/Emergency saved document was processed successfully
+ Succeeded = 512
+};
+
+}
+
+template<> struct o3tl::typed_flags<DocState>: o3tl::is_typed_flags<DocState, 0x2FF> {};
+
+namespace {
+
+// TODO document me ... flag field
+// Emergency_Save and Recovery overwrites Auto_Save!
+enum class Job
+{
+ NoJob = 0,
+ AutoSave = 1,
+ EmergencySave = 2,
+ Recovery = 4,
+ EntryBackup = 8,
+ EntryCleanup = 16,
+ PrepareEmergencySave = 32,
+ SessionSave = 64,
+ SessionRestore = 128,
+ DisableAutorecovery = 256,
+ SetAutosaveState = 512,
+ SessionQuietQuit = 1024,
+ UserAutoSave = 2048
+};
+
+}
+
+template<> struct o3tl::typed_flags<Job>: o3tl::is_typed_flags<Job, 0xFFF> {};
+
+namespace {
+
+/**
+ implements the functionality of AutoSave and AutoRecovery
+ of documents - including features of an EmergencySave in
+ case a GPF occurs.
+ */
+typedef ::cppu::WeakComponentImplHelper<
+ css::lang::XServiceInfo,
+ css::frame::XDispatch,
+ css::document::XDocumentEventListener, // => css.lang.XEventListener
+ css::util::XChangesListener, // => css.lang.XEventListener
+ css::util::XModifyListener > // => css.lang.XEventListener
+ AutoRecovery_BASE;
+
+class AutoRecovery : private cppu::BaseMutex
+ , public AutoRecovery_BASE
+ , public ::cppu::OPropertySetHelper // => XPropertySet, XFastPropertySet, XMultiPropertySet
+{
+public:
+ /** @short indicates the results of a FAILURE_SAFE operation
+
+ @descr We must know, which reason was the real one in case
+ we couldn't copy a "failure document" to a user specified path.
+ We must know, if we can forget our cache entry or not.
+ */
+ enum EFailureSafeResult
+ {
+ E_COPIED,
+ E_ORIGINAL_FILE_MISSING,
+ E_WRONG_TARGET_PATH
+ };
+
+ // TODO document me
+ enum ETimerType
+ {
+ /** the timer should not be used next time */
+ E_DONT_START_TIMER,
+ /** timer (was/must be) started with normal AutoSaveTimeIntervall */
+ E_NORMAL_AUTOSAVE_INTERVALL,
+ /** timer must be started with special short time interval,
+ to poll for an user idle period */
+ E_POLL_FOR_USER_IDLE,
+ /** timer must be started with a very(!) short time interval,
+ to poll for the end of an user action, which does not allow saving documents in general */
+ E_POLL_TILL_AUTOSAVE_IS_ALLOWED,
+ /** don't start the timer - but calls the same action then before immediately again! */
+ E_CALL_ME_BACK
+ };
+
+ /** @short combine different information about one office document. */
+ struct TDocumentInfo
+ {
+ public:
+
+ TDocumentInfo()
+ : DocumentState (DocState::Unknown)
+ , UsedForSaving (false)
+ , ListenForModify (false)
+ , IgnoreClosing (false)
+ , ID (-1 )
+ {}
+
+ /** @short points to the document. */
+ css::uno::Reference< css::frame::XModel > Document;
+
+ /** @short knows, if the document is really modified since the last autosave,
+ or was postponed, because it was an active one etcpp...
+
+ @descr Because we have no CHANGE TRACKING mechanism, based on office document,
+ we implements it by ourself. We listen for MODIFIED events
+ of each document and update this state flag here.
+
+ Further we postpone saving of active documents, e.g. if the user
+ works currently on it. We wait for an idle period then...
+ */
+ DocState DocumentState;
+
+ /** Because our applications not ready for concurrent save requests at the same time,
+ we have suppress our own AutoSave for the moment, a document will be already saved
+ by others.
+ */
+ bool UsedForSaving;
+
+ /** For every user action, which modifies a document (e.g. key input) we get
+ a notification as XModifyListener. That seems to be a "performance issue" .-)
+ So we decided to listen for such modify events only for the time in which the document
+ was stored as temp. file and was not modified again by the user.
+ */
+ bool ListenForModify;
+
+ /** For SessionSave we must close all open documents by ourself.
+ But because we are listen for documents events, we get some ...
+ and deregister these documents from our configuration.
+ That's why we mark these documents as "Closed by ourself" so we can
+ ignore these "OnUnload" or disposing() events .-)
+ */
+ bool IgnoreClosing;
+
+ /** TODO: document me */
+ OUString OrgURL;
+ OUString FactoryURL;
+ OUString TemplateURL;
+
+ OUString OldTempURL;
+ OUString NewTempURL;
+
+ OUString AppModule; // e.g. com.sun.star.text.TextDocument - used to identify app module
+ OUString FactoryService; // the service to create a document of the module
+ OUString RealFilter; // real filter, which was used at loading time
+ OUString DefaultFilter; // supports saving of the default format without losing data
+ OUString Extension; // file extension of the default filter
+ OUString Title; // can be used as "DisplayName" on every recovery UI!
+ css::uno::Sequence< OUString >
+ ViewNames; // names of the view which were active at emergency-save time
+
+ sal_Int32 ID;
+ };
+
+ /** @short used to know every currently open document. */
+ typedef ::std::vector< TDocumentInfo > TDocumentList;
+
+// member
+
+private:
+
+ /** @short the global uno service manager.
+ @descr Must be used to create own needed services.
+ */
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ /** @short points to the underlying recovery configuration.
+ @descr This instance does not cache - it calls directly the
+ configuration API!
+ */
+ css::uno::Reference< css::container::XNameAccess > m_xRecoveryCFG;
+
+ /** @short proxy weak binding to forward Events to ourself without
+ an ownership cycle
+ */
+ css::uno::Reference< css::util::XChangesListener > m_xRecoveryCFGListener;
+
+ /** @short points to the used configuration package or.openoffice.Setup
+ @descr This instance does not cache - it calls directly the
+ configuration API!
+ */
+ css::uno::Reference< css::container::XNameAccess > m_xModuleCFG;
+
+ /** @short holds the global event broadcaster alive,
+ where we listen for new created documents.
+ */
+ css::uno::Reference< css::frame::XGlobalEventBroadcaster > m_xNewDocBroadcaster;
+
+ /** @short proxy weak binding to forward Events to ourself without
+ an ownership cycle
+ */
+ css::uno::Reference< css::document::XDocumentEventListener > m_xNewDocBroadcasterListener;
+
+ /** @short because we stop/restart listening sometimes, it's a good idea to know
+ if we already registered as listener .-)
+ */
+ bool m_bListenForDocEvents;
+ bool m_bListenForConfigChanges;
+
+ /** @short specify the time interval between two save actions.
+ @descr tools::Time is measured in [min].
+ */
+ sal_Int32 m_nAutoSaveTimeIntervall;
+
+ /** @short for an asynchronous operation we must know, if there is
+ at least one running job (may be asynchronous!).
+ */
+ Job m_eJob;
+
+ /** @short the timer, which is used to be informed about the next
+ saving time ...
+ @remark must lock SolarMutex to use
+ */
+ Timer m_aTimer;
+
+ /** @short make our dispatch asynchronous ... if required to do so! */
+ std::unique_ptr<vcl::EventPoster> m_xAsyncDispatcher;
+
+ /** @see DispatchParams
+ */
+ DispatchParams m_aDispatchParams;
+
+ /** @short indicates, which time period is currently used by the
+ internal timer.
+ */
+ ETimerType m_eTimerType;
+
+ /** @short this cache is used to hold all information about
+ recovery/emergency save documents alive.
+ */
+ TDocumentList m_lDocCache;
+
+ // TODO document me
+ sal_Int32 m_nIdPool;
+
+ /** @short contains all status listener registered at this instance.
+ */
+ comphelper::OMultiTypeInterfaceContainerHelperVar3<css::frame::XStatusListener, OUString> m_lListener;
+
+ /** @descr This member is used to prevent us against re-entrance problems.
+ A mutex can't help to prevent us from concurrent using of members
+ inside the same thread. But e.g. our internally used stl structures
+ are not threadsafe ... and furthermore they can't be used at the same time
+ for iteration and add/remove requests!
+ So we have to detect such states and ... show a warning.
+ May be there will be a better solution next time ... (copying the cache temp.
+ bevor using).
+
+ And further it's not possible to use a simple boolean value here.
+ Because if more than one operation iterates over the same stl container ...
+ (only to modify it's elements but don't add new or removing existing ones!)
+ it should be possible doing so. But we must guarantee that the last operation reset
+ this lock ... not the first one ! So we use a "ref count" mechanism for that."
+ */
+ sal_Int32 m_nDocCacheLock;
+
+ /** @descr These members are used to check the minimum disc space, which must exists
+ to start the corresponding operation.
+ */
+ sal_Int32 m_nMinSpaceDocSave;
+ sal_Int32 m_nMinSpaceConfigSave;
+
+// interface
+
+public:
+
+ explicit AutoRecovery(css::uno::Reference< css::uno::XComponentContext > xContext);
+ virtual ~AutoRecovery( ) override;
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.AutoRecovery";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.frame.AutoRecovery"};
+ }
+
+ // XInterface
+ virtual void SAL_CALL acquire() noexcept override
+ { OWeakObject::acquire(); }
+ virtual void SAL_CALL release() noexcept override
+ { OWeakObject::release(); }
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& type) override;
+
+ /// Initialization function after having acquire()'d.
+ void initListeners();
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+
+ // css.frame.XDispatch
+ virtual void SAL_CALL dispatch(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments) override;
+
+ virtual void SAL_CALL addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
+ const css::util::URL& aURL ) override;
+
+ virtual void SAL_CALL removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
+ const css::util::URL& aURL ) override;
+
+ // css.document.XDocumentEventListener
+ /** @short informs about created/opened documents.
+
+ @descr Every new opened/created document will be saved internally
+ so it can be checked if it's modified. This modified state
+ is used later to decide, if it must be saved or not.
+
+ @param aEvent
+ points to the new created/opened document.
+ */
+ virtual void SAL_CALL documentEventOccured(const css::document::DocumentEvent& aEvent) override;
+
+ // css.util.XChangesListener
+ virtual void SAL_CALL changesOccurred(const css::util::ChangesEvent& aEvent) override;
+
+ // css.util.XModifyListener
+ virtual void SAL_CALL modified(const css::lang::EventObject& aEvent) override;
+
+ // css.lang.XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override;
+
+protected:
+
+ // OPropertySetHelper
+
+ virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any& aConvertedValue,
+ css::uno::Any& aOldValue ,
+ sal_Int32 nHandle ,
+ const css::uno::Any& aValue ) override;
+
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle,
+ const css::uno::Any& aValue ) override;
+ using cppu::OPropertySetHelper::getFastPropertyValue;
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& aValue ,
+ sal_Int32 nHandle) const override;
+
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
+
+private:
+ virtual void SAL_CALL disposing() final override;
+
+ /** @short open the underlying configuration.
+
+ @descr This method must be called every time
+ a configuration call is needed. Because
+ method works together with the member
+ m_xCFG, open it on demand and cache it
+ afterwards.
+
+ @throw [com.sun.star.uno.RuntimeException]
+ if config could not be opened successfully!
+
+ @threadsafe
+ */
+ void implts_openConfig();
+
+ /** @short read the underlying configuration.
+
+ @descr After that we know the initial state - means:
+ - if AutoSave was enabled by the user
+ - which time interval has to be used
+ - which recovery entries may already exists
+
+ @throw [com.sun.star.uno.RuntimeException]
+ if config could not be opened or read successfully!
+
+ @threadsafe
+ */
+ void implts_readConfig();
+
+ /** @short read the underlying configuration...
+
+ @descr ... but only keys related to the AutoSave mechanism.
+ Means: State and Timer interval.
+ E.g. the recovery list is not addressed here.
+
+ @throw [com.sun.star.uno.RuntimeException]
+ if config could not be opened or read successfully!
+
+ @threadsafe
+ */
+ void implts_readAutoSaveConfig();
+
+ // TODO document me
+ void implts_flushConfigItem(const AutoRecovery::TDocumentInfo& rInfo ,
+ bool bRemoveIt = false);
+
+ // TODO document me
+ void implts_startListening();
+ void implts_startModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo);
+
+ // TODO document me
+ void implts_stopListening();
+ void implts_stopModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo);
+
+ /** @short stops and may be(!) restarts the timer.
+
+ @descr A running timer is stopped every time here.
+ But starting depends from the different internal
+ timer variables (e.g. AutoSaveEnabled, AutoSaveTimeIntervall,
+ TimerType etcpp.)
+
+ @throw [com.sun.star.uno.RuntimeException]
+ if timer could not be stopped or started!
+
+ @threadsafe
+ */
+ void implts_updateTimer();
+
+ /** @short stop the timer.
+
+ @descr Double calls will be ignored - means we do
+ nothing here, if the timer is already disabled.
+
+ @throw [com.sun.star.uno.RuntimeException]
+ if timer could not be stopped!
+
+ @threadsafe
+ */
+ void implts_stopTimer();
+
+ /** @short callback of our internal timer.
+ */
+ DECL_LINK(implts_timerExpired, Timer*, void);
+
+ /** @short makes our dispatch() method asynchronous!
+ */
+ DECL_LINK(implts_asyncDispatch, LinkParamNone*, void);
+
+ /** @short implements the dispatch real. */
+ void implts_dispatch(const DispatchParams& aParams);
+
+ /** @short validate new detected document and add it into the internal
+ document list.
+
+ @descr This method should be called only if it's clear that a new
+ document was opened/created during office runtime.
+ This method checks if it's a top level document (means not an embedded one).
+ Only such top level documents can be recognized by this auto save mechanism.
+
+ @param xDocument
+ the new document, which should be checked and registered.
+
+ @threadsafe
+ */
+ void implts_registerDocument(const css::uno::Reference< css::frame::XModel3 >& xDocument);
+
+ /** @short remove the specified document from our internal document list.
+
+ @param xDocument
+ the new document, which should be deregistered.
+
+ @param bStopListening
+ sal_False: must be used in case this method is called within disposing() of the document,
+ where it makes no sense to deregister our listener. The container dies...
+ sal_True : must be used in case this method is used on "deregistration" of this document, where
+ we must deregister our listener .-)
+
+ @threadsafe
+ */
+ void implts_deregisterDocument(const css::uno::Reference< css::frame::XModel >& xDocument ,
+ bool bStopListening = true);
+
+ // TODO document me
+ void implts_markDocumentModifiedAgainstLastBackup(const css::uno::Reference< css::frame::XModel >& xDocument);
+
+ // TODO document me
+ void implts_updateModifiedState(const css::uno::Reference< css::frame::XModel >& xDocument);
+
+ // TODO document me
+ void implts_updateDocumentUsedForSavingState(const css::uno::Reference< css::frame::XModel >& xDocument ,
+ bool bSaveInProgress);
+
+ // TODO document me
+ void implts_markDocumentAsSaved(const css::uno::Reference< css::frame::XModel >& xDocument);
+
+ /** @short search a document inside given list.
+
+ @param rList
+ reference to a vector, which can contain such
+ document.
+
+ @param xDocument
+ the document, which should be located inside the
+ given list.
+
+ @return [TDocumentList::iterator]
+ which points to the located document.
+ If document does not exists - it's set to
+ rList.end()!
+ */
+ static TDocumentList::iterator impl_searchDocument( AutoRecovery::TDocumentList& rList ,
+ const css::uno::Reference< css::frame::XModel >& xDocument);
+
+ /** TODO document me */
+ void implts_changeAllDocVisibility(bool bVisible);
+ void implts_prepareSessionShutdown();
+
+ /** @short save all current opened documents to a specific
+ backup directory.
+
+ @descr Only really changed documents will be saved here.
+
+ Further this method returns a suggestion, if and how it should
+ be called again. May be some documents was not saved yet
+ and must wait for an user idle period ...
+
+ @param bAllowUserIdleLoop
+ Because this method is used for different uses cases, it must
+ know, which actions are allowed or not.
+ Job::AutoSave =>
+ If a document is the most active one, saving it
+ will be postponed if there exists other unsaved
+ documents. This feature was implemented, because
+ we don't wish to disturb the user on it's work.
+ ... bAllowUserIdleLoop should be set to sal_True
+ Job::EmergencySave / Job::SessionSave =>
+ Here we must finish our work ASAP! It's not allowed
+ to postpone any document.
+ ... bAllowUserIdleLoop must(!) be set to sal_False
+
+ @param pParams
+ sometimes this method is required inside an external dispatch request.
+ The it contains some special environment variables, which overwrites
+ our normal environment.
+ AutoSave => pParams == 0
+ SessionSave/CrashSave => pParams != 0
+
+ @return A suggestion, how the timer (if it's not already disabled!)
+ should be restarted to fulfill the requirements.
+
+ @threadsafe
+ */
+ AutoRecovery::ETimerType implts_saveDocs( bool bAllowUserIdleLoop,
+ bool bRemoveLockFiles,
+ const DispatchParams* pParams = nullptr);
+
+ /** @short save one of the current documents to a specific
+ backup directory.
+
+ @descr It:
+ - defines a new(!) unique temp file name
+ - save the new temp file
+ - remove the old temp file
+ - patch the given info struct
+ - and return errors.
+
+ It does not:
+ - patch the configuration.
+
+ Note further: it patches the info struct
+ more than ones. E.g. the new temp URL is set
+ before the file is saved. And the old URL is removed
+ only if removing of the old file was successful.
+ If this method returns without an exception - everything
+ was OK. Otherwise the info struct can be analyzed to
+ get more information, e.g. when the problem occurs.
+
+ @param sBackupPath
+ the base path for saving such temp files.
+
+ @param rInfo
+ points to an information structure, where
+ e.g. the document, its modified state, the count
+ of autosave-retries etcpp. exists.
+ It's used also to return the new temp file name
+ and some other state values!
+
+ @threadsafe
+ */
+ void implts_saveOneDoc(const OUString& sBackupPath ,
+ AutoRecovery::TDocumentInfo& rInfo ,
+ const css::uno::Reference< css::task::XStatusIndicator >& xExternalProgress);
+
+ /** @short recovery all documents, which was saved during
+ a crash before.
+
+ @return A suggestion, how this method must be called back!
+
+ @threadsafe
+ */
+ AutoRecovery::ETimerType implts_openDocs(const DispatchParams& aParams);
+
+ // TODO document me
+ void implts_openOneDoc(const OUString& sURL ,
+ utl::MediaDescriptor& lDescriptor,
+ AutoRecovery::TDocumentInfo& rInfo );
+
+ // TODO document me
+ void implts_generateNewTempURL(const OUString& sBackupPath ,
+ utl::MediaDescriptor& rMediaDescriptor,
+ AutoRecovery::TDocumentInfo& rInfo );
+
+ /** @short notifies all interested listener about the current state
+ of the currently running operation.
+
+ @descr We support different set's of functions. Job::AutoSave, Job::EmergencySave,
+ Job::Recovery ... etcpp.
+ Listener can register itself for any type of supported
+ functionality ... but not for document URL's in special.
+
+ @param eJob
+ is used to know, which set of listener we must notify.
+
+ @param aEvent
+ describe the event more in detail.
+
+ @threadsafe
+ */
+ void implts_informListener( Job eJob ,
+ const css::frame::FeatureStateEvent& aEvent);
+
+ /** short create a feature event struct, which can be send
+ to any interested listener.
+
+ @param eJob
+ describe the current running operation
+ Job::AutoSave, Job::EmergencySave, Job::Recovery
+
+ @param sEventType
+ describe the type of this event
+ START, STOP, UPDATE
+
+ @param pInfo
+ if sOperation is an update, this parameter must be different from NULL
+ and is used to send information regarding the current handled document.
+
+ @return [css::frame::FeatureStateEvent]
+ the event structure for sending.
+ */
+ static css::frame::FeatureStateEvent implst_createFeatureStateEvent( Job eJob ,
+ const OUString& sEventType,
+ AutoRecovery::TDocumentInfo const * pInfo );
+
+ class ListenerInformer
+ {
+ private:
+ AutoRecovery &m_rRecovery;
+ Job m_eJob;
+ bool m_bStopped;
+ public:
+ ListenerInformer(AutoRecovery &rRecovery, Job eJob)
+ : m_rRecovery(rRecovery), m_eJob(eJob), m_bStopped(false)
+ {
+ }
+ void start();
+ void stop();
+ ~ListenerInformer()
+ {
+ stop();
+ }
+ };
+
+ // TODO document me
+ void implts_resetHandleStates();
+
+ // TODO document me
+ void implts_specifyDefaultFilterAndExtension(AutoRecovery::TDocumentInfo& rInfo);
+
+ // TODO document me
+ void implts_specifyAppModuleAndFactory(AutoRecovery::TDocumentInfo& rInfo);
+
+ /** retrieves the names of all active views of the given document
+ @param rInfo
+ the document info, whose <code>Document</code> member must not be <NULL/>.
+ */
+ void implts_collectActiveViewNames( AutoRecovery::TDocumentInfo& rInfo );
+
+ /** updates the configuration so that for all documents, their current view/names are stored
+ */
+ void implts_persistAllActiveViewNames();
+
+ // TODO document me
+ void implts_prepareEmergencySave();
+
+ // TODO document me
+ void implts_doEmergencySave(const DispatchParams& aParams);
+
+ // TODO document me
+ void implts_doRecovery(const DispatchParams& aParams);
+
+ // TODO document me
+ void implts_doSessionSave(const DispatchParams& aParams);
+
+ // TODO document me
+ void implts_doSessionQuietQuit();
+
+ // TODO document me
+ void implts_doSessionRestore(const DispatchParams& aParams);
+
+ // TODO document me
+ void implts_backupWorkingEntry(const DispatchParams& aParams);
+
+ // TODO document me
+ void implts_cleanUpWorkingEntry(const DispatchParams& aParams);
+
+ /** try to make sure that all changed config items (not our used
+ config access only) will be flushed back to disc.
+
+ E.g. our svtools::ConfigItems() has to be flushed explicitly .-(
+
+ Note: This method can't fail. Flushing of config entries is an
+ optional feature. Errors can be ignored.
+ */
+ void impl_flushALLConfigChanges();
+
+ // TODO document me
+ AutoRecovery::EFailureSafeResult implts_copyFile(const OUString& sSource ,
+ const OUString& sTargetPath,
+ const OUString& sTargetName);
+
+ /** @short converts m_eJob into a job description, which
+ can be used to inform an outside listener
+ about the current running operation
+
+ @param eJob
+ describe the current running operation
+ Job::AutoSave, Job::EmergencySave, Job::Recovery
+
+ @return [string]
+ a suitable job description of form:
+ vnd.sun.star.autorecovery:/do...
+ */
+ static OUString implst_getJobDescription(Job eJob);
+
+ /** @short map the given URL to an internal int representation.
+
+ @param aURL
+ the url, which describe the next starting or may be already running
+ operation.
+
+ @return [long]
+ the internal int representation
+ see enum class Job
+ */
+ static Job implst_classifyJob(const css::util::URL& aURL);
+
+ /// TODO
+ void implts_verifyCacheAgainstDesktopDocumentList();
+
+ /// TODO document me
+ bool impl_enoughDiscSpace(sal_Int32 nRequiredSpace);
+
+ /// TODO document me
+ static void impl_showFullDiscError();
+
+ /** @short try to create/use a progress and set it inside the
+ environment.
+
+ @descr The problem behind: There exists different use case of this method.
+ a) An external progress is provided by our CrashSave or Recovery dialog.
+ b) We must create our own progress e.g. for an AutoSave
+ c) Sometimes our application filters don't use the progress
+ provided by the MediaDescriptor. They use the Frame every time to create
+ its own progress. So we implemented a HACK for these and now we set
+ an InterceptedProgress there for the time WE use this frame for loading/storing documents .-)
+
+ @param xNewFrame
+ must be set only in case WE create a new frame (e.g. for loading documents
+ on session restore or recovery). Then search for a frame using rInfo.Document must
+ be suppressed and xFrame must be preferred instead .-)
+
+ @param rInfo
+ used e.g. to find the frame corresponding to a document.
+ This frame must be used to create a new progress e.g. for an AutoSave.
+
+ @param rArgs
+ is used to set the new created progress as parameter on these set.
+ */
+ void impl_establishProgress(const AutoRecovery::TDocumentInfo& rInfo ,
+ utl::MediaDescriptor& rArgs ,
+ const css::uno::Reference< css::frame::XFrame >& xNewFrame);
+
+ void impl_forgetProgress(const AutoRecovery::TDocumentInfo& rInfo ,
+ utl::MediaDescriptor& rArgs ,
+ const css::uno::Reference< css::frame::XFrame >& xNewFrame);
+
+ /** try to remove the specified file from disc.
+
+ Every URL supported by our UCB component can be used here.
+ Further it doesn't matter if the file really exists or not.
+ Because removing a non existent file will have the same
+ result at the end... a non existing file .-)
+
+ On the other side removing of files from disc is an optional
+ feature. If we are not able doing so... it's not a real problem.
+ Ok - users disc place will be smaller then... but we should produce
+ a crash during crash save because we can't delete a temporary file only!
+
+ @param sURL
+ the url of the file, which should be removed.
+ */
+ void st_impl_removeFile(const OUString& sURL);
+
+ /** try to remove ".lock" file from disc if office will be terminated
+ not using the official way .-)
+
+ This method has to be handled "optional". So every error inside
+ has to be ignored ! This method CAN NOT FAIL ... it can forget something only .-)
+ */
+ void st_impl_removeLockFile();
+};
+
+// recovery.xcu
+constexpr OUStringLiteral CFG_PACKAGE_RECOVERY = u"org.openoffice.Office.Recovery/";
+
+const char CFG_ENTRY_AUTOSAVE_ENABLED[] = "AutoSave/Enabled";
+const char CFG_ENTRY_AUTOSAVE_TIMEINTERVALL[] = "AutoSave/TimeIntervall"; //sic!
+
+constexpr OUStringLiteral CFG_ENTRY_REALDEFAULTFILTER = u"ooSetupFactoryActualFilter";
+
+constexpr OUStringLiteral CFG_ENTRY_PROP_TEMPURL = u"TempURL";
+constexpr OUStringLiteral CFG_ENTRY_PROP_ORIGINALURL = u"OriginalURL";
+constexpr OUStringLiteral CFG_ENTRY_PROP_TEMPLATEURL = u"TemplateURL";
+constexpr OUStringLiteral CFG_ENTRY_PROP_FACTORYURL = u"FactoryURL";
+constexpr OUStringLiteral CFG_ENTRY_PROP_MODULE = u"Module";
+constexpr OUStringLiteral CFG_ENTRY_PROP_DOCUMENTSTATE = u"DocumentState";
+constexpr OUStringLiteral CFG_ENTRY_PROP_FILTER = u"Filter";
+constexpr OUStringLiteral CFG_ENTRY_PROP_TITLE = u"Title";
+constexpr OUStringLiteral CFG_ENTRY_PROP_ID = u"ID";
+constexpr OUStringLiteral CFG_ENTRY_PROP_VIEWNAMES = u"ViewNames";
+
+constexpr OUStringLiteral FILTER_PROP_TYPE = u"Type";
+constexpr OUStringLiteral TYPE_PROP_EXTENSIONS = u"Extensions";
+
+// setup.xcu
+constexpr OUStringLiteral CFG_ENTRY_PROP_EMPTYDOCUMENTURL = u"ooSetupFactoryEmptyDocumentURL";
+constexpr OUStringLiteral CFG_ENTRY_PROP_FACTORYSERVICE = u"ooSetupFactoryDocumentService";
+
+const char EVENT_ON_NEW[] = "OnNew";
+const char EVENT_ON_LOAD[] = "OnLoad";
+const char EVENT_ON_UNLOAD[] = "OnUnload";
+const char EVENT_ON_MODIFYCHANGED[] = "OnModifyChanged";
+const char EVENT_ON_SAVE[] = "OnSave";
+const char EVENT_ON_SAVEAS[] = "OnSaveAs";
+const char EVENT_ON_SAVETO[] = "OnCopyTo";
+const char EVENT_ON_SAVEDONE[] = "OnSaveDone";
+const char EVENT_ON_SAVEASDONE[] = "OnSaveAsDone";
+const char EVENT_ON_SAVETODONE[] = "OnCopyToDone";
+const char EVENT_ON_SAVEFAILED[] = "OnSaveFailed";
+const char EVENT_ON_SAVEASFAILED[] = "OnSaveAsFailed";
+const char EVENT_ON_SAVETOFAILED[] = "OnCopyToFailed";
+
+constexpr OUStringLiteral RECOVERY_ITEM_BASE_IDENTIFIER = u"recovery_item_";
+
+const char CMD_PROTOCOL[] = "vnd.sun.star.autorecovery:";
+
+const char CMD_DO_AUTO_SAVE[] = "/doAutoSave"; // force AutoSave ignoring the AutoSave timer
+const char CMD_DO_PREPARE_EMERGENCY_SAVE[] = "/doPrepareEmergencySave"; // prepare the office for the following EmergencySave step (hide windows etcpp.)
+const char CMD_DO_EMERGENCY_SAVE[] = "/doEmergencySave"; // do EmergencySave on crash
+const char CMD_DO_RECOVERY[] = "/doAutoRecovery"; // recover all crashed documents
+const char CMD_DO_ENTRY_BACKUP[] = "/doEntryBackup"; // try to store a temp or original file to a user defined location
+const char CMD_DO_ENTRY_CLEANUP[] = "/doEntryCleanUp"; // remove the specified entry from the recovery cache
+const char CMD_DO_SESSION_SAVE[] = "/doSessionSave"; // save all open documents if e.g. a window manager closes an user session
+const char CMD_DO_SESSION_QUIET_QUIT[] = "/doSessionQuietQuit"; // let the current session be quietly closed ( the saving should be done using doSessionSave previously ) if e.g. a window manager closes an user session
+const char CMD_DO_SESSION_RESTORE[] = "/doSessionRestore"; // restore a saved user session from disc
+const char CMD_DO_DISABLE_RECOVERY[] = "/disableRecovery"; // disable recovery and auto save (!) temp. for this office session
+const char CMD_DO_SET_AUTOSAVE_STATE[] = "/setAutoSaveState"; // disable/enable auto save (not crash save) for this office session
+
+constexpr OUStringLiteral REFERRER_USER = u"private:user";
+
+constexpr OUStringLiteral PROP_DISPATCH_ASYNCHRON = u"DispatchAsynchron";
+constexpr OUStringLiteral PROP_PROGRESS = u"StatusIndicator";
+constexpr OUStringLiteral PROP_SAVEPATH = u"SavePath";
+constexpr OUStringLiteral PROP_ENTRY_ID = u"EntryID";
+constexpr OUStringLiteral PROP_AUTOSAVE_STATE = u"AutoSaveState";
+
+constexpr OUStringLiteral OPERATION_START = u"start";
+constexpr OUStringLiteral OPERATION_STOP = u"stop";
+constexpr OUStringLiteral OPERATION_UPDATE = u"update";
+
+const sal_Int32 MIN_DISCSPACE_DOCSAVE = 5; // [MB]
+const sal_Int32 MIN_DISCSPACE_CONFIGSAVE = 1; // [MB]
+const sal_Int32 RETRY_STORE_ON_FULL_DISC_FOREVER = 300; // not forever ... but often enough .-)
+const sal_Int32 RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL = 3; // in case FULL DISC does not seem the real problem
+const sal_Int32 GIVE_UP_RETRY = 1; // in case FULL DISC does not seem the real problem
+
+#define SAVE_IN_PROGRESS true
+#define SAVE_FINISHED false
+
+#define LOCK_FOR_CACHE_ADD_REMOVE true
+#define LOCK_FOR_CACHE_USE false
+
+#define MIN_TIME_FOR_USER_IDLE 10000 // 10s user idle
+
+// enable the following defines in case you wish to simulate a full disc for debug purposes .-)
+
+// this define throws every time a document is stored or a configuration change
+// should be flushed an exception ... so the special error handler for this scenario is triggered
+// #define TRIGGER_FULL_DISC_CHECK
+
+// force "return sal_False" for the method impl_enoughDiscSpace().
+// #define SIMULATE_FULL_DISC
+
+class CacheLockGuard
+{
+ private:
+
+ // holds the outside caller alive, so it's shared resources
+ // are valid every time
+ css::uno::Reference< css::uno::XInterface > m_xOwner;
+
+ // mutex shared with outside caller!
+ osl::Mutex& m_rSharedMutex;
+
+ // this variable knows the state of the "cache lock"
+ sal_Int32& m_rCacheLock;
+
+ // to prevent increasing/decreasing of m_rCacheLock more than once
+ // we must know if THIS guard has an actual lock set there!
+ bool m_bLockedByThisGuard;
+
+ public:
+
+ CacheLockGuard(AutoRecovery* pOwner ,
+ osl::Mutex& rMutex ,
+ sal_Int32& rCacheLock ,
+ bool bLockForAddRemoveVectorItems);
+ ~CacheLockGuard();
+
+ void lock(bool bLockForAddRemoveVectorItems);
+ void unlock();
+};
+
+CacheLockGuard::CacheLockGuard(AutoRecovery* pOwner ,
+ osl::Mutex& rMutex ,
+ sal_Int32& rCacheLock ,
+ bool bLockForAddRemoveVectorItems)
+ : m_xOwner (static_cast< css::frame::XDispatch* >(pOwner))
+ , m_rSharedMutex (rMutex )
+ , m_rCacheLock (rCacheLock )
+ , m_bLockedByThisGuard(false )
+{
+ lock(bLockForAddRemoveVectorItems);
+}
+
+CacheLockGuard::~CacheLockGuard()
+{
+ unlock();
+ m_xOwner.clear();
+}
+
+void CacheLockGuard::lock(bool bLockForAddRemoveVectorItems)
+{
+ /* SAFE */
+ osl::MutexGuard g(m_rSharedMutex);
+
+ if (m_bLockedByThisGuard)
+ return;
+
+ // This cache lock is needed only to prevent us from removing/adding
+ // items from/into the recovery cache ... during it's used at another code place
+ // for iterating .-)
+
+ // Modifying of item properties is allowed and sometimes needed!
+ // So we should detect only the dangerous state of concurrent add/remove
+ // requests and throw an exception then ... which can of course break the whole
+ // operation. On the other side a crash reasoned by an invalid stl iterator
+ // will have the same effect .-)
+
+ if ( (m_rCacheLock > 0) && bLockForAddRemoveVectorItems )
+ {
+ OSL_FAIL("Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp.");
+ throw css::uno::RuntimeException(
+ "Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp.",
+ m_xOwner);
+ }
+
+ ++m_rCacheLock;
+ m_bLockedByThisGuard = true;
+ /* SAFE */
+}
+
+void CacheLockGuard::unlock()
+{
+ /* SAFE */
+ osl::MutexGuard g(m_rSharedMutex);
+
+ if ( ! m_bLockedByThisGuard)
+ return;
+
+ --m_rCacheLock;
+ m_bLockedByThisGuard = false;
+
+ if (m_rCacheLock < 0)
+ {
+ OSL_FAIL("Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)");
+ throw css::uno::RuntimeException(
+ "Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)",
+ m_xOwner);
+ }
+ /* SAFE */
+}
+
+DispatchParams::DispatchParams()
+ : m_nWorkingEntryID(-1)
+{
+};
+
+DispatchParams::DispatchParams(const ::comphelper::SequenceAsHashMap& lArgs ,
+ const css::uno::Reference< css::uno::XInterface >& xOwner)
+{
+ m_nWorkingEntryID = lArgs.getUnpackedValueOrDefault(PROP_ENTRY_ID, sal_Int32(-1) );
+ m_xProgress = lArgs.getUnpackedValueOrDefault(PROP_PROGRESS, css::uno::Reference< css::task::XStatusIndicator >());
+ m_sSavePath = lArgs.getUnpackedValueOrDefault(PROP_SAVEPATH, OUString() );
+ m_xHoldRefForAsyncOpAlive = xOwner;
+};
+
+void DispatchParams::forget()
+{
+ m_sSavePath.clear();
+ m_nWorkingEntryID = -1;
+ m_xProgress.clear();
+ m_xHoldRefForAsyncOpAlive.clear();
+};
+
+AutoRecovery::AutoRecovery(css::uno::Reference< css::uno::XComponentContext > xContext)
+ : AutoRecovery_BASE (m_aMutex)
+ , ::cppu::OPropertySetHelper(cppu::WeakComponentImplHelperBase::rBHelper)
+ , m_xContext (std::move(xContext ))
+ , m_bListenForDocEvents (false )
+ , m_bListenForConfigChanges (false )
+ , m_nAutoSaveTimeIntervall (0 )
+ , m_eJob (Job::NoJob)
+ , m_aTimer( "framework::AutoRecovery m_aTimer" )
+ , m_xAsyncDispatcher (new vcl::EventPoster( LINK( this, AutoRecovery, implts_asyncDispatch ) ))
+ , m_eTimerType (E_DONT_START_TIMER )
+ , m_nIdPool (0 )
+ , m_lListener (cppu::WeakComponentImplHelperBase::rBHelper.rMutex)
+ , m_nDocCacheLock (0 )
+ , m_nMinSpaceDocSave (MIN_DISCSPACE_DOCSAVE )
+ , m_nMinSpaceConfigSave (MIN_DISCSPACE_CONFIGSAVE )
+{
+}
+
+void AutoRecovery::initListeners()
+{
+ // read configuration to know if autosave/recovery is on/off etcpp...
+ implts_readConfig();
+
+ implts_startListening();
+
+ // establish callback for our internal used timer.
+ // Note: Its only active, if the timer will be started ...
+ SolarMutexGuard g;
+ m_aTimer.SetInvokeHandler(LINK(this, AutoRecovery, implts_timerExpired));
+}
+
+AutoRecovery::~AutoRecovery()
+{
+ assert(!m_aTimer.IsActive());
+}
+
+void AutoRecovery::disposing()
+{
+ implts_stopTimer();
+ SolarMutexGuard g;
+ m_xAsyncDispatcher.reset();
+}
+
+Any SAL_CALL AutoRecovery::queryInterface( const css::uno::Type& _rType )
+{
+ Any aRet = AutoRecovery_BASE::queryInterface( _rType );
+ if ( !aRet.hasValue() )
+ aRet = OPropertySetHelper::queryInterface( _rType );
+ return aRet;
+}
+
+Sequence< css::uno::Type > SAL_CALL AutoRecovery::getTypes( )
+{
+ return comphelper::concatSequences(
+ AutoRecovery_BASE::getTypes(),
+ ::cppu::OPropertySetHelper::getTypes()
+ );
+}
+
+void SAL_CALL AutoRecovery::dispatch(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
+{
+ SAL_INFO("fwk.autorecovery", "AutoRecovery::dispatch() starts ..." << aURL.Complete);
+
+ // valid request ?
+ Job eNewJob = AutoRecovery::implst_classifyJob(aURL);
+ if (eNewJob == Job::NoJob)
+ return;
+
+ bool bAsync;
+ DispatchParams aParams;
+ /* SAFE */ {
+ osl::ClearableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ // still running operation ... ignoring Job::AutoSave.
+ // All other requests has higher prio!
+ if (
+ ( m_eJob != Job::NoJob ) &&
+ ((m_eJob & Job::AutoSave ) != Job::AutoSave)
+ )
+ {
+ SAL_INFO("fwk.autorecovery", "AutoRecovery::dispatch(): There is already an asynchronous dispatch() running. New request will be ignored!");
+ return;
+ }
+
+ ::comphelper::SequenceAsHashMap lArgs(lArguments);
+
+ // check if somewhere wish to disable recovery temp. for this office session
+ // This can be done immediately... must not been done asynchronous.
+ if ((eNewJob & Job::DisableAutorecovery) == Job::DisableAutorecovery)
+ {
+ // it's important to set a flag internally, so AutoRecovery will be suppressed - even if it's requested.
+ m_eJob |= eNewJob;
+ implts_stopTimer();
+ implts_stopListening();
+ return;
+ }
+
+ // disable/enable AutoSave for this office session only
+ // independent from the configuration entry.
+ if ((eNewJob & Job::SetAutosaveState) == Job::SetAutosaveState)
+ {
+ bool bOn = lArgs.getUnpackedValueOrDefault(PROP_AUTOSAVE_STATE, true);
+ if (bOn)
+ {
+ // don't enable AutoSave hardly !
+ // reload configuration to know the current state.
+ implts_readAutoSaveConfig();
+ g.clear();
+ implts_updateTimer();
+ // can it happen that might be the listener was stopped? .-)
+ // make sure it runs always... even if AutoSave itself was disabled temporarily.
+ implts_startListening();
+ }
+ else
+ {
+ implts_stopTimer();
+ m_eJob &= ~Job::AutoSave;
+ m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
+ }
+ return;
+ }
+
+ m_eJob |= eNewJob;
+
+ bAsync = lArgs.getUnpackedValueOrDefault(PROP_DISPATCH_ASYNCHRON, false);
+ aParams = DispatchParams(lArgs, static_cast< css::frame::XDispatch* >(this));
+
+ // Hold this instance alive till the asynchronous operation will be finished.
+ if (bAsync)
+ m_aDispatchParams = aParams;
+
+ } /* SAFE */
+
+ if (bAsync)
+ m_xAsyncDispatcher->Post();
+ else
+ implts_dispatch(aParams);
+}
+
+void AutoRecovery::ListenerInformer::start()
+{
+ m_rRecovery.implts_informListener(m_eJob,
+ AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_START, nullptr));
+}
+
+void AutoRecovery::ListenerInformer::stop()
+{
+ if (m_bStopped)
+ return;
+ m_rRecovery.implts_informListener(m_eJob,
+ AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_STOP, nullptr));
+ m_bStopped = true;
+}
+
+void AutoRecovery::implts_dispatch(const DispatchParams& aParams)
+{
+ Job eJob;
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ eJob = m_eJob;
+ } /* SAFE */
+
+ // in case a new dispatch overwrites a may ba active AutoSave session
+ // we must restore this session later. see below ...
+ bool bWasAutoSaveActive = ((eJob & Job::AutoSave) == Job::AutoSave);
+ bool bWasUserAutoSaveActive =
+ ((eJob & Job::UserAutoSave) == Job::UserAutoSave);
+
+ // On the other side it makes no sense to reactivate the AutoSave operation
+ // if the new dispatch indicates a final decision...
+ // E.g. an EmergencySave/SessionSave indicates the end of life of the current office session.
+ // It makes no sense to reactivate an AutoSave then.
+ // But a Recovery or SessionRestore should reactivate a may be already active AutoSave.
+ bool bAllowAutoSaveReactivation = true;
+
+ implts_stopTimer();
+ implts_stopListening();
+
+ ListenerInformer aListenerInformer(*this, eJob);
+ aListenerInformer.start();
+
+ try
+ {
+ // Auto save is called from our internal timer ... not via dispatch() API !
+ // else
+ if (
+ ((eJob & Job::PrepareEmergencySave) == Job::PrepareEmergencySave) &&
+ ((eJob & Job::DisableAutorecovery ) != Job::DisableAutorecovery )
+ )
+ {
+ SAL_INFO("fwk.autorecovery", "... prepare emergency save ...");
+ bAllowAutoSaveReactivation = false;
+ implts_prepareEmergencySave();
+ }
+ else
+ if (
+ ((eJob & Job::EmergencySave ) == Job::EmergencySave ) &&
+ ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery)
+ )
+ {
+ SAL_INFO("fwk.autorecovery", "... do emergency save ...");
+ bAllowAutoSaveReactivation = false;
+ implts_doEmergencySave(aParams);
+ }
+ else
+ if (
+ ((eJob & Job::Recovery ) == Job::Recovery ) &&
+ ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery)
+ )
+ {
+ SAL_INFO("fwk.autorecovery", "... do recovery ...");
+ implts_doRecovery(aParams);
+ }
+ else
+ if (
+ ((eJob & Job::SessionSave ) == Job::SessionSave ) &&
+ ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery)
+ )
+ {
+ SAL_INFO("fwk.autorecovery", "... do session save ...");
+ bAllowAutoSaveReactivation = false;
+ implts_doSessionSave(aParams);
+ }
+ else
+ if (
+ ((eJob & Job::SessionQuietQuit ) == Job::SessionQuietQuit ) &&
+ ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery)
+ )
+ {
+ SAL_INFO("fwk.autorecovery", "... do session quiet quit ...");
+ bAllowAutoSaveReactivation = false;
+ implts_doSessionQuietQuit();
+ }
+ else
+ if (
+ ((eJob & Job::SessionRestore ) == Job::SessionRestore ) &&
+ ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery)
+ )
+ {
+ SAL_INFO("fwk.autorecovery", "... do session restore ...");
+ implts_doSessionRestore(aParams);
+ }
+ else
+ if (
+ ((eJob & Job::EntryBackup ) == Job::EntryBackup ) &&
+ ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery)
+ )
+ implts_backupWorkingEntry(aParams);
+ else
+ if (
+ ((eJob & Job::EntryCleanup ) == Job::EntryCleanup ) &&
+ ((eJob & Job::DisableAutorecovery) != Job::DisableAutorecovery)
+ )
+ implts_cleanUpWorkingEntry(aParams);
+ }
+ catch(const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch(const css::uno::Exception&)
+ {
+ // TODO better error handling
+ }
+
+ aListenerInformer.stop();
+
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ m_eJob = Job::NoJob;
+ if ( bAllowAutoSaveReactivation && bWasAutoSaveActive )
+ {
+ m_eJob |= Job::AutoSave;
+
+ if (bWasUserAutoSaveActive)
+ {
+ m_eJob |= Job::UserAutoSave;
+ }
+ }
+
+ } /* SAFE */
+
+ // depends on bAllowAutoSaveReactivation implicitly by looking on m_eJob=Job::AutoSave! see before ...
+ implts_updateTimer();
+
+ if (bAllowAutoSaveReactivation)
+ implts_startListening();
+}
+
+void SAL_CALL AutoRecovery::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
+ const css::util::URL& aURL )
+{
+ if (!xListener.is())
+ throw css::uno::RuntimeException("Invalid listener reference.", static_cast< css::frame::XDispatch* >(this));
+ // container is threadsafe by using a shared mutex!
+ m_lListener.addInterface(aURL.Complete, xListener);
+
+ // REENTRANT !? -> --------------------------------
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
+
+ /* SAFE */ {
+ osl::ResettableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ for (auto const& elem : m_lDocCache)
+ {
+ css::frame::FeatureStateEvent aEvent = AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_UPDATE, &elem);
+
+ // } /* SAFE */
+ g.clear();
+ xListener->statusChanged(aEvent);
+ g.reset();
+ // /* SAFE */ {
+ }
+
+ } /* SAFE */
+}
+
+void SAL_CALL AutoRecovery::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
+ const css::util::URL& aURL )
+{
+ if (!xListener.is())
+ throw css::uno::RuntimeException("Invalid listener reference.", static_cast< css::frame::XDispatch* >(this));
+ // container is threadsafe by using a shared mutex!
+ m_lListener.removeInterface(aURL.Complete, xListener);
+}
+
+void SAL_CALL AutoRecovery::documentEventOccured(const css::document::DocumentEvent& aEvent)
+{
+ css::uno::Reference< css::frame::XModel3 > xDocument(aEvent.Source, css::uno::UNO_QUERY);
+
+ // new document => put it into the internal list
+ if (
+ (aEvent.EventName == EVENT_ON_NEW) ||
+ (aEvent.EventName == EVENT_ON_LOAD)
+ )
+ {
+ implts_registerDocument(xDocument);
+ }
+ // document modified => set its modify state new (means modified against the original file!)
+ else if ( aEvent.EventName == EVENT_ON_MODIFYCHANGED )
+ {
+ implts_updateModifiedState(xDocument);
+ }
+ /* at least one document starts saving process =>
+ Our application code is not ready for multiple save requests
+ at the same time. So we have to suppress our AutoSave feature
+ for the moment, till this other save requests will be finished.
+ */
+ else if (
+ (aEvent.EventName == EVENT_ON_SAVE) ||
+ (aEvent.EventName == EVENT_ON_SAVEAS) ||
+ (aEvent.EventName == EVENT_ON_SAVETO)
+ )
+ {
+ implts_updateDocumentUsedForSavingState(xDocument, SAVE_IN_PROGRESS);
+ }
+ // document saved => remove tmp. files - but hold config entries alive!
+ else if (
+ (aEvent.EventName == EVENT_ON_SAVEDONE) ||
+ (aEvent.EventName == EVENT_ON_SAVEASDONE)
+ )
+ {
+ SolarMutexGuard g;
+ implts_markDocumentAsSaved(xDocument);
+ implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
+ }
+ /* document saved as copy => mark it as "non used by concurrent save operation".
+ so we can try to create a backup copy if next time AutoSave is started too.
+ Don't remove temp. files or change the modified state of the document!
+ It was not really saved to the original file...
+ */
+ else if ( aEvent.EventName == EVENT_ON_SAVETODONE )
+ {
+ implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
+ }
+ // If saving of a document failed by an error ... we have to save this document
+ // by ourself next time AutoSave or EmergencySave is triggered.
+ // But we can reset the state "used for other save requests". Otherwise
+ // these documents will never be saved!
+ else if (
+ (aEvent.EventName == EVENT_ON_SAVEFAILED) ||
+ (aEvent.EventName == EVENT_ON_SAVEASFAILED) ||
+ (aEvent.EventName == EVENT_ON_SAVETOFAILED)
+ )
+ {
+ implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
+ }
+ // document closed => remove temp. files and configuration entries
+ else if ( aEvent.EventName == EVENT_ON_UNLOAD )
+ {
+ implts_deregisterDocument(xDocument); // sal_True => stop listening for disposing() !
+ }
+}
+
+void SAL_CALL AutoRecovery::changesOccurred(const css::util::ChangesEvent& aEvent)
+{
+ const css::uno::Sequence< css::util::ElementChange > lChanges (aEvent.Changes);
+ const css::util::ElementChange* pChanges = lChanges.getConstArray();
+
+ sal_Int32 c = lChanges.getLength();
+ sal_Int32 i = 0;
+
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ // Changes of the configuration must be ignored if AutoSave/Recovery was disabled for this
+ // office session. That can happen if e.g. the command line arguments "--norestore" or "--headless"
+ // was set.
+ if ((m_eJob & Job::DisableAutorecovery) == Job::DisableAutorecovery)
+ return;
+
+ for (i=0; i<c; ++i)
+ {
+ OUString sPath;
+ pChanges[i].Accessor >>= sPath;
+
+ if ( sPath == CFG_ENTRY_AUTOSAVE_ENABLED )
+ {
+ bool bEnabled = false;
+ if (pChanges[i].Element >>= bEnabled)
+ {
+ if (bEnabled)
+ {
+ m_eJob |= Job::AutoSave;
+ m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
+ }
+ else
+ {
+ m_eJob &= ~Job::AutoSave;
+ m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
+ }
+ }
+ }
+ else
+ if ( sPath == CFG_ENTRY_AUTOSAVE_TIMEINTERVALL )
+ pChanges[i].Element >>= m_nAutoSaveTimeIntervall;
+ }
+
+ } /* SAFE */
+
+ // Note: This call stops the timer and starts it again.
+ // But it checks the different timer states internally and
+ // may be suppress the restart!
+ implts_updateTimer();
+}
+
+void SAL_CALL AutoRecovery::modified(const css::lang::EventObject& aEvent)
+{
+ css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
+ if (! xDocument.is())
+ return;
+
+ implts_markDocumentModifiedAgainstLastBackup(xDocument);
+}
+
+void SAL_CALL AutoRecovery::disposing(const css::lang::EventObject& aEvent)
+{
+ /* SAFE */
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ if (aEvent.Source == m_xNewDocBroadcaster)
+ {
+ m_xNewDocBroadcaster.clear();
+ return;
+ }
+
+ if (aEvent.Source == m_xRecoveryCFG)
+ {
+ m_xRecoveryCFG.clear();
+ return;
+ }
+
+ // dispose from one of our cached documents ?
+ // Normally they should send a OnUnload message ...
+ // But some stacktraces shows another possible use case .-)
+ css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
+ if (xDocument.is())
+ {
+ implts_deregisterDocument(xDocument, false); // sal_False => don't call removeEventListener() .. because it's not needed here
+ return;
+ }
+
+ /* SAFE */
+}
+
+void AutoRecovery::implts_openConfig()
+{
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ if (m_xRecoveryCFG.is())
+ return;
+ } /* SAFE */
+
+ css::uno::Reference<css::lang::XMultiServiceFactory> xConfigProvider(
+ css::configuration::theDefaultProvider::get(m_xContext));
+
+ std::vector<css::uno::Any> lParams;
+ css::beans::PropertyValue aParam;
+
+ // set root path
+ aParam.Name = "nodepath";
+ aParam.Value <<= OUString(CFG_PACKAGE_RECOVERY);
+ lParams.push_back(css::uno::Any(aParam));
+
+ // throws a RuntimeException if an error occurs!
+ css::uno::Reference<css::container::XNameAccess> xCFG(
+ xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess",
+ comphelper::containerToSequence(lParams)),
+ css::uno::UNO_QUERY);
+
+ sal_Int32 nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE;
+ sal_Int32 nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE;
+
+ try
+ {
+ nMinSpaceDocSave = officecfg::Office::Recovery::AutoSave::MinSpaceDocSave::get();
+ nMinSpaceConfigSave = officecfg::Office::Recovery::AutoSave::MinSpaceConfigSave::get();
+ }
+ catch(const css::uno::Exception&)
+ {
+ // These config keys are not sooooo important, that
+ // we are interested on errors here really .-)
+ nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE;
+ nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE;
+ }
+
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ m_xRecoveryCFG = xCFG;
+ m_nMinSpaceDocSave = nMinSpaceDocSave;
+ m_nMinSpaceConfigSave = nMinSpaceConfigSave;
+ } /* SAFE */
+}
+
+void AutoRecovery::implts_readAutoSaveConfig()
+{
+ implts_openConfig();
+
+ // AutoSave [bool]
+ bool bEnabled(officecfg::Office::Common::Save::Document::AutoSave::get());
+
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ if (bEnabled)
+ {
+ bool bUserEnabled(officecfg::Office::Recovery::AutoSave::UserAutoSaveEnabled::get());
+
+ m_eJob |= Job::AutoSave;
+ m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
+
+ if (bUserEnabled)
+ {
+ m_eJob |= Job::UserAutoSave;
+ }
+ else
+ {
+ m_eJob &= ~Job::UserAutoSave;
+ }
+ }
+ else
+ {
+ m_eJob &= ~Job::AutoSave;
+ m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
+ }
+ } /* SAFE */
+
+ // AutoSaveTimeIntervall [int] in min
+ sal_Int32 nTimeIntervall(
+ officecfg::Office::Common::Save::Document::AutoSaveTimeIntervall::get());
+
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ m_nAutoSaveTimeIntervall = nTimeIntervall;
+ } /* SAFE */
+}
+
+void AutoRecovery::implts_readConfig()
+{
+ implts_readAutoSaveConfig();
+
+ // REENTRANT -> --------------------------------
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
+
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ // reset current cache load cache
+ m_lDocCache.clear();
+ m_nIdPool = 0;
+ } /* SAFE */
+
+ aCacheLock.unlock();
+ // <- REENTRANT --------------------------------
+
+ css::uno::Reference<css::container::XNameAccess> xRecoveryList(
+ officecfg::Office::Recovery::RecoveryList::get());
+ const OUString sRECOVERY_ITEM_BASE_IDENTIFIER(RECOVERY_ITEM_BASE_IDENTIFIER);
+ const css::uno::Sequence< OUString > lItems = xRecoveryList->getElementNames();
+ const OUString* pItems = lItems.getConstArray();
+ sal_Int32 c = lItems.getLength();
+ sal_Int32 i = 0;
+
+ // REENTRANT -> --------------------------
+ aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE);
+
+ for (i=0; i<c; ++i)
+ {
+ css::uno::Reference< css::beans::XPropertySet > xItem;
+ xRecoveryList->getByName(pItems[i]) >>= xItem;
+ if (!xItem.is())
+ continue;
+
+ AutoRecovery::TDocumentInfo aInfo;
+ aInfo.NewTempURL.clear();
+ aInfo.Document.clear();
+ xItem->getPropertyValue(CFG_ENTRY_PROP_ORIGINALURL) >>= aInfo.OrgURL;
+ xItem->getPropertyValue(CFG_ENTRY_PROP_TEMPURL) >>= aInfo.OldTempURL;
+ xItem->getPropertyValue(CFG_ENTRY_PROP_TEMPLATEURL) >>= aInfo.TemplateURL;
+ xItem->getPropertyValue(CFG_ENTRY_PROP_FILTER) >>= aInfo.RealFilter;
+ sal_Int32 tmp = 0;
+ xItem->getPropertyValue(CFG_ENTRY_PROP_DOCUMENTSTATE) >>= tmp;
+ aInfo.DocumentState = DocState(tmp);
+ xItem->getPropertyValue(CFG_ENTRY_PROP_MODULE) >>= aInfo.AppModule;
+ xItem->getPropertyValue(CFG_ENTRY_PROP_TITLE) >>= aInfo.Title;
+ xItem->getPropertyValue(CFG_ENTRY_PROP_VIEWNAMES) >>= aInfo.ViewNames;
+ implts_specifyAppModuleAndFactory(aInfo);
+ implts_specifyDefaultFilterAndExtension(aInfo);
+
+ if (pItems[i].startsWith(sRECOVERY_ITEM_BASE_IDENTIFIER))
+ {
+ std::u16string_view sID = pItems[i].subView(sRECOVERY_ITEM_BASE_IDENTIFIER.getLength());
+ aInfo.ID = o3tl::toInt32(sID);
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ if (aInfo.ID > m_nIdPool)
+ {
+ m_nIdPool = aInfo.ID+1;
+ SAL_WARN_IF(m_nIdPool<0, "fwk.autorecovery", "AutoRecovery::implts_readConfig(): Overflow of IDPool detected!");
+ }
+ } /* SAFE */
+ }
+ else
+ SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_readConfig(): Who changed numbering of recovery items? Cache will be inconsistent then! I do not know, what will happen next time .-)");
+
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ m_lDocCache.push_back(aInfo);
+ } /* SAFE */
+ }
+
+ aCacheLock.unlock();
+ // <- REENTRANT --------------------------
+
+ implts_updateTimer();
+}
+
+void AutoRecovery::implts_specifyDefaultFilterAndExtension(AutoRecovery::TDocumentInfo& rInfo)
+{
+ if (rInfo.AppModule.isEmpty())
+ {
+ throw css::uno::RuntimeException(
+ "Can not find out the default filter and its extension, if no application module is known!",
+ static_cast< css::frame::XDispatch* >(this));
+ }
+
+ css::uno::Reference< css::container::XNameAccess> xCFG;
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ xCFG = m_xModuleCFG;
+ } /* SAFE */
+
+ try
+ {
+ if (! xCFG.is())
+ {
+ implts_openConfig();
+ // open module config on demand and cache the update access
+ xCFG.set(officecfg::Setup::Office::Factories::get(),
+ css::uno::UNO_SET_THROW);
+
+ /* SAFE */ {
+ osl::MutexGuard g2(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ m_xModuleCFG = xCFG;
+ } /* SAFE */
+ }
+
+ css::uno::Reference< css::container::XNameAccess > xModuleProps(
+ xCFG->getByName(rInfo.AppModule),
+ css::uno::UNO_QUERY_THROW);
+
+ xModuleProps->getByName(CFG_ENTRY_REALDEFAULTFILTER) >>= rInfo.DefaultFilter;
+
+ css::uno::Reference< css::container::XNameAccess > xFilterCFG(
+ m_xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.document.FilterFactory", m_xContext), css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::container::XNameAccess > xTypeCFG(
+ m_xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.document.TypeDetection", m_xContext), css::uno::UNO_QUERY_THROW);
+
+ ::comphelper::SequenceAsHashMap lFilterProps (xFilterCFG->getByName(rInfo.DefaultFilter));
+ OUString sTypeRegistration = lFilterProps.getUnpackedValueOrDefault(FILTER_PROP_TYPE, OUString());
+ ::comphelper::SequenceAsHashMap lTypeProps (xTypeCFG->getByName(sTypeRegistration));
+ css::uno::Sequence< OUString > lExtensions = lTypeProps.getUnpackedValueOrDefault(TYPE_PROP_EXTENSIONS, css::uno::Sequence< OUString >());
+ if (lExtensions.hasElements())
+ {
+ rInfo.Extension = "." + lExtensions[0];
+ }
+ else
+ rInfo.Extension = ".unknown";
+ }
+ catch(const css::uno::Exception&)
+ {
+ rInfo.DefaultFilter.clear();
+ rInfo.Extension.clear();
+ }
+}
+
+void AutoRecovery::implts_specifyAppModuleAndFactory(AutoRecovery::TDocumentInfo& rInfo)
+{
+ ENSURE_OR_THROW2(
+ !rInfo.AppModule.isEmpty() || rInfo.Document.is(),
+ "Can not find out the application module nor its factory URL, if no application module (or a suitable) document is known!",
+ *this );
+
+ css::uno::Reference< css::frame::XModuleManager2 > xManager = ModuleManager::create(m_xContext);
+
+ if (rInfo.AppModule.isEmpty())
+ rInfo.AppModule = xManager->identify(rInfo.Document);
+
+ ::comphelper::SequenceAsHashMap lModuleDescription(xManager->getByName(rInfo.AppModule));
+ lModuleDescription[OUString(CFG_ENTRY_PROP_EMPTYDOCUMENTURL)] >>= rInfo.FactoryURL;
+ lModuleDescription[OUString(CFG_ENTRY_PROP_FACTORYSERVICE)] >>= rInfo.FactoryService;
+}
+
+void AutoRecovery::implts_collectActiveViewNames( AutoRecovery::TDocumentInfo& i_rInfo )
+{
+ ENSURE_OR_THROW2( i_rInfo.Document.is(), "need at document, at the very least", *this );
+
+ i_rInfo.ViewNames.realloc(0);
+
+ // obtain list of controllers of this document
+ ::std::vector< OUString > aViewNames;
+ const Reference< XModel2 > xModel( i_rInfo.Document, UNO_QUERY );
+ if ( xModel.is() )
+ {
+ const Reference< css::container::XEnumeration > xEnumControllers( xModel->getControllers() );
+ while ( xEnumControllers->hasMoreElements() )
+ {
+ const Reference< XController2 > xController( xEnumControllers->nextElement(), UNO_QUERY );
+ OUString sViewName;
+ if ( xController.is() )
+ sViewName = xController->getViewControllerName();
+ OSL_ENSURE( !sViewName.isEmpty(), "AutoRecovery::implts_collectActiveViewNames: (no XController2 ->) no view name -> no recovery of this view!" );
+
+ if ( !sViewName.isEmpty() )
+ aViewNames.push_back( sViewName );
+ }
+ }
+
+ i_rInfo.ViewNames.realloc( aViewNames.size() );
+ ::std::copy( aViewNames.begin(), aViewNames.end(), i_rInfo.ViewNames.getArray() );
+}
+
+void AutoRecovery::implts_persistAllActiveViewNames()
+{
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ // This list will be filled with every document
+ for (auto & elem : m_lDocCache)
+ {
+ implts_collectActiveViewNames(elem);
+ implts_flushConfigItem(elem);
+ }
+}
+
+void AutoRecovery::implts_flushConfigItem(const AutoRecovery::TDocumentInfo& rInfo, bool bRemoveIt)
+{
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(
+ comphelper::ConfigurationChanges::create());
+
+ try
+ {
+ implts_openConfig();
+
+ css::uno::Reference<css::container::XNameAccess> xCheck(
+ officecfg::Office::Recovery::RecoveryList::get(batch));
+
+ css::uno::Reference< css::container::XNameContainer > xModify(xCheck, css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::lang::XSingleServiceFactory > xCreate(xCheck, css::uno::UNO_QUERY_THROW);
+
+ OUString sID = RECOVERY_ITEM_BASE_IDENTIFIER + OUString::number(rInfo.ID);
+
+ // remove
+ if (bRemoveIt)
+ {
+ // Catch NoSuchElementException.
+ // It's not a good idea inside multithreaded environments to call hasElement - removeElement.
+ // DO IT!
+ try
+ {
+ xModify->removeByName(sID);
+ }
+ catch(const css::container::NoSuchElementException&)
+ {
+ return;
+ }
+ }
+ else
+ {
+ // new/modify
+ css::uno::Reference< css::beans::XPropertySet > xSet;
+ bool bNew = !xCheck->hasByName(sID);
+ if (bNew)
+ xSet.set(xCreate->createInstance(), css::uno::UNO_QUERY_THROW);
+ else
+ xCheck->getByName(sID) >>= xSet;
+
+ xSet->setPropertyValue(CFG_ENTRY_PROP_ORIGINALURL, css::uno::Any(rInfo.OrgURL ));
+ xSet->setPropertyValue(CFG_ENTRY_PROP_TEMPURL, css::uno::Any(rInfo.OldTempURL ));
+ xSet->setPropertyValue(CFG_ENTRY_PROP_TEMPLATEURL, css::uno::Any(rInfo.TemplateURL ));
+ xSet->setPropertyValue(CFG_ENTRY_PROP_FILTER, css::uno::Any(rInfo.RealFilter));
+ xSet->setPropertyValue(CFG_ENTRY_PROP_DOCUMENTSTATE, css::uno::Any(sal_Int32(rInfo.DocumentState)));
+ xSet->setPropertyValue(CFG_ENTRY_PROP_MODULE, css::uno::Any(rInfo.AppModule));
+ xSet->setPropertyValue(CFG_ENTRY_PROP_TITLE, css::uno::Any(rInfo.Title));
+ xSet->setPropertyValue(CFG_ENTRY_PROP_VIEWNAMES, css::uno::Any(rInfo.ViewNames));
+
+ if (bNew)
+ xModify->insertByName(sID, css::uno::Any(xSet));
+ }
+ }
+ catch(const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch(const css::uno::Exception&)
+ {
+ // ??? can it happen that a full disc let these set of operations fail too ???
+ }
+
+ sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER;
+ do
+ {
+ try
+ {
+ batch->commit();
+
+#ifdef TRIGGER_FULL_DISC_CHECK
+ throw css::uno::Exception("trigger full disk check");
+#else // TRIGGER_FULL_DISC_CHECK
+ nRetry = 0;
+#endif // TRIGGER_FULL_DISC_CHECK
+ }
+ catch(const css::uno::Exception&)
+ {
+ // a) FULL DISC seems to be the problem behind => show error and retry it forever (e.g. retry=300)
+ // b) unknown problem (may be locking problem) => reset RETRY value to more useful value(!) (e.g. retry=3)
+ // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace !
+
+ sal_Int32 nMinSpaceConfigSave;
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ nMinSpaceConfigSave = m_nMinSpaceConfigSave;
+ } /* SAFE */
+
+ if (! impl_enoughDiscSpace(nMinSpaceConfigSave))
+ AutoRecovery::impl_showFullDiscError();
+ else if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL)
+ nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL;
+ else if (nRetry <= GIVE_UP_RETRY)
+ throw; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!!
+
+ --nRetry;
+ }
+ }
+ while(nRetry>0);
+}
+
+void AutoRecovery::implts_startListening()
+{
+ css::uno::Reference< css::util::XChangesNotifier > xCFG;
+ css::uno::Reference< css::frame::XGlobalEventBroadcaster > xBroadcaster;
+ bool bListenForDocEvents;
+ bool bListenForConfigChanges;
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ xCFG.set (m_xRecoveryCFG, css::uno::UNO_QUERY);
+ xBroadcaster = m_xNewDocBroadcaster;
+ bListenForDocEvents = m_bListenForDocEvents;
+ bListenForConfigChanges = m_bListenForConfigChanges;
+ } /* SAFE */
+
+ if (
+ ( xCFG.is() ) &&
+ (! bListenForConfigChanges)
+ )
+ {
+ css::uno::Reference<css::util::XChangesListener> const xListener(
+ new WeakChangesListener(this));
+ xCFG->addChangesListener(xListener);
+ /* SAFE */ {
+ osl::MutexGuard g2(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ m_xRecoveryCFGListener = xListener;
+ m_bListenForConfigChanges = true;
+ } /* SAFE */
+ }
+
+ if (!xBroadcaster.is())
+ {
+ xBroadcaster = css::frame::theGlobalEventBroadcaster::get(m_xContext);
+ /* SAFE */ {
+ osl::MutexGuard g2(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ m_xNewDocBroadcaster = xBroadcaster;
+ } /* SAFE */
+ }
+
+ if (
+ ( xBroadcaster.is() ) &&
+ (! bListenForDocEvents)
+ )
+ {
+ css::uno::Reference<css::document::XDocumentEventListener> const
+ xListener(new WeakDocumentEventListener(this));
+ xBroadcaster->addDocumentEventListener(xListener);
+ /* SAFE */ {
+ osl::MutexGuard g2(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ m_xNewDocBroadcasterListener = xListener;
+ m_bListenForDocEvents = true;
+ } /* SAFE */
+ }
+}
+
+void AutoRecovery::implts_stopListening()
+{
+ css::uno::Reference< css::util::XChangesNotifier > xCFG;
+ css::uno::Reference< css::document::XDocumentEventBroadcaster > xGlobalEventBroadcaster;
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ // Attention: Don't reset our internal members here too.
+ // May be we must work with our configuration, but don't wish to be informed
+ // about changes any longer. Needed e.g. during Job::EmergencySave!
+ xCFG.set (m_xRecoveryCFG , css::uno::UNO_QUERY);
+ xGlobalEventBroadcaster = m_xNewDocBroadcaster;
+ } /* SAFE */
+
+ if (xGlobalEventBroadcaster.is() && m_bListenForDocEvents)
+ {
+ xGlobalEventBroadcaster->removeDocumentEventListener(m_xNewDocBroadcasterListener);
+ m_bListenForDocEvents = false;
+ }
+
+ if (xCFG.is() && m_bListenForConfigChanges)
+ {
+ xCFG->removeChangesListener(m_xRecoveryCFGListener);
+ m_bListenForConfigChanges = false;
+ }
+}
+
+void AutoRecovery::implts_startModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo)
+{
+ if (rInfo.ListenForModify)
+ return;
+
+ css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY);
+ if (xBroadcaster.is())
+ {
+ css::uno::Reference< css::util::XModifyListener > xThis(this);
+ xBroadcaster->addModifyListener(xThis);
+ rInfo.ListenForModify = true;
+ }
+}
+
+void AutoRecovery::implts_stopModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo)
+{
+ if (! rInfo.ListenForModify)
+ return;
+
+ css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY);
+ if (xBroadcaster.is())
+ {
+ css::uno::Reference< css::util::XModifyListener > xThis(this);
+ xBroadcaster->removeModifyListener(xThis);
+ rInfo.ListenForModify = false;
+ }
+}
+
+void AutoRecovery::implts_updateTimer()
+{
+ implts_stopTimer();
+
+ sal_Int32 nMilliSeconds = 0;
+
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ if (
+ (m_eJob == Job::NoJob ) || // TODO may be superfluous - E_DONT_START_TIMER should be used only
+ (m_eTimerType == AutoRecovery::E_DONT_START_TIMER)
+ )
+ return;
+
+ if (m_eTimerType == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL)
+ {
+ nMilliSeconds = (m_nAutoSaveTimeIntervall*60000); // [min] => 60.000 ms
+ }
+ else if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE)
+ {
+ nMilliSeconds = MIN_TIME_FOR_USER_IDLE;
+ }
+ else if (m_eTimerType == AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED)
+ nMilliSeconds = 300; // there is a minimum time frame, where the user can lose some key input data!
+
+
+ } /* SAFE */
+
+ SolarMutexGuard g;
+ m_aTimer.SetTimeout(nMilliSeconds);
+ m_aTimer.Start();
+}
+
+void AutoRecovery::implts_stopTimer()
+{
+ SolarMutexGuard g;
+
+ if (!m_aTimer.IsActive())
+ return;
+ m_aTimer.Stop();
+}
+
+IMPL_LINK_NOARG(AutoRecovery, implts_timerExpired, Timer *, void)
+{
+ try
+ {
+ // This method is called by using a pointer to us.
+ // But we must be aware that we can be destroyed hardly
+ // if our uno reference will be gone!
+ // => Hold this object alive till this method finish its work.
+ css::uno::Reference< css::uno::XInterface > xSelfHold(static_cast< css::lang::XTypeProvider* >(this));
+
+ // Needed! Otherwise every reschedule request allow a new triggered timer event :-(
+ implts_stopTimer();
+
+ // The timer must be ignored if AutoSave/Recovery was disabled for this
+ // office session. That can happen if e.g. the command line arguments "--norestore" or "--headless"
+ // was set. But normally the timer was disabled if recovery was disabled ...
+ // But so we are more "safe" .-)
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ if ((m_eJob & Job::DisableAutorecovery) == Job::DisableAutorecovery)
+ return;
+ } /* SAFE */
+
+ // check some "states", where it's not allowed (better: not a good idea) to
+ // start an AutoSave. (e.g. if the user makes drag & drop ...)
+ // Then we poll till this "disallowed" state is gone.
+ bool bAutoSaveNotAllowed = Application::IsUICaptured();
+ if (bAutoSaveNotAllowed)
+ {
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ m_eTimerType = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED;
+ } /* SAFE */
+ implts_updateTimer();
+ return;
+ }
+
+ // analyze timer type.
+ // If we poll for an user idle period, may be we must
+ // do nothing here and start the timer again.
+ /* SAFE */ {
+ osl::ClearableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE)
+ {
+ bool bUserIdle = Application::GetLastInputInterval() > MIN_TIME_FOR_USER_IDLE;
+ if (!bUserIdle)
+ {
+ g.clear();
+ implts_updateTimer();
+ return;
+ }
+ }
+
+ } /* SAFE */
+
+ implts_informListener(Job::AutoSave,
+ AutoRecovery::implst_createFeatureStateEvent(Job::AutoSave, OPERATION_START, nullptr));
+
+ // force save of all currently open documents
+ // The called method returns an info, if and how this
+ // timer must be restarted.
+ AutoRecovery::ETimerType eSuggestedTimer = implts_saveDocs(true/*bAllowUserIdleLoop*/, false);
+
+ // If timer is not used for "short callbacks" (means polling
+ // for special states) ... reset the handle state of all
+ // cache items. Such handle state indicates, that a document
+ // was already saved during the THIS(!) AutoSave session.
+ // Of course NEXT AutoSave session must be started without
+ // any "handle" state ...
+ if (
+ (eSuggestedTimer == AutoRecovery::E_DONT_START_TIMER ) ||
+ (eSuggestedTimer == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL)
+ )
+ {
+ implts_resetHandleStates();
+ }
+
+ implts_informListener(Job::AutoSave,
+ AutoRecovery::implst_createFeatureStateEvent(Job::AutoSave, OPERATION_STOP, nullptr));
+
+ // restart timer - because it was disabled before ...
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ m_eTimerType = eSuggestedTimer;
+ } /* SAFE */
+
+ implts_updateTimer();
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+}
+
+IMPL_LINK_NOARG(AutoRecovery, implts_asyncDispatch, LinkParamNone*, void)
+{
+ DispatchParams aParams;
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ aParams = m_aDispatchParams;
+ css::uno::Reference< css::uno::XInterface > xHoldRefForMethodAlive = aParams.m_xHoldRefForAsyncOpAlive;
+ m_aDispatchParams.forget(); // clears all members ... including the ref-hold object .-)
+ } /* SAFE */
+
+ try
+ {
+ implts_dispatch(aParams);
+ }
+ catch (...)
+ {
+ }
+}
+
+void AutoRecovery::implts_registerDocument(const css::uno::Reference< css::frame::XModel3 > & xDocument)
+{
+ // ignore corrupted events, where no document is given ... Runtime Error ?!
+ if (!xDocument.is())
+ return;
+
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
+
+ // notification for already existing document !
+ // Can happen if events came in asynchronous on recovery time.
+ // Then our cache was filled from the configuration ... but now we get some
+ // asynchronous events from the global event broadcaster. We must be sure that
+ // we don't add the same document more than once.
+ AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
+ if (pIt != m_lDocCache.end())
+ {
+ // Normally nothing must be done for this "late" notification.
+ // But may be the modified state was changed inbetween.
+ // Check it...
+ implts_updateModifiedState(xDocument);
+ return;
+ }
+
+ aCacheLock.unlock();
+
+ utl::MediaDescriptor lDescriptor(xDocument->getArgs2( { utl::MediaDescriptor::PROP_FILTERNAME, utl::MediaDescriptor::PROP_NOAUTOSAVE } ));
+
+ // check if this document must be ignored for recovery !
+ // Some use cases don't wish support for AutoSave/Recovery ... as e.g. OLE-Server / ActiveX Control etcpp.
+ bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_NOAUTOSAVE, false);
+ if (bNoAutoSave)
+ return;
+
+ // Check if doc is well known on the desktop. Otherwise ignore it!
+ // Other frames mostly are used from external programs - e.g. the bean ...
+ css::uno::Reference< css::frame::XController > xController = xDocument->getCurrentController();
+ if (!xController.is())
+ return;
+
+ css::uno::Reference< css::frame::XFrame > xFrame = xController->getFrame();
+ if (!xFrame.is())
+ return;
+ css::uno::Reference< css::frame::XDesktop > xDesktop (xFrame->getCreator(), css::uno::UNO_QUERY);
+ if (!xDesktop.is())
+ return;
+
+ // if the document doesn't support the XDocumentRecovery interface, we're not interested in it.
+ Reference< XDocumentRecovery > xDocRecovery( xDocument, UNO_QUERY );
+ if ( !xDocRecovery.is() )
+ return;
+
+ // get all needed information of this document
+ // We need it to update our cache or to locate already existing elements there!
+ AutoRecovery::TDocumentInfo aNew;
+ aNew.Document = xDocument;
+
+ // TODO replace getLocation() with getURL() ... it's a workaround currently only!
+ css::uno::Reference< css::frame::XStorable > xDoc(aNew.Document, css::uno::UNO_QUERY_THROW);
+ aNew.OrgURL = xDoc->getLocation();
+
+ css::uno::Reference< css::frame::XTitle > xTitle(aNew.Document, css::uno::UNO_QUERY_THROW);
+ aNew.Title = xTitle->getTitle ();
+
+ // classify the used application module, which is used by this document.
+ implts_specifyAppModuleAndFactory(aNew);
+
+ // Hack! Check for "illegal office documents"... as e.g. the Basic IDE
+ // It's not really a full featured office document. It doesn't provide a URL, any filter, a factory URL etcpp.
+ // TODO file bug to Basic IDE developers. They must remove the office document API from its service.
+ if (
+ (aNew.OrgURL.isEmpty()) &&
+ (aNew.FactoryURL.isEmpty())
+ )
+ {
+ OSL_FAIL( "AutoRecovery::implts_registerDocument: this should not happen anymore!" );
+ // nowadays, the Basic IDE should already die on the "supports XDocumentRecovery" check. And no other known
+ // document type fits in here ...
+ return;
+ }
+
+ // By the way - get some information about the default format for saving!
+ // and save an information about the real used filter by this document.
+ // We save this document with DefaultFilter ... and load it with the RealFilter.
+ implts_specifyDefaultFilterAndExtension(aNew);
+ aNew.RealFilter = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME, OUString());
+
+ // Further we must know, if this document base on a template.
+ // Then we must load it in a different way.
+ css::uno::Reference< css::document::XDocumentPropertiesSupplier > xSupplier(aNew.Document, css::uno::UNO_QUERY);
+ if (xSupplier.is()) // optional interface!
+ {
+ css::uno::Reference< css::document::XDocumentProperties > xDocProps(xSupplier->getDocumentProperties(), css::uno::UNO_SET_THROW);
+ aNew.TemplateURL = xDocProps->getTemplateURL();
+ }
+
+ css::uno::Reference< css::util::XModifiable > xModifyCheck(xDocument, css::uno::UNO_QUERY_THROW);
+ if (xModifyCheck->isModified())
+ {
+ aNew.DocumentState |= DocState::Modified;
+ }
+
+ aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE);
+
+ AutoRecovery::TDocumentInfo aInfo;
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ // create a new cache entry ... this document is not known.
+ ++m_nIdPool;
+ aNew.ID = m_nIdPool;
+ SAL_WARN_IF(m_nIdPool<0, "fwk.autorecovery", "AutoRecovery::implts_registerDocument(): Overflow of ID pool detected.");
+ m_lDocCache.push_back(aNew);
+
+ AutoRecovery::TDocumentList::iterator pIt1 = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
+ aInfo = *pIt1;
+
+ } /* SAFE */
+
+ implts_flushConfigItem(aInfo);
+ implts_startModifyListeningOnDoc(aInfo);
+
+ aCacheLock.unlock();
+}
+
+void AutoRecovery::implts_deregisterDocument(const css::uno::Reference< css::frame::XModel >& xDocument ,
+ bool bStopListening)
+{
+ AutoRecovery::TDocumentInfo aInfo;
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ // Attention: Don't leave SAFE section, if you work with pIt!
+ // Because it points directly into the m_lDocCache list ...
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
+
+ AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
+ if (pIt == m_lDocCache.end())
+ return; // unknown document => not a runtime error! Because we register only a few documents. see registration ...
+
+ aInfo = *pIt;
+
+ aCacheLock.unlock();
+
+ // Sometimes we close documents by ourself.
+ // And these documents can't be deregistered.
+ // Otherwise we lose our configuration data... but need it !
+ // see SessionSave !
+ if (aInfo.IgnoreClosing)
+ return;
+
+ CacheLockGuard aCacheLock2(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
+ pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
+ if (pIt != m_lDocCache.end())
+ m_lDocCache.erase(pIt);
+ pIt = m_lDocCache.end(); // otherwise it's not specified what pIt means!
+ aCacheLock2.unlock();
+
+ } /* SAFE */
+
+ /* This method is called within disposing() of the document too. But there it's not a good idea to
+ deregister us as listener. Further it makes no sense - because the broadcaster dies.
+ So we suppress deregistration in such case...
+ */
+ if (bStopListening)
+ implts_stopModifyListeningOnDoc(aInfo);
+
+ AutoRecovery::st_impl_removeFile(aInfo.OldTempURL);
+ AutoRecovery::st_impl_removeFile(aInfo.NewTempURL);
+ implts_flushConfigItem(aInfo, true); // sal_True => remove it from config
+}
+
+void AutoRecovery::implts_markDocumentModifiedAgainstLastBackup(const css::uno::Reference< css::frame::XModel >& xDocument)
+{
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
+
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
+ if (pIt != m_lDocCache.end())
+ {
+ /* Now we know, that this document was modified again and must be saved next time.
+ But we don't need this information for every e.g. key input of the user.
+ So we stop listening here.
+ But if the document was saved as temp. file we start listening for this event again.
+ */
+ implts_stopModifyListeningOnDoc(*pIt);
+ }
+
+ } /* SAFE */
+}
+
+void AutoRecovery::implts_updateModifiedState(const css::uno::Reference< css::frame::XModel >& xDocument)
+{
+ // use true as fallback to get every document on EmergencySave/AutoRecovery!
+ bool bModified = true;
+ css::uno::Reference< css::util::XModifiable > xModify(xDocument, css::uno::UNO_QUERY);
+ if (xModify.is())
+ bModified = xModify->isModified();
+
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
+
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
+ if (pIt != m_lDocCache.end())
+ {
+ AutoRecovery::TDocumentInfo& rInfo = *pIt;
+
+ if (bModified)
+ {
+ rInfo.DocumentState |= DocState::Modified;
+ }
+ else
+ {
+ rInfo.DocumentState &= ~DocState::Modified;
+ }
+ }
+
+ } /* SAFE */
+}
+
+void AutoRecovery::implts_updateDocumentUsedForSavingState(const css::uno::Reference< css::frame::XModel >& xDocument ,
+ bool bSaveInProgress)
+{
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
+
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
+ if (pIt == m_lDocCache.end())
+ return;
+ AutoRecovery::TDocumentInfo& rInfo = *pIt;
+ rInfo.UsedForSaving = bSaveInProgress;
+
+ } /* SAFE */
+}
+
+void AutoRecovery::implts_markDocumentAsSaved(const css::uno::Reference< css::frame::XModel >& xDocument)
+{
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
+
+ AutoRecovery::TDocumentInfo aInfo;
+ OUString sRemoveURL1;
+ OUString sRemoveURL2;
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
+ if (pIt == m_lDocCache.end())
+ return;
+ aInfo = *pIt;
+
+ /* Since the document has been saved, update its entry in the document
+ * cache. We essentially reset the state of the document from an
+ * autorecovery perspective, updating things like the filename (which
+ * would change in the case of a 'Save as' operation) and the associated
+ * backup file URL. */
+
+ aInfo.DocumentState = DocState::Unknown;
+ // TODO replace getLocation() with getURL() ... it's a workaround currently only!
+ css::uno::Reference< css::frame::XStorable > xDoc(aInfo.Document, css::uno::UNO_QUERY);
+ aInfo.OrgURL = xDoc->getLocation();
+
+ /* Save off the backup file URLs and then clear them. NOTE - it is
+ * important that we clear them - otherwise, we could enter a state
+ * where pIt->OldTempURL == pIt->NewTempURL and our backup algorithm
+ * in implts_saveOneDoc will write to that URL and then delete the file
+ * at that URL (bug #96607) */
+ sRemoveURL1 = aInfo.OldTempURL;
+ sRemoveURL2 = aInfo.NewTempURL;
+ aInfo.OldTempURL.clear();
+ aInfo.NewTempURL.clear();
+
+ utl::MediaDescriptor lDescriptor(aInfo.Document->getArgs());
+ aInfo.RealFilter = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME, OUString());
+
+ css::uno::Reference< css::frame::XTitle > xDocTitle(xDocument, css::uno::UNO_QUERY);
+ if (xDocTitle.is ())
+ aInfo.Title = xDocTitle->getTitle ();
+ else
+ {
+ aInfo.Title = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_TITLE, OUString());
+ if (aInfo.Title.isEmpty())
+ aInfo.Title = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_DOCUMENTTITLE, OUString());
+ }
+
+ aInfo.UsedForSaving = false;
+
+ *pIt = aInfo;
+
+ } /* SAFE */
+
+ implts_flushConfigItem(aInfo);
+
+ aCacheLock.unlock();
+
+ AutoRecovery::st_impl_removeFile(sRemoveURL1);
+ AutoRecovery::st_impl_removeFile(sRemoveURL2);
+}
+
+AutoRecovery::TDocumentList::iterator AutoRecovery::impl_searchDocument( AutoRecovery::TDocumentList& rList ,
+ const css::uno::Reference< css::frame::XModel >& xDocument)
+{
+ return std::find_if(rList.begin(), rList.end(),
+ [&xDocument](const AutoRecovery::TDocumentInfo& rInfo) { return rInfo.Document == xDocument; });
+}
+
+void lcl_changeVisibility( const css::uno::Reference< css::frame::XFramesSupplier >& i_rFrames, bool i_bVisible )
+{
+ css::uno::Reference< css::container::XIndexAccess > xFramesContainer = i_rFrames->getFrames();
+ const sal_Int32 count = xFramesContainer->getCount();
+
+ Any aElement;
+ for ( sal_Int32 i=0; i < count; ++i )
+ {
+ aElement = xFramesContainer->getByIndex(i);
+ // check for sub frames
+ css::uno::Reference< css::frame::XFramesSupplier > xFramesSupp( aElement, css::uno::UNO_QUERY );
+ if ( xFramesSupp.is() )
+ lcl_changeVisibility( xFramesSupp, i_bVisible );
+
+ css::uno::Reference< css::frame::XFrame > xFrame( aElement, css::uno::UNO_QUERY );
+ if ( !xFrame.is() )
+ continue;
+
+ css::uno::Reference< css::awt::XWindow > xWindow( xFrame->getContainerWindow(), UNO_SET_THROW );
+ xWindow->setVisible( i_bVisible );
+ }
+}
+
+void AutoRecovery::implts_changeAllDocVisibility(bool bVisible)
+{
+ css::uno::Reference< css::frame::XFramesSupplier > xDesktop = css::frame::Desktop::create(m_xContext);
+ lcl_changeVisibility( xDesktop, bVisible );
+}
+
+/* Currently the document is not closed in case of crash,
+ so the lock file must be removed explicitly
+*/
+void lc_removeLockFile(AutoRecovery::TDocumentInfo const & rInfo)
+{
+#if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT || HAVE_FEATURE_MACOSX_SANDBOX
+ (void) rInfo;
+#else
+ if ( !rInfo.Document.is() )
+ return;
+
+ try
+ {
+ css::uno::Reference< css::frame::XStorable > xStore(rInfo.Document, css::uno::UNO_QUERY_THROW);
+ OUString aURL = xStore->getLocation();
+ if ( !aURL.isEmpty() )
+ {
+ ::svt::DocumentLockFile aLockFile( aURL );
+ aLockFile.RemoveFile();
+ }
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+#endif
+}
+
+void AutoRecovery::implts_prepareSessionShutdown()
+{
+ SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_prepareSessionShutdown() starts ...");
+
+ // a) reset modified documents (of course the must be saved before this method is called!)
+ // b) close it without showing any UI!
+
+ /* SAFE */ {
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
+
+ for (auto & info : m_lDocCache)
+ {
+ // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly
+ // it is not done on documents saving since shutdown can be cancelled
+ lc_removeLockFile( info );
+
+ // Prevent us from deregistration of these documents.
+ // Because we close these documents by ourself (see XClosable below) ...
+ // it's fact, that we reach our deregistration method. There we
+ // must not(!) update our configuration ... Otherwise all
+ // session data are lost !!!
+ info.IgnoreClosing = true;
+
+ // reset modified flag of these documents (ignoring the notification about it!)
+ // Otherwise a message box is shown on closing these models.
+ implts_stopModifyListeningOnDoc(info);
+
+ // if the session save is still running the documents should not be thrown away,
+ // actually that would be a bad sign, that means that the SessionManager tries
+ // to kill the session before the saving is ready
+ if ((m_eJob & Job::SessionSave) != Job::SessionSave)
+ {
+ css::uno::Reference< css::util::XModifiable > xModify(info.Document, css::uno::UNO_QUERY);
+ if (xModify.is())
+ xModify->setModified(false);
+
+ // close the model.
+ css::uno::Reference< css::util::XCloseable > xClose(info.Document, css::uno::UNO_QUERY);
+ if (xClose.is())
+ {
+ try
+ {
+ xClose->close(false);
+ }
+ catch(const css::uno::Exception&)
+ {
+ // At least it's only a try to close these documents before anybody else it does.
+ // So it seems to be possible to ignore any error here .-)
+ }
+
+ info.Document.clear();
+ }
+ }
+ }
+
+ aCacheLock.unlock();
+ } /* SAFE */
+}
+
+/* TODO WORKAROUND:
+
+ #i64599#
+
+ Normally the MediaDescriptor argument NoAutoSave indicates,
+ that a document must be ignored for AutoSave and Recovery.
+ But sometimes XModel->getArgs() does not contained this information
+ if implts_registerDocument() was called.
+ So we have to check a second time, if this property is set...
+ Best place doing so is to check it immediately before saving
+ and suppressing saving the document then.
+ Of course removing the corresponding cache entry is not an option.
+ Because it would disturb iteration over the cache!
+ So we ignore such documents only...
+ Hopefully next time they are not inserted in our cache.
+*/
+bool lc_checkIfSaveForbiddenByArguments(AutoRecovery::TDocumentInfo const & rInfo)
+{
+ if (! rInfo.Document.is())
+ return true;
+
+ utl::MediaDescriptor lDescriptor(rInfo.Document->getArgs());
+ bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_NOAUTOSAVE, false);
+
+ return bNoAutoSave;
+}
+
+AutoRecovery::ETimerType AutoRecovery::implts_saveDocs( bool bAllowUserIdleLoop,
+ bool bRemoveLockFiles,
+ const DispatchParams* pParams )
+{
+ css::uno::Reference< css::task::XStatusIndicator > xExternalProgress;
+ if (pParams)
+ xExternalProgress = pParams->m_xProgress;
+
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(m_xContext);
+ OUString sBackupPath(SvtPathOptions().GetBackupPath());
+
+ css::uno::Reference< css::frame::XController > xActiveController;
+ css::uno::Reference< css::frame::XModel > xActiveModel;
+ css::uno::Reference< css::frame::XFrame > xActiveFrame = xDesktop->getActiveFrame();
+ if (xActiveFrame.is())
+ xActiveController = xActiveFrame->getController();
+ if (xActiveController.is())
+ xActiveModel = xActiveController->getModel();
+
+ // Set the default timer action for our call.
+ // Default = NORMAL_AUTOSAVE
+ // We return a suggestion for an active timer only.
+ // It will be ignored if the timer was disabled by the user ...
+ // Further this state can be set to USER_IDLE only later in this method.
+ // It's not allowed to reset such state then. Because we must know, if
+ // there exists POSTPONED documents. see below ...
+ AutoRecovery::ETimerType eTimer = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
+
+ Job eJob = m_eJob;
+
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
+
+ /* SAFE */ {
+ osl::ResettableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ // This list will be filled with every document
+ // which should be saved as last one. E.g. if it was used
+ // already for a UI save operation => crashed ... and
+ // now we try to save it again ... which can fail again ( of course .-) ).
+ ::std::vector< AutoRecovery::TDocumentList::iterator > lDangerousDocs;
+
+ AutoRecovery::TDocumentList::iterator pIt;
+ for ( pIt = m_lDocCache.begin();
+ pIt != m_lDocCache.end();
+ ++pIt )
+ {
+ AutoRecovery::TDocumentInfo aInfo = *pIt;
+
+ // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly
+ if ( bRemoveLockFiles )
+ lc_removeLockFile( aInfo );
+
+ // WORKAROUND ... see comment of this method
+ if (lc_checkIfSaveForbiddenByArguments(aInfo))
+ continue;
+
+ // already auto saved during this session :-)
+ // This state must be reset for all documents
+ // if timer is started with normal AutoSaveTimerIntervall!
+ if ((aInfo.DocumentState & DocState::Handled) == DocState::Handled)
+ continue;
+
+ // Not modified documents are not saved.
+ // We safe an information about the URL only!
+ Reference< XDocumentRecovery > xDocRecover( aInfo.Document, UNO_QUERY_THROW );
+ if ( !xDocRecover->wasModifiedSinceLastSave() )
+ {
+ aInfo.DocumentState |= DocState::Handled;
+ continue;
+ }
+
+ // check if this document is still used by a concurrent save operation
+ // e.g. if the user tried to save via UI.
+ // Handle it in the following way:
+ // i) For an AutoSave ... ignore this document! It will be saved and next time we will (hopefully)
+ // get a notification about the state of this operation.
+ // And if a document was saved by the user we can remove our temp. file. But that will be done inside
+ // our callback for SaveDone notification.
+ // ii) For a CrashSave ... add it to the list of dangerous documents and
+ // save it after all other documents was saved successfully. That decrease
+ // the chance for a crash inside a crash.
+ // On the other side it's not necessary for documents, which are not modified.
+ // They can be handled normally - means we patch the corresponding configuration entry only.
+ // iii) For a SessionSave ... ignore it! There is no time to wait for this save operation.
+ // Because the WindowManager will kill the process if it doesn't react immediately.
+ // On the other side we can't risk a concurrent save request ... because we know
+ // that it will produce a crash.
+
+ // Attention: Because eJob is used as a flag field, you have to check for the worst case first.
+ // E.g. a CrashSave can overwrite an AutoSave. So you have to check for a CrashSave before an AutoSave!
+ if (aInfo.UsedForSaving)
+ {
+ if ((eJob & Job::EmergencySave) == Job::EmergencySave)
+ {
+ lDangerousDocs.push_back(pIt);
+ continue;
+ }
+ else
+ if ((eJob & Job::SessionSave) == Job::SessionSave)
+ {
+ continue;
+ }
+ else
+ if ((eJob & Job::AutoSave) == Job::AutoSave)
+ {
+ eTimer = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED;
+ aInfo.DocumentState |= DocState::Postponed;
+ continue;
+ }
+ }
+
+ // a) Document was not postponed - and is active now. => postpone it (restart timer, restart loop)
+ // b) Document was not postponed - and is not active now. => save it
+ // c) Document was postponed - and is not active now. => save it
+ // d) Document was postponed - and is active now. => save it (because user idle was checked already)
+ bool bActive = (xActiveModel == aInfo.Document);
+ bool bWasPostponed = ((aInfo.DocumentState & DocState::Postponed) == DocState::Postponed);
+
+ if (
+ ! bWasPostponed &&
+ bActive
+ )
+ {
+ aInfo.DocumentState |= DocState::Postponed;
+ *pIt = aInfo;
+ // postponed documents will be saved if this method is called again!
+ // That can be done by an outside started timer => E_POLL_FOR_USER_IDLE (if normal AutoSave is active)
+ // or it must be done directly without starting any timer => E_CALL_ME_BACK (if Emergency- or SessionSave is active and must be finished ASAP!)
+ eTimer = AutoRecovery::E_POLL_FOR_USER_IDLE;
+ if (!bAllowUserIdleLoop)
+ eTimer = AutoRecovery::E_CALL_ME_BACK;
+ continue;
+ }
+
+ // b, c, d)
+ // } /* SAFE */
+ g.clear();
+ // changing of aInfo and flushing it is done inside implts_saveOneDoc!
+ implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress);
+ implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo));
+ g.reset();
+ // /* SAFE */ {
+
+ *pIt = aInfo;
+ }
+
+ // Did we have some "dangerous candidates" ?
+ // Try to save it ... but may be it will fail !
+ for (auto const& dangerousDoc : lDangerousDocs)
+ {
+ pIt = dangerousDoc;
+ AutoRecovery::TDocumentInfo aInfo = *pIt;
+
+ // } /* SAFE */
+ g.clear();
+ // changing of aInfo and flushing it is done inside implts_saveOneDoc!
+ implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress);
+ implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo));
+ g.reset();
+ // /* SAFE */ {
+
+ *pIt = aInfo;
+ }
+
+ } /* SAFE */
+
+ return eTimer;
+}
+
+void AutoRecovery::implts_saveOneDoc(const OUString& sBackupPath ,
+ AutoRecovery::TDocumentInfo& rInfo ,
+ const css::uno::Reference< css::task::XStatusIndicator >& xExternalProgress)
+{
+ // no document? => can occur if we loaded our configuration with files,
+ // which couldn't be recovered successfully. In such case we have all needed information
+ // excepting the real document instance!
+
+ // TODO: search right place, where such "dead files" can be removed from the configuration!
+ if (!rInfo.Document.is())
+ return;
+
+ utl::MediaDescriptor lOldArgs(rInfo.Document->getArgs());
+ implts_generateNewTempURL(sBackupPath, lOldArgs, rInfo);
+
+ // if the document was loaded with a password, it should be
+ // stored with password
+ utl::MediaDescriptor lNewArgs;
+ css::uno::Sequence< css::beans::NamedValue > aEncryptionData =
+ lOldArgs.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ENCRYPTIONDATA,
+ css::uno::Sequence< css::beans::NamedValue >());
+ if (aEncryptionData.hasElements())
+ lNewArgs[utl::MediaDescriptor::PROP_ENCRYPTIONDATA] <<= aEncryptionData;
+
+ // Further it must be saved using the default file format of that application.
+ // Otherwise we will some data lost.
+ if (!rInfo.DefaultFilter.isEmpty())
+ lNewArgs[utl::MediaDescriptor::PROP_FILTERNAME] <<= rInfo.DefaultFilter;
+
+ // prepare frame/document/mediadescriptor in a way, that it uses OUR progress .-)
+ if (xExternalProgress.is())
+ lNewArgs[utl::MediaDescriptor::PROP_STATUSINDICATOR] <<= xExternalProgress;
+ impl_establishProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >());
+
+ // #i66598# use special handling of property "DocumentBaseURL" (it must be an empty string!)
+ // for make hyperlinks working
+ lNewArgs[utl::MediaDescriptor::PROP_DOCUMENTBASEURL] <<= OUString();
+
+ lNewArgs[utl::MediaDescriptor::PROP_AUTOSAVEEVENT] <<= true;
+
+ // try to save this document as a new temp file every time.
+ // Mark AutoSave state as "INCOMPLETE" if it failed.
+ // Because the last temp file is too old and does not include all changes.
+ Reference< XDocumentRecovery > xDocRecover(rInfo.Document, css::uno::UNO_QUERY_THROW);
+
+ // safe the state about "trying to save"
+ // ... we need it for recovery if e.g. a crash occurs inside next line!
+ rInfo.DocumentState |= DocState::TrySave;
+ implts_flushConfigItem(rInfo);
+
+ // If userautosave is enabled, first try to save the original file.
+ // Note that we must do it *before* calling storeToRecoveryFile, so in case of failure here
+ // we won't remain with the modified flag set to true, even though the autorecovery save succeeded.
+ try
+ {
+ // We must check here for an empty URL to avoid a "This operation is not supported on this operating system."
+ // message during autosave.
+ if ((m_eJob & Job::UserAutoSave) == Job::UserAutoSave && !rInfo.OrgURL.isEmpty())
+ {
+ Reference< XStorable > xDocSave(rInfo.Document, css::uno::UNO_QUERY_THROW);
+ xDocSave->store();
+ }
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+
+ sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER;
+ bool bError = false;
+ do
+ {
+ try
+ {
+ xDocRecover->storeToRecoveryFile( rInfo.NewTempURL, lNewArgs.getAsConstPropertyValueList() );
+
+#ifdef TRIGGER_FULL_DISC_CHECK
+ throw css::uno::Exception("trigger full disk check");
+#else // TRIGGER_FULL_DISC_CHECK
+
+ bError = false;
+ nRetry = 0;
+#endif // TRIGGER_FULL_DISC_CHECK
+ }
+ catch(const css::uno::Exception& rException)
+ {
+ bError = true;
+
+ // skip saving XLSX with protected sheets, if their passwords haven't supported yet
+ if ( rException.Message.startsWith("SfxBaseModel::impl_store") )
+ {
+ const css::task::ErrorCodeIOException& pErrorCodeIOException =
+ static_cast<const css::task::ErrorCodeIOException&>(rException);
+ if ( static_cast<ErrCode>(pErrorCodeIOException.ErrCode) == ERRCODE_SFX_WRONGPASSWORD )
+ {
+ // stop and remove the bad temporary file, instead of filling the disk with them
+ bError = false;
+ break;
+ }
+ }
+
+ // a) FULL DISC seems to be the problem behind => show error and retry it forever (e.g. retry=300)
+ // b) unknown problem (may be locking problem) => reset RETRY value to more useful value(!) (e.g. retry=3)
+ // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace !
+
+ sal_Int32 nMinSpaceDocSave;
+ /* SAFE */ {
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ nMinSpaceDocSave = m_nMinSpaceDocSave;
+ } /* SAFE */
+
+ if (! impl_enoughDiscSpace(nMinSpaceDocSave))
+ AutoRecovery::impl_showFullDiscError();
+ else if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL)
+ nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL;
+ else if (nRetry <= GIVE_UP_RETRY)
+ throw; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!!
+
+ --nRetry;
+ }
+ }
+ while(nRetry>0);
+
+ if (! bError)
+ {
+ // safe the state about success
+ // ... you know the reason: to know it on recovery time if next line crash .-)
+ rInfo.DocumentState &= ~DocState::TrySave;
+ rInfo.DocumentState |= DocState::Handled;
+ rInfo.DocumentState |= DocState::Succeeded;
+ }
+ else
+ {
+ // safe the state about error ...
+ rInfo.NewTempURL.clear();
+ rInfo.DocumentState &= ~DocState::TrySave;
+ rInfo.DocumentState |= DocState::Handled;
+ rInfo.DocumentState |= DocState::Incomplete;
+ }
+
+ // make sure the progress is not referred any longer
+ impl_forgetProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >());
+
+ // try to remove the old temp file.
+ // Ignore any error here. We have a new temp file, which is up to date.
+ // The only thing is: we fill the disk with temp files, if we can't remove old ones :-)
+ OUString sRemoveFile = rInfo.OldTempURL;
+ rInfo.OldTempURL = rInfo.NewTempURL;
+ rInfo.NewTempURL.clear();
+
+ implts_flushConfigItem(rInfo);
+
+ // We must know if the user modifies the document again ...
+ implts_startModifyListeningOnDoc(rInfo);
+
+ AutoRecovery::st_impl_removeFile(sRemoveFile);
+}
+
+AutoRecovery::ETimerType AutoRecovery::implts_openDocs(const DispatchParams& aParams)
+{
+ AutoRecovery::ETimerType eTimer = AutoRecovery::E_DONT_START_TIMER;
+
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
+
+ /* SAFE */ {
+ osl::ResettableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ Job eJob = m_eJob;
+ for (auto & info : m_lDocCache)
+ {
+ // Such documents are already loaded by the last loop.
+ // Don't check DocState::Succeeded here! It may be the final state of an AutoSave
+ // operation before!!!
+ if ((info.DocumentState & DocState::Handled) == DocState::Handled)
+ continue;
+
+ // a1,b1,c1,d2,e2,f2)
+ if ((info.DocumentState & DocState::Damaged) == DocState::Damaged)
+ {
+ // don't forget to inform listener! May be this document was
+ // damaged on last saving time ...
+ // Then our listener need this notification.
+ // If it was damaged during last "try to open" ...
+ // it will be notified more than once. SH.. HAPPENS ...
+ // } /* SAFE */
+ g.clear();
+ implts_informListener(eJob,
+ AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &info));
+ g.reset();
+ // /* SAFE */ {
+ continue;
+ }
+
+ utl::MediaDescriptor lDescriptor;
+
+ // it's a UI feature - so the "USER" itself must be set as referrer
+ lDescriptor[utl::MediaDescriptor::PROP_REFERRER] <<= OUString(REFERRER_USER);
+ lDescriptor[utl::MediaDescriptor::PROP_SALVAGEDFILE] <<= OUString();
+
+ // recovered documents are loaded hidden, and shown all at once, later
+ lDescriptor[utl::MediaDescriptor::PROP_HIDDEN] <<= true;
+
+ if (aParams.m_xProgress.is())
+ lDescriptor[utl::MediaDescriptor::PROP_STATUSINDICATOR] <<= aParams.m_xProgress;
+
+ bool bBackupWasTried = (
+ ((info.DocumentState & DocState::TryLoadBackup ) == DocState::TryLoadBackup) || // temp. state!
+ ((info.DocumentState & DocState::Incomplete ) == DocState::Incomplete ) // transport DocState::TryLoadBackup from last loop to this new one!
+ );
+ bool bOriginalWasTried = ((info.DocumentState & DocState::TryLoadOriginal) == DocState::TryLoadOriginal);
+
+ if (bBackupWasTried)
+ {
+ if (!bOriginalWasTried)
+ {
+ info.DocumentState |= DocState::Incomplete;
+ // try original URL ... ! don't continue with next item here ...
+ }
+ else
+ {
+ info.DocumentState |= DocState::Damaged;
+ continue;
+ }
+ }
+
+ OUString sLoadOriginalURL;
+ OUString sLoadBackupURL;
+
+ if (!bBackupWasTried)
+ sLoadBackupURL = info.OldTempURL;
+
+ if (!info.OrgURL.isEmpty())
+ {
+ sLoadOriginalURL = info.OrgURL;
+ }
+ else if (!info.TemplateURL.isEmpty())
+ {
+ sLoadOriginalURL = info.TemplateURL;
+ lDescriptor[utl::MediaDescriptor::PROP_ASTEMPLATE] <<= true;
+ lDescriptor[utl::MediaDescriptor::PROP_TEMPLATENAME] <<= info.TemplateURL;
+ }
+ else if (!info.FactoryURL.isEmpty())
+ {
+ sLoadOriginalURL = info.FactoryURL;
+ lDescriptor[utl::MediaDescriptor::PROP_ASTEMPLATE] <<= true;
+ }
+
+ // A "Salvaged" item must exists every time. The core can make something special then for recovery.
+ // Of course it should be the real file name of the original file, in case we load the temp. backup here.
+ OUString sURL;
+ if (!sLoadBackupURL.isEmpty())
+ {
+ sURL = sLoadBackupURL;
+ info.DocumentState |= DocState::TryLoadBackup;
+ lDescriptor[utl::MediaDescriptor::PROP_SALVAGEDFILE] <<= sLoadOriginalURL;
+ }
+ else if (!sLoadOriginalURL.isEmpty())
+ {
+ sURL = sLoadOriginalURL;
+ info.DocumentState |= DocState::TryLoadOriginal;
+ }
+ else
+ continue; // TODO ERROR!
+
+ LoadEnv::initializeUIDefaults( m_xContext, lDescriptor, true, nullptr );
+
+ // } /* SAFE */
+ g.clear();
+
+ implts_flushConfigItem(info);
+ implts_informListener(eJob,
+ AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &info));
+
+ try
+ {
+ implts_openOneDoc(sURL, lDescriptor, info);
+ }
+ catch(const css::uno::Exception&)
+ {
+ info.DocumentState &= ~DocState::TryLoadBackup;
+ info.DocumentState &= ~DocState::TryLoadOriginal;
+ if (!sLoadBackupURL.isEmpty())
+ {
+ info.DocumentState |= DocState::Incomplete;
+ eTimer = AutoRecovery::E_CALL_ME_BACK;
+ }
+ else
+ {
+ info.DocumentState |= DocState::Handled;
+ info.DocumentState |= DocState::Damaged;
+ }
+
+ implts_flushConfigItem(info, true);
+ implts_informListener(eJob,
+ AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &info));
+
+ // /* SAFE */ {
+ // Needed for next loop!
+ g.reset();
+ continue;
+ }
+
+ if (!info.RealFilter.isEmpty())
+ {
+ utl::MediaDescriptor lPatchDescriptor(info.Document->getArgs());
+ lPatchDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= info.RealFilter;
+ info.Document->attachResource(info.Document->getURL(), lPatchDescriptor.getAsConstPropertyValueList());
+ // do *not* use sURL here. In case this points to the recovery file, it has already been passed
+ // to recoverFromFile. Also, passing it here is logically wrong, as attachResource is intended
+ // to take the logical file URL.
+ }
+
+ css::uno::Reference< css::util::XModifiable > xModify(info.Document, css::uno::UNO_QUERY);
+ if ( xModify.is() )
+ {
+ bool bModified = ((info.DocumentState & DocState::Modified) == DocState::Modified);
+ xModify->setModified(bModified);
+ }
+
+ info.DocumentState &= ~DocState::TryLoadBackup;
+ info.DocumentState &= ~DocState::TryLoadOriginal;
+ info.DocumentState |= DocState::Handled;
+ info.DocumentState |= DocState::Succeeded;
+
+ implts_flushConfigItem(info);
+ implts_informListener(eJob,
+ AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &info));
+
+ /* Normally we listen as XModifyListener on a document to know if a document was changed
+ since our last AutoSave. And we deregister us in case we know this state.
+ But directly after one document as recovered ... we must start listening.
+ Otherwise the first "modify" doesn't reach us. Because we ourself called setModified()
+ on the document via API. And currently we don't listen for any events (not at theGlobalEventBroadcaster
+ nor at any document!).
+ */
+ implts_startModifyListeningOnDoc(info);
+
+ // /* SAFE */ {
+ // Needed for next loop. Don't unlock it again!
+ g.reset();
+ }
+
+ } /* SAFE */
+
+ return eTimer;
+}
+
+void AutoRecovery::implts_openOneDoc(const OUString& sURL ,
+ utl::MediaDescriptor& lDescriptor,
+ AutoRecovery::TDocumentInfo& rInfo )
+{
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(m_xContext);
+
+ ::std::vector< Reference< XComponent > > aCleanup;
+ try
+ {
+ // create a new document of the desired type
+ Reference< XModel2 > xModel(m_xContext->getServiceManager()->createInstanceWithContext(
+ rInfo.FactoryService, m_xContext), UNO_QUERY_THROW);
+ aCleanup.emplace_back(xModel.get() );
+
+ // put the filter name into the descriptor - we're not going to involve any type detection, so
+ // the document might be lost without the FilterName property
+ if ( (rInfo.DocumentState & DocState::TryLoadOriginal) == DocState::TryLoadOriginal)
+ lDescriptor[ utl::MediaDescriptor::PROP_FILTERNAME ] <<= rInfo.RealFilter;
+ else
+ lDescriptor[ utl::MediaDescriptor::PROP_FILTERNAME ] <<= rInfo.DefaultFilter;
+
+ if ( sURL == rInfo.FactoryURL )
+ {
+ // if the document was a new, unmodified document, then there's nothing to recover, just to init
+ ENSURE_OR_THROW( ( rInfo.DocumentState & DocState::Modified ) == DocState(0),
+ "unexpected document state" );
+ Reference< XLoadable > xModelLoad( xModel, UNO_QUERY_THROW );
+ xModelLoad->initNew();
+
+ // TODO: remove load-process specific arguments from the descriptor, e.g. the status indicator
+ xModel->attachResource( sURL, lDescriptor.getAsConstPropertyValueList() );
+ }
+ else
+ {
+ OUString sFilterName;
+ lDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] >>= sFilterName;
+ if (!sFilterName.isEmpty()
+ && ( sFilterName == "Calc MS Excel 2007 XML"
+ || sFilterName == "Impress MS PowerPoint 2007 XML"
+ || sFilterName == "MS Word 2007 XML"))
+ // TODO: Probably need to check other affected formats + templates?
+ {
+ // tdf#129096: in case of recovery of password protected OOXML document it is done not
+ // the same way as ordinal loading. Inside XDocumentRecovery::recoverFromFile
+ // there is a call to XFilter::filter which has constant media descriptor and thus
+ // all encryption data used in document is lost. To avoid this try to walkaround
+ // with explicit call to FormatDetector. It will try to load document, prompt for password
+ // and store this info in media descriptor we will use for recoverFromFile call.
+ Reference< css::document::XExtendedFilterDetection > xDetection(
+ m_xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.comp.oox.FormatDetector", m_xContext),
+ UNO_QUERY_THROW);
+ lDescriptor[utl::MediaDescriptor::PROP_URL] <<= sURL;
+ Sequence< css::beans::PropertyValue > aDescriptorSeq = lDescriptor.getAsConstPropertyValueList();
+ OUString sType = xDetection->detect(aDescriptorSeq);
+
+ OUString sNewFilterName;
+ lDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] >>= sNewFilterName;
+ if (!sType.isEmpty() && sNewFilterName == sFilterName)
+ {
+ // Filter detection was okay, update media descriptor with one received from FilterDetect
+ lDescriptor = aDescriptorSeq;
+ }
+ }
+
+ // let it recover itself
+ Reference< XDocumentRecovery > xDocRecover( xModel, UNO_QUERY_THROW );
+ xDocRecover->recoverFromFile(
+ sURL,
+ lDescriptor.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_SALVAGEDFILE, OUString() ),
+ lDescriptor.getAsConstPropertyValueList()
+ );
+
+ // No attachResource needed here. By definition (of XDocumentRecovery), the implementation is responsible
+ // for completely initializing the model, which includes attachResource (or equivalent), if required.
+ }
+
+ // re-create all the views
+ ::std::vector< OUString > aViewsToRestore( std::cbegin(rInfo.ViewNames), std::cend(rInfo.ViewNames) );
+ // if we don't have views for whatever reason, then create a default-view, at least
+ if ( aViewsToRestore.empty() )
+ aViewsToRestore.emplace_back( );
+
+ for (auto const& viewToRestore : aViewsToRestore)
+ {
+ // create a frame
+ Reference< XFrame > xTargetFrame = xDesktop->findFrame( SPECIALTARGET_BLANK, 0 );
+ aCleanup.emplace_back(xTargetFrame.get() );
+
+ // create a view to the document
+ Reference< XController2 > xController;
+ if ( viewToRestore.getLength() )
+ {
+ xController.set( xModel->createViewController( viewToRestore, Sequence< css::beans::PropertyValue >(), xTargetFrame ), UNO_SET_THROW );
+ }
+ else
+ {
+ xController.set( xModel->createDefaultViewController( xTargetFrame ), UNO_SET_THROW );
+ }
+
+ // introduce model/view/controller to each other
+ xController->attachModel( xModel );
+ xModel->connectController( xController );
+ xTargetFrame->setComponent( xController->getComponentWindow(), xController );
+ xController->attachFrame( xTargetFrame );
+ xModel->setCurrentController( xController );
+ }
+
+ rInfo.Document = xModel.get();
+ }
+ catch(const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch(const css::uno::Exception&)
+ {
+ Any aCaughtException( ::cppu::getCaughtException() );
+
+ // clean up
+ for (auto const& component : aCleanup)
+ {
+ css::uno::Reference< css::util::XCloseable > xClose(component, css::uno::UNO_QUERY);
+ if ( xClose.is() )
+ xClose->close( true );
+ else
+ component->dispose();
+ }
+
+ // re-throw
+ throw css::lang::WrappedTargetException(
+ "Recovery of \"" + sURL + "\" failed.",
+ static_cast< css::frame::XDispatch* >(this),
+ aCaughtException
+ );
+ }
+}
+
+void AutoRecovery::implts_generateNewTempURL(const OUString& sBackupPath ,
+ utl::MediaDescriptor& /*rMediaDescriptor*/,
+ AutoRecovery::TDocumentInfo& rInfo )
+{
+ // specify URL for saving (which points to a temp file inside backup directory)
+ // and define a unique name, so we can locate it later.
+ // This unique name must solve an optimization problem too!
+ // In case we are asked to save unmodified documents too - and one of them
+ // is an empty one (because it was new created using e.g. a URL private:factory/...)
+ // we should not save it really. Then we put the information about such "empty document"
+ // into the configuration and don't create any recovery file on disk.
+ // We use the title of the document to make it unique.
+ OUStringBuffer sUniqueName;
+ if (!rInfo.OrgURL.isEmpty())
+ {
+ css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
+ css::util::URL aURL;
+ aURL.Complete = rInfo.OrgURL;
+ xParser->parseStrict(aURL);
+ sUniqueName.append(aURL.Name);
+ }
+ else if (!rInfo.FactoryURL.isEmpty())
+ sUniqueName.append("untitled");
+ sUniqueName.append("_");
+
+ // TODO: Must we strip some illegal signes - if we use the title?
+
+ OUString sName(sUniqueName.makeStringAndClear());
+ OUString sExtension(rInfo.Extension);
+ OUString sPath(sBackupPath);
+ ::utl::TempFile aTempFile(sName, true, &sExtension, &sPath, true);
+
+ rInfo.NewTempURL = aTempFile.GetURL();
+}
+
+void AutoRecovery::implts_informListener( Job eJob ,
+ const css::frame::FeatureStateEvent& aEvent)
+{
+ // Helper shares mutex with us -> threadsafe!
+ ::comphelper::OInterfaceContainerHelper3<css::frame::XStatusListener>* pListenerForURL = nullptr;
+ OUString sJob = AutoRecovery::implst_getJobDescription(eJob);
+
+ // inform listener, which are registered for any URLs(!)
+ pListenerForURL = m_lListener.getContainer(sJob);
+ if(pListenerForURL == nullptr)
+ return;
+
+ ::comphelper::OInterfaceIteratorHelper3 pIt(*pListenerForURL);
+ while(pIt.hasMoreElements())
+ {
+ try
+ {
+ pIt.next()->statusChanged(aEvent);
+ }
+ catch(const css::uno::RuntimeException&)
+ {
+ pIt.remove();
+ }
+ }
+}
+
+OUString AutoRecovery::implst_getJobDescription(Job eJob)
+{
+ // describe the current running operation
+ OUStringBuffer sFeature(256);
+ sFeature.append(CMD_PROTOCOL);
+
+ // Attention: Because "eJob" is used as a flag field the order of checking these
+ // flags is important. We must prefer job with higher priorities!
+ // E.g. EmergencySave has an higher prio then AutoSave ...
+ // On the other side there exist a well defined order between two different jobs.
+ // e.g. PrepareEmergencySave must be done before EmergencySave is started of course.
+
+ if ((eJob & Job::PrepareEmergencySave) == Job::PrepareEmergencySave)
+ sFeature.append(CMD_DO_PREPARE_EMERGENCY_SAVE);
+ else if ((eJob & Job::EmergencySave) == Job::EmergencySave)
+ sFeature.append(CMD_DO_EMERGENCY_SAVE);
+ else if ((eJob & Job::Recovery) == Job::Recovery)
+ sFeature.append(CMD_DO_RECOVERY);
+ else if ((eJob & Job::SessionSave) == Job::SessionSave)
+ sFeature.append(CMD_DO_SESSION_SAVE);
+ else if ((eJob & Job::SessionQuietQuit) == Job::SessionQuietQuit)
+ sFeature.append(CMD_DO_SESSION_QUIET_QUIT);
+ else if ((eJob & Job::SessionRestore) == Job::SessionRestore)
+ sFeature.append(CMD_DO_SESSION_RESTORE);
+ else if ((eJob & Job::EntryBackup) == Job::EntryBackup)
+ sFeature.append(CMD_DO_ENTRY_BACKUP);
+ else if ((eJob & Job::EntryCleanup) == Job::EntryCleanup)
+ sFeature.append(CMD_DO_ENTRY_CLEANUP);
+ else if ((eJob & Job::AutoSave) == Job::AutoSave)
+ sFeature.append(CMD_DO_AUTO_SAVE);
+ else if ( eJob != Job::NoJob )
+ SAL_INFO("fwk.autorecovery", "AutoRecovery::implst_getJobDescription(): Invalid job identifier detected.");
+
+ return sFeature.makeStringAndClear();
+}
+
+Job AutoRecovery::implst_classifyJob(const css::util::URL& aURL)
+{
+ if ( aURL.Protocol == CMD_PROTOCOL )
+ {
+ if ( aURL.Path == CMD_DO_PREPARE_EMERGENCY_SAVE )
+ return Job::PrepareEmergencySave;
+ else if ( aURL.Path == CMD_DO_EMERGENCY_SAVE )
+ return Job::EmergencySave;
+ else if ( aURL.Path == CMD_DO_RECOVERY )
+ return Job::Recovery;
+ else if ( aURL.Path == CMD_DO_ENTRY_BACKUP )
+ return Job::EntryBackup;
+ else if ( aURL.Path == CMD_DO_ENTRY_CLEANUP )
+ return Job::EntryCleanup;
+ else if ( aURL.Path == CMD_DO_SESSION_SAVE )
+ return Job::SessionSave;
+ else if ( aURL.Path == CMD_DO_SESSION_QUIET_QUIT )
+ return Job::SessionQuietQuit;
+ else if ( aURL.Path == CMD_DO_SESSION_RESTORE )
+ return Job::SessionRestore;
+ else if ( aURL.Path == CMD_DO_DISABLE_RECOVERY )
+ return Job::DisableAutorecovery;
+ else if ( aURL.Path == CMD_DO_SET_AUTOSAVE_STATE )
+ return Job::SetAutosaveState;
+ }
+
+ SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_classifyJob(): Invalid URL (protocol).");
+ return Job::NoJob;
+}
+
+css::frame::FeatureStateEvent AutoRecovery::implst_createFeatureStateEvent( Job eJob ,
+ const OUString& sEventType,
+ AutoRecovery::TDocumentInfo const * pInfo )
+{
+ css::frame::FeatureStateEvent aEvent;
+ aEvent.FeatureURL.Complete = AutoRecovery::implst_getJobDescription(eJob);
+ aEvent.FeatureDescriptor = sEventType;
+
+ if (pInfo && sEventType == OPERATION_UPDATE)
+ {
+ // pack rInfo for transport via UNO
+ ::comphelper::NamedValueCollection aInfo;
+ aInfo.put( OUString(CFG_ENTRY_PROP_ID), pInfo->ID );
+ aInfo.put( OUString(CFG_ENTRY_PROP_ORIGINALURL), pInfo->OrgURL );
+ aInfo.put( OUString(CFG_ENTRY_PROP_FACTORYURL), pInfo->FactoryURL );
+ aInfo.put( OUString(CFG_ENTRY_PROP_TEMPLATEURL), pInfo->TemplateURL );
+ aInfo.put( OUString(CFG_ENTRY_PROP_TEMPURL), pInfo->OldTempURL.isEmpty() ? pInfo->NewTempURL : pInfo->OldTempURL );
+ aInfo.put( OUString(CFG_ENTRY_PROP_MODULE), pInfo->AppModule);
+ aInfo.put( OUString(CFG_ENTRY_PROP_TITLE), pInfo->Title);
+ aInfo.put( OUString(CFG_ENTRY_PROP_VIEWNAMES), pInfo->ViewNames);
+ aInfo.put( OUString(CFG_ENTRY_PROP_DOCUMENTSTATE), sal_Int32(pInfo->DocumentState));
+
+ aEvent.State <<= aInfo.getPropertyValues();
+ }
+
+ return aEvent;
+}
+
+void AutoRecovery::implts_resetHandleStates()
+{
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
+
+ /* SAFE */ {
+ osl::ResettableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ for (auto & info : m_lDocCache)
+ {
+ info.DocumentState &= ~DocState::Handled;
+ info.DocumentState &= ~DocState::Postponed;
+
+ // } /* SAFE */
+ g.clear();
+ implts_flushConfigItem(info);
+ g.reset();
+ // /* SAFE */ {
+ }
+ } /* SAFE */
+}
+
+void AutoRecovery::implts_prepareEmergencySave()
+{
+ // Be sure to know all open documents really .-)
+ implts_verifyCacheAgainstDesktopDocumentList();
+
+ // hide all docs, so the user can't disturb our emergency save .-)
+ implts_changeAllDocVisibility(false);
+}
+
+void AutoRecovery::implts_doEmergencySave(const DispatchParams& aParams)
+{
+ // Write a hint "we crashed" into the configuration, so
+ // the error report tool is started too in case no recovery
+ // documents exists and was saved.
+
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Recovery::RecoveryInfo::Crashed::set(true, batch);
+ batch->commit();
+
+ // for all docs, store their current view/names in the configuration
+ implts_persistAllActiveViewNames();
+
+ // The called method for saving documents runs
+ // during normal AutoSave more than once. Because
+ // it postpone active documents and save it later.
+ // That is normally done by recalling it from a timer.
+ // Here we must do it immediately!
+ // Of course this method returns the right state -
+ // because it knows, that we are running in EMERGENCY SAVE mode .-)
+
+ bool const bAllowUserIdleLoop = false; // not allowed to change that .-)
+ AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
+ do
+ {
+ eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, true, &aParams);
+ }
+ while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
+
+ // reset the handle state of all
+ // cache items. Such handle state indicates, that a document
+ // was already saved during the THIS(!) EmergencySave session.
+ // Of course following recovery session must be started without
+ // any "handle" state ...
+ implts_resetHandleStates();
+
+ // flush config cached back to disc.
+ impl_flushALLConfigChanges();
+
+ // try to make sure next time office will be started user won't be
+ // notified about any other might be running office instance
+ // remove ".lock" file from disc !
+ AutoRecovery::st_impl_removeLockFile();
+}
+
+void AutoRecovery::implts_doRecovery(const DispatchParams& aParams)
+{
+ AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
+ do
+ {
+ eSuggestedTimer = implts_openDocs(aParams);
+ }
+ while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
+
+ // reset the handle state of all
+ // cache items. Such handle state indicates, that a document
+ // was already saved during the THIS(!) Recovery session.
+ // Of course a may be following EmergencySave session must be started without
+ // any "handle" state...
+ implts_resetHandleStates();
+
+ // Reset the configuration hint "we were crashed"!
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Recovery::RecoveryInfo::Crashed::set(false, batch);
+ batch->commit();
+}
+
+void AutoRecovery::implts_doSessionSave(const DispatchParams& aParams)
+{
+ SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_doSessionSave()");
+
+ // Be sure to know all open documents really .-)
+ implts_verifyCacheAgainstDesktopDocumentList();
+
+ // for all docs, store their current view/names in the configuration
+ implts_persistAllActiveViewNames();
+
+ // The called method for saving documents runs
+ // during normal AutoSave more than once. Because
+ // it postpone active documents and save it later.
+ // That is normally done by recalling it from a timer.
+ // Here we must do it immediately!
+ // Of course this method returns the right state -
+ // because it knows, that we are running in SESSION SAVE mode .-)
+
+ bool const bAllowUserIdleLoop = false; // not allowed to change that .-)
+ AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
+ do
+ {
+ // do not remove lock files of the documents, it will be done on session quit
+ eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, false, &aParams);
+ }
+ while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
+
+ // reset the handle state of all
+ // cache items. Such handle state indicates, that a document
+ // was already saved during the THIS(!) save session.
+ // Of course following restore session must be started without
+ // any "handle" state ...
+ implts_resetHandleStates();
+
+ // flush config cached back to disc.
+ impl_flushALLConfigChanges();
+}
+
+void AutoRecovery::implts_doSessionQuietQuit()
+{
+ SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_doSessionQuietQuit()");
+
+ // try to make sure next time office will be started user won't be
+ // notified about any other might be running office instance
+ // remove ".lock" file from disc!
+ // it is done as a first action for session save since Gnome sessions
+ // do not provide enough time for shutdown, and the dialog looks to be
+ // confusing for the user
+ AutoRecovery::st_impl_removeLockFile();
+
+ // reset all modified documents, so the don't show any UI on closing ...
+ // and close all documents, so we can shutdown the OS!
+ implts_prepareSessionShutdown();
+
+ // Write a hint for "stored session data" into the configuration, so
+ // the on next startup we know what's happen last time
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Recovery::RecoveryInfo::SessionData::set(true, batch);
+ batch->commit();
+
+ // flush config cached back to disc.
+ impl_flushALLConfigChanges();
+}
+
+void AutoRecovery::implts_doSessionRestore(const DispatchParams& aParams)
+{
+ SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_doSessionRestore() ...");
+
+ AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
+ do
+ {
+ eSuggestedTimer = implts_openDocs(aParams);
+ }
+ while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
+
+ // reset the handle state of all
+ // cache items. Such handle state indicates, that a document
+ // was already saved during the THIS(!) Restore session.
+ // Of course a may be following save session must be started without
+ // any "handle" state ...
+ implts_resetHandleStates();
+
+ // make all opened documents visible
+ implts_changeAllDocVisibility(true);
+
+ // Reset the configuration hint for "session save"!
+ SAL_INFO("fwk.autorecovery", "... reset config key 'SessionData'");
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Recovery::RecoveryInfo::SessionData::set(false, batch);
+ batch->commit();
+
+ SAL_INFO("fwk.autorecovery", "... AutoRecovery::implts_doSessionRestore()");
+}
+
+void AutoRecovery::implts_backupWorkingEntry(const DispatchParams& aParams)
+{
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
+
+ for (auto const& info : m_lDocCache)
+ {
+ if (info.ID != aParams.m_nWorkingEntryID)
+ continue;
+
+ OUString sSourceURL;
+ // Prefer temp file. It contains the changes against the original document!
+ if (!info.OldTempURL.isEmpty())
+ sSourceURL = info.OldTempURL;
+ else if (!info.NewTempURL.isEmpty())
+ sSourceURL = info.NewTempURL;
+ else if (!info.OrgURL.isEmpty())
+ sSourceURL = info.OrgURL;
+ else
+ continue; // nothing real to save! An unmodified but new created document.
+
+ INetURLObject aParser(sSourceURL);
+ // AutoRecovery::EFailureSafeResult eResult =
+ implts_copyFile(sSourceURL, aParams.m_sSavePath, aParser.getName());
+
+ // TODO: Check eResult and react for errors (InteractionHandler!?)
+ // Currently we ignore it ...
+ // DON'T UPDATE THE CACHE OR REMOVE ANY TEMP. FILES FROM DISK.
+ // That has to be forced from outside explicitly.
+ // See implts_cleanUpWorkingEntry() for further details.
+ }
+}
+
+void AutoRecovery::implts_cleanUpWorkingEntry(const DispatchParams& aParams)
+{
+ CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
+
+ AutoRecovery::TDocumentList::iterator pIt = std::find_if(m_lDocCache.begin(), m_lDocCache.end(),
+ [&aParams](const AutoRecovery::TDocumentInfo& rInfo) { return rInfo.ID == aParams.m_nWorkingEntryID; });
+ if (pIt != m_lDocCache.end())
+ {
+ AutoRecovery::TDocumentInfo& rInfo = *pIt;
+ AutoRecovery::st_impl_removeFile(rInfo.OldTempURL);
+ AutoRecovery::st_impl_removeFile(rInfo.NewTempURL);
+ implts_flushConfigItem(rInfo, true); // sal_True => remove it from xml config!
+
+ m_lDocCache.erase(pIt);
+ }
+}
+
+AutoRecovery::EFailureSafeResult AutoRecovery::implts_copyFile(const OUString& sSource ,
+ const OUString& sTargetPath,
+ const OUString& sTargetName)
+{
+ // create content for the parent folder and call transfer on that content with the source content
+ // and the destination file name as parameters
+
+ css::uno::Reference< css::ucb::XCommandEnvironment > xEnvironment;
+
+ ::ucbhelper::Content aSourceContent;
+ ::ucbhelper::Content aTargetContent;
+
+ try
+ {
+ aTargetContent = ::ucbhelper::Content(sTargetPath, xEnvironment, m_xContext);
+ }
+ catch(const css::uno::Exception&)
+ {
+ return AutoRecovery::E_WRONG_TARGET_PATH;
+ }
+
+ sal_Int32 nNameClash;
+ nNameClash = css::ucb::NameClash::RENAME;
+
+ try
+ {
+ bool bSuccess = ::ucbhelper::Content::create(sSource, xEnvironment, m_xContext, aSourceContent);
+ if (!bSuccess)
+ return AutoRecovery::E_ORIGINAL_FILE_MISSING;
+ aTargetContent.transferContent(aSourceContent, ::ucbhelper::InsertOperation::Copy, sTargetName, nNameClash);
+ }
+ catch(const css::uno::Exception&)
+ {
+ return AutoRecovery::E_ORIGINAL_FILE_MISSING;
+ }
+
+ return AutoRecovery::E_COPIED;
+}
+
+sal_Bool SAL_CALL AutoRecovery::convertFastPropertyValue( css::uno::Any& /*aConvertedValue*/,
+ css::uno::Any& /*aOldValue*/ ,
+ sal_Int32 /*nHandle*/ ,
+ const css::uno::Any& /*aValue*/ )
+{
+ // not needed currently
+ return false;
+}
+
+void SAL_CALL AutoRecovery::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/,
+ const css::uno::Any& /*aValue*/ )
+{
+ // not needed currently
+}
+
+void SAL_CALL AutoRecovery::getFastPropertyValue(css::uno::Any& aValue ,
+ sal_Int32 nHandle) const
+{
+ switch(nHandle)
+ {
+ case AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA :
+ {
+ bool bSessionData = officecfg::Office::Recovery::RecoveryInfo::SessionData::get();
+ bool bRecoveryData = !m_lDocCache.empty();
+
+ // exists session data ... => then we can't say, that these
+ // data are valid for recovery. So we have to return sal_False then!
+ if (bSessionData)
+ bRecoveryData = false;
+
+ aValue <<= bRecoveryData;
+ }
+ break;
+
+ case AUTORECOVERY_PROPHANDLE_CRASHED :
+ aValue <<= officecfg::Office::Recovery::RecoveryInfo::Crashed::get();
+ break;
+
+ case AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA :
+ aValue <<= officecfg::Office::Recovery::RecoveryInfo::SessionData::get();
+ break;
+ }
+}
+
+css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor()
+{
+ return
+ {
+ css::beans::Property( AUTORECOVERY_PROPNAME_CRASHED , AUTORECOVERY_PROPHANDLE_CRASHED , cppu::UnoType<bool>::get() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
+ css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_RECOVERYDATA, AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA, cppu::UnoType<bool>::get() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
+ css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_SESSIONDATA , AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA , cppu::UnoType<bool>::get() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
+ };
+}
+
+::cppu::IPropertyArrayHelper& SAL_CALL AutoRecovery::getInfoHelper()
+{
+ static ::cppu::OPropertyArrayHelper ourInfoHelper(impl_getStaticPropertyDescriptor(), true);
+
+ return ourInfoHelper;
+}
+
+css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL AutoRecovery::getPropertySetInfo()
+{
+ static css::uno::Reference< css::beans::XPropertySetInfo > xInfo(
+ ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()));
+
+ return xInfo;
+}
+
+void AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList()
+{
+ SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList() ...");
+ try
+ {
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(m_xContext);
+
+ css::uno::Reference< css::container::XIndexAccess > xContainer(
+ xDesktop->getFrames(),
+ css::uno::UNO_QUERY_THROW);
+
+ sal_Int32 i = 0;
+ sal_Int32 c = xContainer->getCount();
+
+ for (i=0; i<c; ++i)
+ {
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ try
+ {
+ xContainer->getByIndex(i) >>= xFrame;
+ if (!xFrame.is())
+ continue;
+ }
+ // can happen in multithreaded environments, that frames was removed from the container during this loop runs!
+ // Ignore it.
+ catch(const css::lang::IndexOutOfBoundsException&)
+ {
+ continue;
+ }
+
+ // We are interested on visible documents only.
+ // Note: It's n optional interface .-(
+ css::uno::Reference< css::awt::XWindow2 > xVisibleCheck(
+ xFrame->getContainerWindow(),
+ css::uno::UNO_QUERY);
+ if (
+ (!xVisibleCheck.is() ) ||
+ (!xVisibleCheck->isVisible())
+ )
+ {
+ continue;
+ }
+
+ // extract the model from the frame.
+ // Ignore "view only" frames, which does not have a model.
+ css::uno::Reference< css::frame::XController > xController;
+ css::uno::Reference< css::frame::XModel3 > xModel;
+
+ xController = xFrame->getController();
+ if (xController.is())
+ xModel.set( xController->getModel(), UNO_QUERY_THROW );
+ if (!xModel.is())
+ continue;
+
+ // insert model into cache ...
+ // If the model is already well known inside cache
+ // it's information set will be updated by asking the
+ // model again for its new states.
+ implts_registerDocument(xModel);
+ }
+ }
+ catch(const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+
+ SAL_INFO("fwk.autorecovery", "... AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList()");
+}
+
+bool AutoRecovery::impl_enoughDiscSpace(sal_Int32 nRequiredSpace)
+{
+#ifdef SIMULATE_FULL_DISC
+ return sal_False;
+#else // SIMULATE_FULL_DISC
+ // In case an error occurs and we are not able to retrieve the needed information
+ // it's better to "disable" the feature ShowErrorOnFullDisc !
+ // Otherwise we start a confusing process of error handling ...
+
+ sal_uInt64 nFreeSpace = SAL_MAX_UINT64;
+
+ OUString sBackupPath(SvtPathOptions().GetBackupPath());
+ ::osl::VolumeInfo aInfo (osl_VolumeInfo_Mask_FreeSpace);
+ ::osl::FileBase::RC aRC = ::osl::Directory::getVolumeInfo(sBackupPath, aInfo);
+
+ if (
+ (aInfo.isValid(osl_VolumeInfo_Mask_FreeSpace)) &&
+ (aRC == ::osl::FileBase::E_None )
+ )
+ {
+ nFreeSpace = aInfo.getFreeSpace();
+ }
+
+ sal_uInt64 nFreeMB = nFreeSpace/1048576;
+ return (nFreeMB >= o3tl::make_unsigned(nRequiredSpace));
+#endif // SIMULATE_FULL_DISC
+}
+
+void AutoRecovery::impl_showFullDiscError()
+{
+ OUString sBtn(FwkResId(STR_FULL_DISC_RETRY_BUTTON));
+ OUString sMsg(FwkResId(STR_FULL_DISC_MSG));
+
+ OUString sBackupURL(SvtPathOptions().GetBackupPath());
+ INetURLObject aConverter(sBackupURL);
+ sal_Unicode aDelimiter;
+ OUString sBackupPath = aConverter.getFSysPath(FSysStyle::Detect, &aDelimiter);
+ if (sBackupPath.getLength() < 1)
+ sBackupPath = sBackupURL;
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Error, VclButtonsType::NONE,
+ sMsg.replaceAll("%PATH", sBackupPath)));
+ xBox->add_button(sBtn, RET_OK);
+ xBox->run();
+}
+
+void AutoRecovery::impl_establishProgress(const AutoRecovery::TDocumentInfo& rInfo ,
+ utl::MediaDescriptor& rArgs ,
+ const css::uno::Reference< css::frame::XFrame >& xNewFrame)
+{
+ // external well known frame must be preferred (because it was created by ourself
+ // for loading documents into this frame)!
+ // But if no frame exists... we can try to locate it using any frame bound to the provided
+ // document. Of course we must live without any frame in case the document does not exists at this
+ // point. But this state should not occur. In such case xNewFrame should be valid ... hopefully .-)
+ css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame;
+ if (
+ (!xFrame.is() ) &&
+ (rInfo.Document.is())
+ )
+ {
+ css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController();
+ if (xController.is())
+ xFrame = xController->getFrame();
+ }
+
+ // Any outside progress must be used ...
+ // Only if there is no progress, we can create our own one.
+ css::uno::Reference< css::task::XStatusIndicator > xInternalProgress;
+ css::uno::Reference< css::task::XStatusIndicator > xExternalProgress = rArgs.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_STATUSINDICATOR,
+ css::uno::Reference< css::task::XStatusIndicator >() );
+
+ // Normally a progress is set from outside (e.g. by the CrashSave/Recovery dialog, which uses our dispatch API).
+ // But for a normal auto save we don't have such "external progress"... because this function is triggered by our own timer then.
+ // In such case we must create our own progress !
+ if (
+ (! xExternalProgress.is()) &&
+ (xFrame.is() )
+ )
+ {
+ css::uno::Reference< css::task::XStatusIndicatorFactory > xProgressFactory(xFrame, css::uno::UNO_QUERY);
+ if (xProgressFactory.is())
+ xInternalProgress = xProgressFactory->createStatusIndicator();
+ }
+
+ // HACK
+ // An external provided progress (most given by the CrashSave/Recovery dialog)
+ // must be preferred. But we know that some application filters query its own progress instance
+ // at the frame method Frame::createStatusIndicator().
+ // So we use a two step mechanism:
+ // 1) we set the progress inside the MediaDescriptor, which will be provided to the filter
+ // 2) and we set a special Frame property, which overwrites the normal behaviour of Frame::createStatusIndicator .-)
+ // But we suppress 2) in case we uses an internal progress. Because then it doesn't matter
+ // if our applications make it wrong. In such case the internal progress resists at the same frame
+ // and there is no need to forward progress activities to e.g. an outside dialog .-)
+ if (
+ (xExternalProgress.is()) &&
+ (xFrame.is() )
+ )
+ {
+ css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY);
+ if (xFrameProps.is())
+ xFrameProps->setPropertyValue(FRAME_PROPNAME_ASCII_INDICATORINTERCEPTION, css::uno::Any(xExternalProgress));
+ }
+
+ // But inside the MediaDescriptor we must set our own create progress ...
+ // in case there is not already another progress set.
+ rArgs.createItemIfMissing(utl::MediaDescriptor::PROP_STATUSINDICATOR, xInternalProgress);
+}
+
+void AutoRecovery::impl_forgetProgress(const AutoRecovery::TDocumentInfo& rInfo ,
+ utl::MediaDescriptor& rArgs ,
+ const css::uno::Reference< css::frame::XFrame >& xNewFrame)
+{
+ // external well known frame must be preferred (because it was created by ourself
+ // for loading documents into this frame)!
+ // But if no frame exists... we can try to locate it using any frame bound to the provided
+ // document. Of course we must live without any frame in case the document does not exists at this
+ // point. But this state should not occur. In such case xNewFrame should be valid ... hopefully .-)
+ css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame;
+ if (
+ (!xFrame.is() ) &&
+ (rInfo.Document.is())
+ )
+ {
+ css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController();
+ if (xController.is())
+ xFrame = xController->getFrame();
+ }
+
+ // stop progress interception on corresponding frame.
+ css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY);
+ if (xFrameProps.is())
+ xFrameProps->setPropertyValue(FRAME_PROPNAME_ASCII_INDICATORINTERCEPTION, css::uno::Any(css::uno::Reference< css::task::XStatusIndicator >()));
+
+ // forget progress inside list of arguments.
+ utl::MediaDescriptor::iterator pArg = rArgs.find(utl::MediaDescriptor::PROP_STATUSINDICATOR);
+ if (pArg != rArgs.end())
+ {
+ rArgs.erase(pArg);
+ pArg = rArgs.end();
+ }
+}
+
+void AutoRecovery::impl_flushALLConfigChanges()
+{
+ try
+ {
+ // SOLAR SAFE ->
+ SolarMutexGuard aGuard;
+ ::utl::ConfigManager::storeConfigItems();
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+}
+
+void AutoRecovery::st_impl_removeFile(const OUString& sURL)
+{
+ if ( sURL.isEmpty())
+ return;
+
+ try
+ {
+ ::ucbhelper::Content aContent(sURL, css::uno::Reference< css::ucb::XCommandEnvironment >(), m_xContext);
+ aContent.executeCommand("delete", css::uno::Any(true));
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+}
+
+void AutoRecovery::st_impl_removeLockFile()
+{
+ try
+ {
+ OUString sUserURL;
+ ::utl::Bootstrap::locateUserInstallation( sUserURL );
+
+ OUString sLockURL = sUserURL + "/.lock";
+ AutoRecovery::st_impl_removeFile(sLockURL);
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_AutoRecovery_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ rtl::Reference<AutoRecovery> xAutoRecovery = new AutoRecovery(context);
+ // 2nd phase initialization needed
+ xAutoRecovery->initListeners();
+
+ return cppu::acquire(xAutoRecovery.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/services/desktop.cxx b/framework/source/services/desktop.cxx
new file mode 100644
index 000000000..cbc3ce6eb
--- /dev/null
+++ b/framework/source/services/desktop.cxx
@@ -0,0 +1,1775 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/desktop.hxx>
+
+#include <loadenv/loadenv.hxx>
+
+#include <helper/ocomponentaccess.hxx>
+#include <helper/oframes.hxx>
+#include <dispatch/dispatchprovider.hxx>
+
+#include <dispatch/interceptionhelper.hxx>
+#include <classes/taskcreator.hxx>
+#include <threadhelp/transactionguard.hxx>
+#include <properties.h>
+#include <targets.h>
+
+#include <strings.hrc>
+#include <classes/fwkresid.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/document/XInteractionFilterSelect.hpp>
+#include <com/sun/star/task/ErrorCodeRequest.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/frame/XTerminateListener2.hpp>
+
+#include <comphelper/numberedcollection.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/lok.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <desktop/crashreport.hxx>
+#include <vcl/scheduler.hxx>
+#include <sal/log.hxx>
+#include <vcl/errcode.hxx>
+#include <vcl/threadex.hxx>
+#include <unotools/configmgr.hxx>
+
+namespace framework{
+
+namespace {
+
+enum PropHandle {
+ ActiveFrame, DispatchRecorderSupplier, IsPlugged, SuspendQuickstartVeto,
+ Title };
+
+}
+
+OUString SAL_CALL Desktop::getImplementationName()
+{
+ return "com.sun.star.comp.framework.Desktop";
+}
+
+sal_Bool SAL_CALL Desktop::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> SAL_CALL Desktop::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.Desktop" };
+}
+
+void Desktop::constructorInit()
+{
+ // Initialize a new XFrames-helper-object to handle XIndexAccess and XElementAccess.
+ // We hold member as reference ... not as pointer too!
+ // Attention: We share our frame container with this helper. Container is threadsafe himself ... So I think we can do that.
+ // But look on dispose() for right order of deinitialization.
+ m_xFramesHelper = new OFrames( this, &m_aChildTaskContainer );
+
+ // Initialize a new dispatchhelper-object to handle dispatches.
+ // We use these helper as slave for our interceptor helper ... not directly!
+ // But he is event listener on THIS instance!
+ rtl::Reference<DispatchProvider> xDispatchProvider = new DispatchProvider( m_xContext, this );
+
+ // Initialize a new interception helper object to handle dispatches and implement an interceptor mechanism.
+ // Set created dispatch provider as slowest slave of it.
+ // Hold interception helper by reference only - not by pointer!
+ // So it's easier to destroy it.
+ m_xDispatchHelper = new InterceptionHelper( this, xDispatchProvider );
+
+ OUString sUntitledPrefix = FwkResId(STR_UNTITLED_DOCUMENT) + " ";
+
+ rtl::Reference<::comphelper::NumberedCollection> pNumbers = new ::comphelper::NumberedCollection ();
+ m_xTitleNumberGenerator = pNumbers;
+ pNumbers->setOwner ( static_cast< ::cppu::OWeakObject* >(this) );
+ pNumbers->setUntitledPrefix ( sUntitledPrefix );
+
+ // Safe impossible cases
+ // We can't work without this helper!
+ SAL_WARN_IF( !m_xFramesHelper.is(), "fwk.desktop", "Desktop::Desktop(): Frames helper is not valid. XFrames, XIndexAccess and XElementAccess are not supported!");
+ SAL_WARN_IF( !m_xDispatchHelper.is(), "fwk.desktop", "Desktop::Desktop(): Dispatch helper is not valid. XDispatch will not work correctly!" );
+
+ // Enable object for real working!
+ // Otherwise all calls will be rejected ...
+ m_aTransactionManager.setWorkingMode( E_WORK );
+}
+
+/*-************************************************************************************************************
+ @short standard constructor to create instance by factory
+ @descr This constructor initialize a new instance of this class by valid factory,
+ and will be set valid values on his member and baseclasses.
+
+ @attention a) Don't use your own reference during a UNO-Service-ctor! There is no guarantee, that you
+ will get over this. (e.g. using of your reference as parameter to initialize some member)
+ Do such things in DEFINE_INIT_SERVICE() method, which is called automatically after your ctor!!!
+ b) Baseclass OBroadcastHelper is a typedef in namespace cppu!
+ The microsoft compiler has some problems to handle it right BY using namespace explicitly ::cppu::OBroadcastHelper.
+ If we write it without a namespace or expand the typedef to OBroadcastHelperVar<...> -> it will be OK!?
+ I don't know why! (other compiler not tested .. but it works!)
+
+ @seealso method DEFINE_INIT_SERVICE()
+
+ @param "xFactory" is the multi service manager, which create this instance.
+ The value must be different from NULL!
+ @onerror We throw an ASSERT in debug version or do nothing in release version.
+*//*-*************************************************************************************************************/
+Desktop::Desktop( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : Desktop_BASE ( m_aMutex )
+ , cppu::OPropertySetHelper( cppu::WeakComponentImplHelperBase::rBHelper )
+ // Init member
+ , m_bIsTerminated(false)
+ , m_bIsShutdown(false) // see dispose() for further information!
+ , m_bSession ( false )
+ , m_xContext (std::move( xContext ))
+ , m_aListenerContainer ( m_aMutex )
+ , m_eLoadState ( E_NOTSET )
+ , m_bSuspendQuickstartVeto( false )
+{
+}
+
+/*-************************************************************************************************************
+ @short standard destructor
+ @descr This one do NOTHING! Use dispose() instead of this.
+
+ @seealso method dispose()
+*//*-*************************************************************************************************************/
+Desktop::~Desktop()
+{
+ SAL_WARN_IF(!m_bIsShutdown, "fwk.desktop", "Desktop not terminated before being destructed");
+ SAL_WARN_IF( m_aTransactionManager.getWorkingMode()!=E_CLOSE, "fwk.desktop", "Desktop::~Desktop(): Who forgot to dispose this service?" );
+}
+
+css::uno::Any SAL_CALL Desktop::queryInterface( const css::uno::Type& _rType )
+{
+ css::uno::Any aRet = Desktop_BASE::queryInterface( _rType );
+ if ( !aRet.hasValue() )
+ aRet = OPropertySetHelper::queryInterface( _rType );
+ return aRet;
+}
+
+css::uno::Sequence< css::uno::Type > SAL_CALL Desktop::getTypes( )
+{
+ return comphelper::concatSequences(
+ Desktop_BASE::getTypes(),
+ ::cppu::OPropertySetHelper::getTypes()
+ );
+}
+
+sal_Bool SAL_CALL Desktop::terminate()
+{
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+ SolarMutexResettableGuard aGuard;
+
+ if (m_bIsTerminated)
+ return true;
+
+ css::uno::Reference< css::frame::XTerminateListener > xPipeTerminator = m_xPipeTerminator;
+ css::uno::Reference< css::frame::XTerminateListener > xQuickLauncher = m_xQuickLauncher;
+ css::uno::Reference< css::frame::XTerminateListener > xSWThreadManager = m_xSWThreadManager;
+ css::uno::Reference< css::frame::XTerminateListener > xSfxTerminator = m_xSfxTerminator;
+
+ css::lang::EventObject aEvent ( static_cast< ::cppu::OWeakObject* >(this) );
+ bool bAskQuickStart = !m_bSuspendQuickstartVeto;
+ const bool bRestartableMainLoop = Application::IsEventTestingModeEnabled() ||
+ comphelper::LibreOfficeKit::isActive();
+ aGuard.clear();
+
+ // Allow using of any UI ... because Desktop.terminate() was designed as UI functionality in the past.
+
+ // Ask normal terminate listener. They could veto terminating the process.
+ Desktop::TTerminateListenerList lCalledTerminationListener;
+ if (!impl_sendQueryTerminationEvent(lCalledTerminationListener))
+ {
+ impl_sendCancelTerminationEvent(lCalledTerminationListener);
+ return false;
+ }
+
+ // try to close all open frames
+ if (!impl_closeFrames(!bRestartableMainLoop))
+ {
+ impl_sendCancelTerminationEvent(lCalledTerminationListener);
+ return false;
+ }
+
+ // Normal listener had no problem ...
+ // all frames was closed ...
+ // now it's time to ask our specialized listener.
+ // They are handled these way because they wish to hinder the office on termination
+ // but they wish also closing of all frames.
+
+ // Note further:
+ // We shouldn't ask quicklauncher in case it was allowed from outside only.
+ // This is special trick to "ignore existing quick starter" for debug purposes.
+
+ // Attention:
+ // Order of called listener is important!
+ // Some of them are harmless,-)
+ // but some can be dangerous. E.g. it would be dangerous if we close our pipe
+ // and don't terminate in real because another listener throws a veto exception .-)
+
+ try
+ {
+ if( bAskQuickStart && xQuickLauncher.is() )
+ {
+ xQuickLauncher->queryTermination( aEvent );
+ lCalledTerminationListener.push_back( xQuickLauncher );
+ }
+
+ if ( xSWThreadManager.is() )
+ {
+ xSWThreadManager->queryTermination( aEvent );
+ lCalledTerminationListener.push_back( xSWThreadManager );
+ }
+
+ if ( xPipeTerminator.is() )
+ {
+ xPipeTerminator->queryTermination( aEvent );
+ lCalledTerminationListener.push_back( xPipeTerminator );
+ }
+
+ if ( xSfxTerminator.is() )
+ {
+ xSfxTerminator->queryTermination( aEvent );
+ lCalledTerminationListener.push_back( xSfxTerminator );
+ }
+ }
+ catch(const css::frame::TerminationVetoException&)
+ {
+ impl_sendCancelTerminationEvent(lCalledTerminationListener);
+ return false;
+ }
+
+ aGuard.reset();
+ if (m_bIsTerminated)
+ return true;
+ m_bIsTerminated = true;
+
+ if (!bRestartableMainLoop)
+ {
+ CrashReporter::addKeyValue("ShutDown", OUString::boolean(true), CrashReporter::Write);
+
+ // The clipboard listener needs to be the first. It can create copies of the
+ // existing document which needs basically all the available infrastructure.
+ impl_sendTerminateToClipboard();
+ {
+ SolarMutexReleaser aReleaser;
+ impl_sendNotifyTerminationEvent();
+ }
+ Scheduler::ProcessEventsToIdle();
+
+ if( bAskQuickStart && xQuickLauncher.is() )
+ xQuickLauncher->notifyTermination( aEvent );
+
+ if ( xSWThreadManager.is() )
+ xSWThreadManager->notifyTermination( aEvent );
+
+ if ( xPipeTerminator.is() )
+ xPipeTerminator->notifyTermination( aEvent );
+
+ // further termination is postponed to shutdown, if LO already runs the main loop
+ if (!Application::IsInExecute())
+ shutdown();
+ }
+ else
+ m_bIsShutdown = true;
+
+#ifndef IOS // or ANDROID?
+ aGuard.clear();
+ // In the iOS app, posting the ImplQuitMsg user event will be too late, it will not be handled during the
+ // lifetime of the current document, but handled for the next document opened, which thus will break horribly.
+ Application::Quit();
+#endif
+
+ return true;
+}
+
+void Desktop::shutdown()
+{
+ TransactionGuard aTransaction(m_aTransactionManager, E_HARDEXCEPTIONS);
+ SolarMutexGuard aGuard;
+
+ if (m_bIsShutdown)
+ return;
+ m_bIsShutdown = true;
+
+ css::uno::Reference<css::frame::XTerminateListener> xSfxTerminator = m_xSfxTerminator;
+ css::lang::EventObject aEvent(static_cast<::cppu::OWeakObject* >(this));
+
+ // we need a copy here as the notifyTermination call might cause a removeTerminateListener call
+ std::vector< css::uno::Reference<css::frame::XTerminateListener> > xComponentDllListeners;
+ xComponentDllListeners.swap(m_xComponentDllListeners);
+ for (auto& xListener : xComponentDllListeners)
+ xListener->notifyTermination(aEvent);
+ xComponentDllListeners.clear();
+
+ // Must be really the last listener to be called.
+ // Because it shuts down the whole process asynchronous!
+ if (xSfxTerminator.is())
+ xSfxTerminator->notifyTermination(aEvent);
+}
+
+namespace
+{
+ class QuickstartSuppressor
+ {
+ Desktop* const m_pDesktop;
+ css::uno::Reference< css::frame::XTerminateListener > m_xQuickLauncher;
+ public:
+ QuickstartSuppressor(Desktop* const pDesktop, css::uno::Reference< css::frame::XTerminateListener > xQuickLauncher)
+ : m_pDesktop(pDesktop)
+ , m_xQuickLauncher(std::move(xQuickLauncher))
+ {
+ SAL_INFO("fwk.desktop", "temporary removing Quickstarter");
+ if(m_xQuickLauncher.is())
+ m_pDesktop->removeTerminateListener(m_xQuickLauncher);
+ }
+ ~QuickstartSuppressor()
+ {
+ SAL_INFO("fwk.desktop", "readding Quickstarter");
+ if(m_xQuickLauncher.is())
+ m_pDesktop->addTerminateListener(m_xQuickLauncher);
+ }
+ };
+}
+
+bool Desktop::terminateQuickstarterToo()
+{
+ QuickstartSuppressor aQuickstartSuppressor(this, m_xQuickLauncher);
+ m_bSession = true;
+ return terminate();
+}
+
+void SAL_CALL Desktop::addTerminateListener( const css::uno::Reference< css::frame::XTerminateListener >& xListener )
+{
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ css::uno::Reference< css::lang::XServiceInfo > xInfo( xListener, css::uno::UNO_QUERY );
+ if ( xInfo.is() )
+ {
+ OUString sImplementationName = xInfo->getImplementationName();
+
+ SolarMutexGuard g;
+
+ if( sImplementationName == "com.sun.star.comp.sfx2.SfxTerminateListener" )
+ {
+ m_xSfxTerminator = xListener;
+ return;
+ }
+ if( sImplementationName == "com.sun.star.comp.RequestHandlerController" )
+ {
+ m_xPipeTerminator = xListener;
+ return;
+ }
+ if( sImplementationName == "com.sun.star.comp.desktop.QuickstartWrapper" )
+ {
+ m_xQuickLauncher = xListener;
+ return;
+ }
+ if( sImplementationName == "com.sun.star.util.comp.FinalThreadManager" )
+ {
+ m_xSWThreadManager = xListener;
+ return;
+ }
+ else if ( sImplementationName == "com.sun.star.comp.ComponentDLLListener" )
+ {
+ m_xComponentDllListeners.push_back(xListener);
+ return;
+ }
+ }
+
+ // No lock required... container is threadsafe by itself.
+ m_aListenerContainer.addInterface( cppu::UnoType<css::frame::XTerminateListener>::get(), xListener );
+}
+
+void SAL_CALL Desktop::removeTerminateListener( const css::uno::Reference< css::frame::XTerminateListener >& xListener )
+{
+ TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS );
+
+ css::uno::Reference< css::lang::XServiceInfo > xInfo( xListener, css::uno::UNO_QUERY );
+ if ( xInfo.is() )
+ {
+ OUString sImplementationName = xInfo->getImplementationName();
+
+ SolarMutexGuard g;
+
+ if( sImplementationName == "com.sun.star.comp.sfx2.SfxTerminateListener" )
+ {
+ m_xSfxTerminator.clear();
+ return;
+ }
+
+ if( sImplementationName == "com.sun.star.comp.RequestHandlerController" )
+ {
+ m_xPipeTerminator.clear();
+ return;
+ }
+
+ if( sImplementationName == "com.sun.star.comp.desktop.QuickstartWrapper" )
+ {
+ m_xQuickLauncher.clear();
+ return;
+ }
+
+ if( sImplementationName == "com.sun.star.util.comp.FinalThreadManager" )
+ {
+ m_xSWThreadManager.clear();
+ return;
+ }
+ else if (sImplementationName == "com.sun.star.comp.ComponentDLLListener")
+ {
+ m_xComponentDllListeners.erase(
+ std::remove(m_xComponentDllListeners.begin(), m_xComponentDllListeners.end(), xListener),
+ m_xComponentDllListeners.end());
+ return;
+ }
+ }
+
+ // No lock required ... container is threadsafe by itself.
+ m_aListenerContainer.removeInterface( cppu::UnoType<css::frame::XTerminateListener>::get(), xListener );
+}
+
+/*-************************************************************************************************************
+ @interface XDesktop
+ @short get access to create enumerations of all current components
+ @descr You will be the owner of the returned object and must delete it if you don't use it again.
+
+ @seealso class TasksAccess
+ @seealso class TasksEnumeration
+ @return A reference to an XEnumerationAccess-object.
+
+ @onerror We return a null-reference.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+css::uno::Reference< css::container::XEnumerationAccess > SAL_CALL Desktop::getComponents()
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ // We use a helper class OComponentAccess to have access on all child components.
+ // Create it on demand and return it as a reference.
+ return new OComponentAccess( this );
+}
+
+/*-************************************************************************************************************
+ @interface XDesktop
+ @short return the current active component
+ @descr The most current component is the window, model or the controller of the current active frame.
+
+ @seealso method getCurrentFrame()
+ @seealso method impl_getFrameComponent()
+ @return A reference to the component.
+
+ @onerror We return a null-reference.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+css::uno::Reference< css::lang::XComponent > SAL_CALL Desktop::getCurrentComponent()
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ // Set return value if method failed.
+ css::uno::Reference< css::lang::XComponent > xComponent;
+
+ // Get reference to current frame ...
+ // ... get component of this frame ... (It can be the window, the model or the controller.)
+ // ... and return the result.
+ css::uno::Reference< css::frame::XFrame > xCurrentFrame = getCurrentFrame();
+ if( xCurrentFrame.is() )
+ {
+ xComponent = impl_getFrameComponent( xCurrentFrame );
+ }
+ return xComponent;
+}
+
+/*-************************************************************************************************************
+ @interface XDesktop
+ @short return the current active frame in hierarchy
+ @descr There can be more than one different active paths in our frame hierarchy. But only one of them
+ could be the most active frame (normal he has the focus).
+ Don't mix it with getActiveFrame()! That will return our current active frame, which must be
+ a direct child of us and should be a part(!) of an active path.
+
+ @seealso method getActiveFrame()
+ @return A valid reference, if there is an active frame.
+ A null reference , otherwise.
+
+ @onerror We return a null reference.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+css::uno::Reference< css::frame::XFrame > SAL_CALL Desktop::getCurrentFrame()
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ // Start search with our direct active frame (if it exist!).
+ // Search on his children for other active frames too.
+ // Stop if no one could be found and return last of found ones.
+ css::uno::Reference< css::frame::XFramesSupplier > xLast( getActiveFrame(), css::uno::UNO_QUERY );
+ if( xLast.is() )
+ {
+ css::uno::Reference< css::frame::XFramesSupplier > xNext( xLast->getActiveFrame(), css::uno::UNO_QUERY );
+ while( xNext.is() )
+ {
+ xLast = xNext;
+ xNext.set( xNext->getActiveFrame(), css::uno::UNO_QUERY );
+ }
+ }
+ return xLast;
+}
+
+/*-************************************************************************************************************
+ @interface XComponentLoader
+ @short try to load given URL into a task
+ @descr You can give us some information about the content, which you will load into a frame.
+ We search or create this target for you, make a type detection of given URL and try to load it.
+ As result of this operation we return the new created component or nothing, if loading failed.
+ @param "sURL" , URL, which represent the content
+ @param "sTargetFrameName" , name of target frame or special value like "_self", "_blank" ...
+ @param "nSearchFlags" , optional arguments for frame search, if target isn't a special one
+ @param "lArguments" , optional arguments for loading
+ @return A valid component reference, if loading was successful.
+ A null reference otherwise.
+
+ @onerror We return a null reference.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+css::uno::Reference< css::lang::XComponent > SAL_CALL Desktop::loadComponentFromURL( const OUString& sURL ,
+ const OUString& sTargetFrameName,
+ sal_Int32 nSearchFlags ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+ SAL_INFO( "fwk.desktop", "loadComponentFromURL" );
+
+ css::uno::Reference< css::frame::XComponentLoader > xThis(this);
+
+ utl::MediaDescriptor aDescriptor(lArguments);
+ bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault("OnMainThread", false);
+
+ if (bOnMainThread)
+ {
+ // Make sure that we own the solar mutex, otherwise later
+ // vcl::SolarThreadExecutor::execute() will release the solar mutex, even if it's owned by
+ // another thread, leading to an std::abort() at the end.
+ SolarMutexGuard g;
+
+ return vcl::solarthread::syncExecute(std::bind(&LoadEnv::loadComponentFromURL, xThis,
+ m_xContext, sURL, sTargetFrameName,
+ nSearchFlags, lArguments));
+ }
+ else
+ {
+ return LoadEnv::loadComponentFromURL(xThis, m_xContext, sURL, sTargetFrameName,
+ nSearchFlags, lArguments);
+ }
+}
+
+/*-************************************************************************************************************
+ @interface XTasksSupplier
+ @short get access to create enumerations of our taskchildren
+ @descr Direct children of desktop are tasks every time.
+ Call these method to could create enumerations of it.
+
+But; Don't forget - you will be the owner of returned object and must release it!
+ We use a helper class to implement the access interface. They hold a weakreference to us.
+ It can be, that the desktop is dead - but not your tasksaccess-object! Then they will do nothing!
+ You can't create enumerations then.
+
+ @attention Normally we don't need any lock here. We don't work on internal member!
+
+ @seealso class TasksAccess
+ @return A reference to an accessobject, which can create enumerations of our childtasks.
+
+ @onerror A null reference is returned.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+css::uno::Reference< css::container::XEnumerationAccess > SAL_CALL Desktop::getTasks()
+{
+ SAL_INFO("fwk.desktop", "Desktop::getTasks(): Use of obsolete interface XTaskSupplier");
+ return nullptr;
+}
+
+/*-************************************************************************************************************
+ @interface XTasksSupplier
+ @short return current active task of our direct children
+ @descr Desktop children are tasks only ! If we have an active path from desktop
+ as top to any frame on bottom, we must have an active direct child. His reference is returned here.
+
+ @attention a) Do not confuse it with getCurrentFrame()! The current frame don't must one of our direct children.
+ It can be every frame in subtree and must have the focus (Is the last one of an active path!).
+ b) We don't need any lock here. Our container is threadsafe himself and live, if we live!
+
+ @seealso method getCurrentFrame()
+ @return A reference to our current active taskchild.
+
+ @onerror A null reference is returned.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+css::uno::Reference< css::frame::XTask > SAL_CALL Desktop::getActiveTask()
+{
+ SAL_INFO("fwk.desktop", "Desktop::getActiveTask(): Use of obsolete interface XTaskSupplier");
+ return nullptr;
+}
+
+/*-************************************************************************************************************
+ @interface XDispatchProvider
+ @short search a dispatcher for given URL
+ @descr We use a helper implementation (class DispatchProvider) to do so.
+ So we don't must implement this algorithm twice!
+
+ @attention We don't need any lock here. Our helper is threadsafe himself and live, if we live!
+
+ @seealso class DispatchProvider
+
+ @param "aURL" , URL to dispatch
+ @param "sTargetFrameName" , name of target frame, who should dispatch these URL
+ @param "nSearchFlags" , flags to regulate the search
+ @param "lQueries" , list of queryDispatch() calls!
+ @return A reference or list of founded dispatch objects for these URL.
+
+ @onerror A null reference is returned.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+css::uno::Reference< css::frame::XDispatch > SAL_CALL Desktop::queryDispatch( const css::util::URL& aURL ,
+ const OUString& sTargetFrameName ,
+ sal_Int32 nSearchFlags )
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ // Remove uno and cmd protocol part as we want to support both of them. We store only the command part
+ // in our hash map. All other protocols are stored with the protocol part.
+ OUString aCommand( aURL.Main );
+ if ( aURL.Protocol.equalsIgnoreAsciiCase(".uno:") )
+ aCommand = aURL.Path;
+
+ if (!m_xCommandOptions && !utl::ConfigManager::IsFuzzing())
+ m_xCommandOptions.reset(new SvtCommandOptions);
+
+ // Make std::unordered_map lookup if the current URL is in the disabled list
+ if (m_xCommandOptions && m_xCommandOptions->Lookup(SvtCommandOptions::CMDOPTION_DISABLED, aCommand))
+ return css::uno::Reference< css::frame::XDispatch >();
+ else
+ {
+ // We use a helper to support these interface and an interceptor mechanism.
+ // Our helper is threadsafe by himself!
+ return m_xDispatchHelper->queryDispatch( aURL, sTargetFrameName, nSearchFlags );
+ }
+}
+
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL Desktop::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lQueries )
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ return m_xDispatchHelper->queryDispatches( lQueries );
+}
+
+/*-************************************************************************************************************
+ @interface XDispatchProviderInterception
+ @short supports registration/deregistration of interception objects, which
+ are interested on special dispatches.
+
+ @descr It's really provided by an internal helper, which is used inside the dispatch API too.
+ @param xInterceptor
+ the interceptor object, which wishes to be (de)registered.
+
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+void SAL_CALL Desktop::registerDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor)
+{
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper( m_xDispatchHelper, css::uno::UNO_QUERY );
+ xInterceptionHelper->registerDispatchProviderInterceptor( xInterceptor );
+}
+
+void SAL_CALL Desktop::releaseDispatchProviderInterceptor ( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor)
+{
+ TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS );
+
+ css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper( m_xDispatchHelper, css::uno::UNO_QUERY );
+ xInterceptionHelper->releaseDispatchProviderInterceptor( xInterceptor );
+}
+
+/*-************************************************************************************************************
+ @interface XFramesSupplier
+ @short return access to append or remove children on desktop
+ @descr We don't implement these interface directly. We use a helper class to do this.
+ If you wish to add or delete children to/from the container, call these method to get
+ a reference to the helper.
+
+ @attention Helper is threadsafe himself. So we don't need any lock here.
+
+ @seealso class OFrames
+ @return A reference to the helper.
+
+ @onerror A null reference is returned.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+css::uno::Reference< css::frame::XFrames > SAL_CALL Desktop::getFrames()
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ return m_xFramesHelper;
+}
+
+/*-************************************************************************************************************
+ @interface XFramesSupplier
+ @short set/get the current active child frame
+ @descr It must be a task. Direct children of desktop are tasks only! No frames are accepted.
+ We don't save this information directly in this class. We use our container-helper
+ to do that.
+
+ @attention Helper is threadsafe himself. So we don't need any lock here.
+
+ @seealso class OFrameContainer
+
+ @param "xFrame", new active frame (must be valid!)
+ @return A reference to our current active childtask, if anyone exist.
+
+ @onerror A null reference is returned.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+void SAL_CALL Desktop::setActiveFrame( const css::uno::Reference< css::frame::XFrame >& xFrame )
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ // Get old active frame first.
+ // If nothing will change - do nothing!
+ // Otherwise set new active frame ...
+ // and deactivate last frame.
+ // It's necessary for our FrameActionEvent listener on a frame!
+ css::uno::Reference< css::frame::XFrame > xLastActiveChild = m_aChildTaskContainer.getActive();
+ if( xLastActiveChild != xFrame )
+ {
+ m_aChildTaskContainer.setActive( xFrame );
+ if( xLastActiveChild.is() )
+ {
+ xLastActiveChild->deactivate();
+ }
+ }
+}
+
+css::uno::Reference< css::frame::XFrame > SAL_CALL Desktop::getActiveFrame()
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ return m_aChildTaskContainer.getActive();
+}
+
+/*
+ @interface XFrame
+ @short non implemented methods!
+ @descr Some method make no sense for our desktop! He has no window or parent or ...
+ So we should implement it empty and warn programmer, if he use it!
+*/
+void SAL_CALL Desktop::initialize( const css::uno::Reference< css::awt::XWindow >& )
+{
+}
+
+css::uno::Reference< css::awt::XWindow > SAL_CALL Desktop::getContainerWindow()
+{
+ return css::uno::Reference< css::awt::XWindow >();
+}
+
+void SAL_CALL Desktop::setCreator( const css::uno::Reference< css::frame::XFramesSupplier >& /*xCreator*/ )
+{
+}
+
+css::uno::Reference< css::frame::XFramesSupplier > SAL_CALL Desktop::getCreator()
+{
+ return css::uno::Reference< css::frame::XFramesSupplier >();
+}
+
+OUString SAL_CALL Desktop::getName()
+{
+ SolarMutexGuard g;
+ return m_sName;
+}
+
+void SAL_CALL Desktop::setName( const OUString& sName )
+{
+ SolarMutexGuard g;
+ m_sName = sName;
+}
+
+sal_Bool SAL_CALL Desktop::isTop()
+{
+ return true;
+}
+
+void SAL_CALL Desktop::activate()
+{
+ // Desktop is active always... but sometimes our frames try to activate
+ // the complete path from bottom to top... And our desktop is the topest frame :-(
+ // So - please don't show any assertions here. Do nothing!
+}
+
+void SAL_CALL Desktop::deactivate()
+{
+ // Desktop is active always... but sometimes our frames try to deactivate
+ // the complete path from bottom to top... And our desktop is the topest frame :-(
+ // So - please don't show any assertions here. Do nothing!
+}
+
+sal_Bool SAL_CALL Desktop::isActive()
+{
+ return true;
+}
+
+sal_Bool SAL_CALL Desktop::setComponent( const css::uno::Reference< css::awt::XWindow >& /*xComponentWindow*/ ,
+ const css::uno::Reference< css::frame::XController >& /*xController*/ )
+{
+ return false;
+}
+
+css::uno::Reference< css::awt::XWindow > SAL_CALL Desktop::getComponentWindow()
+{
+ return css::uno::Reference< css::awt::XWindow >();
+}
+
+css::uno::Reference< css::frame::XController > SAL_CALL Desktop::getController()
+{
+ return css::uno::Reference< css::frame::XController >();
+}
+
+void SAL_CALL Desktop::contextChanged()
+{
+}
+
+void SAL_CALL Desktop::addFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& )
+{
+}
+
+// css::frame::XFrame
+void SAL_CALL Desktop::removeFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& )
+{
+}
+
+/*-************************************************************************************************************
+ @interface XFrame
+ @short try to find a frame with special parameters
+ @descr This method searches for a frame with the specified name.
+ Frames may contain other frames (e.g. a frameset) and may
+ be contained in other frames. This hierarchy is searched by
+ this method.
+ First some special names are taken into account, i.e. "",
+ "_self", "_top", "_parent" etc. The FrameSearchFlags are ignored
+ when comparing these names with aTargetFrameName, further steps are
+ controlled by the FrameSearchFlags. If allowed, the name of the frame
+ itself is compared with the desired one, then ( again if allowed )
+ the method findFrame is called for all children of the frame.
+ If no Frame with the given name is found until the top frames container,
+ a new top Frame is created, if this is allowed by a special
+ FrameSearchFlag. The new Frame also gets the desired name.
+ We use a helper to get right search direction and react in a right manner.
+
+ @seealso class TargetFinder
+
+ @param "sTargetFrameName" , name of searched frame
+ @param "nSearchFlags" , flags to regulate search
+ @return A reference to an existing frame in hierarchy, if it exist.
+
+ @onerror A null reference is returned.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+css::uno::Reference< css::frame::XFrame > SAL_CALL Desktop::findFrame( const OUString& sTargetFrameName ,
+ sal_Int32 nSearchFlags )
+{
+ css::uno::Reference< css::frame::XFrame > xTarget;
+
+ // 0) Ignore wrong parameter!
+ // We don't support search for following special targets.
+ // If we reject these requests, we must not check for such names
+ // in following code again and again. If we do not, so wrong
+ // search results can occur!
+
+ if (
+ (sTargetFrameName==SPECIALTARGET_DEFAULT ) || // valid for dispatches - not for findFrame()!
+ (sTargetFrameName==SPECIALTARGET_PARENT ) || // we have no parent by definition
+ (sTargetFrameName==SPECIALTARGET_BEAMER ) // beamer frames are allowed as child of tasks only -
+ // and they exist more than ones. We have no idea which our sub tasks is the right one
+ )
+ {
+ return nullptr;
+ }
+
+ // I) check for special defined targets first which must be handled exclusive.
+ // force using of "if() else if() ..."
+
+ // I.I) "_blank"
+ // create a new task as child of this desktop instance
+ // Note: Used helper TaskCreator use us automatically ...
+
+ if ( sTargetFrameName==SPECIALTARGET_BLANK )
+ {
+ TaskCreator aCreator( m_xContext );
+ xTarget = aCreator.createTask(sTargetFrameName, utl::MediaDescriptor());
+ }
+
+ // I.II) "_top"
+ // We are top by definition
+
+ else if ( sTargetFrameName==SPECIALTARGET_TOP )
+ {
+ xTarget = this;
+ }
+
+ // I.III) "_self", ""
+ // This mean this "frame" in every case.
+
+ else if (
+ ( sTargetFrameName==SPECIALTARGET_SELF ) ||
+ ( sTargetFrameName.isEmpty() )
+ )
+ {
+ xTarget = this;
+ }
+
+ else
+ {
+
+ // II) otherwise use optional given search flags
+ // force using of combinations of such flags. means no "else" part of use if() statements.
+ // But we ust break further searches if target was already found.
+ // Order of using flags is fix: SELF - CHILDREN - SIBLINGS - PARENT
+ // TASK and CREATE are handled special.
+ // But note: Such flags are not valid for the desktop - especially SIBLINGS or PARENT.
+
+ // II.I) SELF
+ // Check for right name. If it's the searched one return ourself - otherwise
+ // ignore this flag.
+
+ if (
+ (nSearchFlags & css::frame::FrameSearchFlag::SELF) &&
+ (m_sName == sTargetFrameName)
+ )
+ {
+ xTarget = this;
+ }
+
+ // II.II) TASKS
+ // This is a special flag. Normally it regulate search inside tasks and forbid access to parent trees.
+ // But the desktop exists outside such task trees. They are our sub trees. So the desktop implement
+ // a special feature: We use it to start search on our direct children only. That means we suppress
+ // search on ALL child frames. May that can be useful to get access on opened document tasks
+ // only without filter out all non really required sub frames ...
+ // Used helper method on our container doesn't create any frame - it's a search only.
+
+ if (
+ ( ! xTarget.is() ) &&
+ (nSearchFlags & css::frame::FrameSearchFlag::TASKS)
+ )
+ {
+ xTarget = m_aChildTaskContainer.searchOnDirectChildrens(sTargetFrameName);
+ }
+
+ // II.III) CHILDREN
+ // Search on all children for the given target name.
+ // An empty name value can't occur here - because it must be already handled as "_self"
+ // before. Used helper function of container doesn't create any frame.
+ // It makes a deep search only.
+
+ if (
+ ( ! xTarget.is() ) &&
+ (nSearchFlags & css::frame::FrameSearchFlag::CHILDREN)
+ )
+ {
+ xTarget = m_aChildTaskContainer.searchOnAllChildrens(sTargetFrameName);
+ }
+
+ // II.IV) CREATE
+ // If we haven't found any valid target frame by using normal flags - but user allowed us to create
+ // a new one ... we should do that. Used TaskCreator use us automatically as parent!
+
+ if (
+ ( ! xTarget.is() ) &&
+ (nSearchFlags & css::frame::FrameSearchFlag::CREATE)
+ )
+ {
+ TaskCreator aCreator( m_xContext );
+ xTarget = aCreator.createTask(sTargetFrameName, utl::MediaDescriptor());
+ }
+ }
+
+ return xTarget;
+}
+
+void SAL_CALL Desktop::disposing()
+{
+ // Safe impossible cases
+ // It's a programming error if dispose is called before terminate!
+
+ // But if you just ignore the assertion (which happens in unit
+ // tests for instance in sc/qa/unit) nothing bad happens.
+ assert(m_bIsShutdown && "Desktop disposed before terminating it");
+
+ {
+ SolarMutexGuard aWriteLock;
+
+ {
+ TransactionGuard aTransaction(m_aTransactionManager, E_HARDEXCEPTIONS);
+ }
+
+ // Disable this instance for further work.
+ // This will wait for all current running transactions ...
+ // and reject all new incoming requests!
+ m_aTransactionManager.setWorkingMode(E_BEFORECLOSE);
+ }
+
+ // Following lines of code can be called outside a synchronized block ...
+ // Because our transaction manager will block all new requests to this object.
+ // So nobody can use us any longer.
+ // Exception: Only removing of listener will work ... and this code can't be dangerous.
+
+ // First we have to kill all listener connections.
+ // They might rely on our member and can hinder us on releasing them.
+ css::uno::Reference< css::uno::XInterface > xThis ( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY );
+ css::lang::EventObject aEvent( xThis );
+ m_aListenerContainer.disposeAndClear( aEvent );
+
+ // Clear our child task container and forget all task references hardly.
+ // Normally all open document was already closed by our terminate() function before ...
+ // New opened frames will have a problem now .-)
+ m_aChildTaskContainer.clear();
+
+ // Dispose our helper too.
+ css::uno::Reference< css::lang::XEventListener > xFramesHelper( m_xFramesHelper, css::uno::UNO_QUERY );
+ if( xFramesHelper.is() )
+ xFramesHelper->disposing( aEvent );
+
+ // At least clean up other member references.
+ m_xDispatchHelper.clear();
+ m_xFramesHelper.clear();
+ m_xContext.clear();
+
+ m_xPipeTerminator.clear();
+ m_xQuickLauncher.clear();
+ m_xSWThreadManager.clear();
+
+ // we need a copy because the disposing might call the removeEventListener method
+ std::vector< css::uno::Reference<css::frame::XTerminateListener> > xComponentDllListeners;
+ xComponentDllListeners.swap(m_xComponentDllListeners);
+ for (auto& xListener: xComponentDllListeners)
+ {
+ xListener->disposing(aEvent);
+ }
+ xComponentDllListeners.clear();
+ m_xSfxTerminator.clear();
+ m_xCommandOptions.reset();
+
+ // From this point nothing will work further on this object ...
+ // excepting our dtor() .-)
+ m_aTransactionManager.setWorkingMode( E_CLOSE );
+}
+
+/*
+ @interface XComponent
+ @short add/remove listener for dispose events
+ @descr Add an event listener to this object, if you wish to get information
+ about our dying!
+ You must release this listener reference during your own disposing() method.
+
+ @attention Our container is threadsafe himself. So we don't need any lock here.
+ @param "xListener", reference to valid listener. We don't accept invalid values!
+ @threadsafe yes
+*/
+void SAL_CALL Desktop::addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener )
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Safe impossible cases
+ // Method not defined for all incoming parameter.
+ SAL_WARN_IF( !xListener.is(), "fwk.desktop", "Desktop::addEventListener(): Invalid parameter detected!" );
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ m_aListenerContainer.addInterface( cppu::UnoType<css::lang::XEventListener>::get(), xListener );
+}
+
+void SAL_CALL Desktop::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener )
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Safe impossible cases
+ // Method not defined for all incoming parameter.
+ SAL_WARN_IF( !xListener.is(), "fwk.desktop", "Desktop::removeEventListener(): Invalid parameter detected!" );
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS );
+
+ m_aListenerContainer.removeInterface( cppu::UnoType<css::lang::XEventListener>::get(), xListener );
+}
+
+/*-************************************************************************************************************
+ @interface XDispatchResultListener
+ @short callback for dispatches
+ @descr To support our method "loadComponentFromURL()" we are listener on temp. created dispatcher.
+ They call us back in this method "statusChanged()". As source of given state event, they give us a
+ reference to the target frame, in which dispatch was loaded! So we can use it to return his component
+ to caller! If no target exist ... ??!!
+
+ @seealso method loadComponentFromURL()
+
+ @param "aEvent", state event which (hopefully) valid information
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+void SAL_CALL Desktop::dispatchFinished( const css::frame::DispatchResultEvent& aEvent )
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ SolarMutexGuard g;
+ if( m_eLoadState != E_INTERACTION )
+ {
+ m_eLoadState = E_FAILED;
+ if( aEvent.State == css::frame::DispatchResultState::SUCCESS )
+ {
+ css::uno::Reference< css::frame::XFrame > xLastFrame; /// last target of "loadComponentFromURL()"!
+ if ( aEvent.Result >>= xLastFrame )
+ m_eLoadState = E_SUCCESSFUL;
+ }
+ }
+}
+
+/*-************************************************************************************************************
+ @interface XEventListener
+ @short not implemented!
+ @descr We are a status listener ... and so we must be an event listener too ... But we don't need it really!
+ We are a temp. listener only and our lifetime isn't smaller then of our temp. used dispatcher.
+
+ @seealso method loadComponentFromURL()
+*//*-*************************************************************************************************************/
+void SAL_CALL Desktop::disposing( const css::lang::EventObject& )
+{
+ SAL_WARN( "fwk.desktop", "Desktop::disposing(): Algorithm error! Normally desktop is temp. listener ... not all the time. So this method shouldn't be called." );
+}
+
+/*-************************************************************************************************************
+ @interface XInteractionHandler
+ @short callback for loadComponentFromURL for detected exceptions during load process
+ @descr In this case we must cancel loading and throw these detected exception again as result
+ of our own called method.
+
+ @attention a)
+ Normal loop in loadComponentFromURL() breaks on set member m_eLoadState during callback statusChanged().
+ But these interaction feature implements second way to do so! So we must look on different callbacks
+ for same operation ... and live with it.
+ b)
+ Search for given continuations too. If any XInteractionAbort exist ... use it to abort further operations
+ for currently running operation!
+
+ @seealso method loadComponentFromURL()
+ @seealso member m_eLoadState
+
+ @param "xRequest", request for interaction - normal a wrapped target exception from bottom services
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+void SAL_CALL Desktop::handle( const css::uno::Reference< css::task::XInteractionRequest >& xRequest )
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ // Don't check incoming request!
+ // If somewhere starts interaction without right parameter - he made something wrong.
+ // loadComponentFromURL() waits for these event - otherwise it yield for ever!
+
+ // get packed request and work on it first
+ // Attention: Don't set it on internal member BEFORE interaction is finished - because
+ // "loadComponentFromURL()" yield tills this member is changed. If we do it before
+ // interaction finish we can't guarantee right functionality. May be we cancel load process to earlier...
+ css::uno::Any aRequest = xRequest->getRequest();
+
+ // extract continuations from request
+ css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations = xRequest->getContinuations();
+ css::uno::Reference< css::task::XInteractionAbort > xAbort;
+ css::uno::Reference< css::task::XInteractionApprove > xApprove;
+ css::uno::Reference< css::document::XInteractionFilterSelect > xFilterSelect;
+ bool bAbort = false;
+
+ sal_Int32 nCount=lContinuations.getLength();
+ for( sal_Int32 nStep=0; nStep<nCount; ++nStep )
+ {
+ if( ! xAbort.is() )
+ xAbort.set( lContinuations[nStep], css::uno::UNO_QUERY );
+
+ if( ! xApprove.is() )
+ xApprove.set( lContinuations[nStep], css::uno::UNO_QUERY );
+
+ if( ! xFilterSelect.is() )
+ xFilterSelect.set( lContinuations[nStep], css::uno::UNO_QUERY );
+ }
+
+ // differ between abortable interactions (error, unknown filter...)
+ // and other ones (ambiguous but not unknown filter...)
+ css::task::ErrorCodeRequest aErrorCodeRequest;
+ if( aRequest >>= aErrorCodeRequest )
+ {
+ bool bWarning = ErrCode(aErrorCodeRequest.ErrCode).IsWarning();
+ if (xApprove.is() && bWarning)
+ xApprove->select();
+ else
+ if (xAbort.is())
+ {
+ xAbort->select();
+ bAbort = true;
+ }
+ }
+ else if( xAbort.is() )
+ {
+ xAbort->select();
+ bAbort = true;
+ }
+
+ // Ok now it's time to break yield loop of loadComponentFromURL().
+ // But only for really aborted requests!
+ // For example warnings will be approved and we wait for any success story ...
+ if (bAbort)
+ {
+ SolarMutexGuard g;
+ m_eLoadState = E_INTERACTION;
+ }
+}
+
+::sal_Int32 SAL_CALL Desktop::leaseNumber( const css::uno::Reference< css::uno::XInterface >& xComponent )
+{
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+ return m_xTitleNumberGenerator->leaseNumber (xComponent);
+}
+
+void SAL_CALL Desktop::releaseNumber( ::sal_Int32 nNumber )
+{
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+ m_xTitleNumberGenerator->releaseNumber (nNumber);
+}
+
+void SAL_CALL Desktop::releaseNumberForComponent( const css::uno::Reference< css::uno::XInterface >& xComponent )
+{
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+ m_xTitleNumberGenerator->releaseNumberForComponent (xComponent);
+}
+
+OUString SAL_CALL Desktop::getUntitledPrefix()
+{
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+ return m_xTitleNumberGenerator->getUntitledPrefix ();
+}
+
+/*-************************************************************************************************************
+ @short try to convert a property value
+ @descr This method is called from helperclass "OPropertySetHelper".
+ Don't use this directly!
+ You must try to convert the value of given PropHandle and
+ return results of this operation. This will be used to ask vetoable
+ listener. If no listener has a veto, we will change value really!
+ ( in method setFastPropertyValue_NoBroadcast(...) )
+
+ @attention Methods of OPropertySethelper are safed by using our shared osl mutex! (see ctor!)
+ So we must use different locks to make our implementation threadsafe.
+
+ @seealso class OPropertySetHelper
+ @seealso method setFastPropertyValue_NoBroadcast()
+
+ @param "aConvertedValue" new converted value of property
+ @param "aOldValue" old value of property
+ @param "nHandle" handle of property
+ @param "aValue" new value of property
+ @return sal_True if value will be changed, sal_FALSE otherway
+
+ @onerror IllegalArgumentException, if you call this with an invalid argument
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+sal_Bool SAL_CALL Desktop::convertFastPropertyValue( css::uno::Any& aConvertedValue ,
+ css::uno::Any& aOldValue ,
+ sal_Int32 nHandle ,
+ const css::uno::Any& aValue )
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ // Initialize state with sal_False !!!
+ // (Handle can be invalid)
+ bool bReturn = false;
+
+ switch( nHandle )
+ {
+ case PropHandle::SuspendQuickstartVeto:
+ bReturn = PropHelper::willPropertyBeChanged(
+ css::uno::Any(m_bSuspendQuickstartVeto),
+ aValue,
+ aOldValue,
+ aConvertedValue);
+ break;
+ case PropHandle::DispatchRecorderSupplier :
+ bReturn = PropHelper::willPropertyBeChanged(
+ css::uno::Any(m_xDispatchRecorderSupplier),
+ aValue,
+ aOldValue,
+ aConvertedValue);
+ break;
+ case PropHandle::Title :
+ bReturn = PropHelper::willPropertyBeChanged(
+ css::uno::Any(m_sTitle),
+ aValue,
+ aOldValue,
+ aConvertedValue);
+ break;
+ }
+
+ // Return state of operation.
+ return bReturn;
+}
+
+/*-************************************************************************************************************
+ @short set value of a transient property
+ @descr This method is calling from helperclass "OPropertySetHelper".
+ Don't use this directly!
+ Handle and value are valid everyway! You must set the new value only.
+ After this, baseclass send messages to all listener automatically.
+
+ @seealso class OPropertySetHelper
+
+ @param "nHandle" handle of property to change
+ @param "aValue" new value of property
+ @onerror An exception is thrown.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+void SAL_CALL Desktop::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle ,
+ const css::uno::Any& aValue )
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ switch( nHandle )
+ {
+ case PropHandle::SuspendQuickstartVeto: aValue >>= m_bSuspendQuickstartVeto;
+ break;
+ case PropHandle::DispatchRecorderSupplier: aValue >>= m_xDispatchRecorderSupplier;
+ break;
+ case PropHandle::Title: aValue >>= m_sTitle;
+ break;
+ }
+}
+
+/*-************************************************************************************************************
+ @short get value of a transient property
+ @descr This method is calling from helperclass "OPropertySetHelper".
+ Don't use this directly!
+
+ @attention We don't need any mutex or lock here ... We use threadsafe container or methods here only!
+
+ @seealso class OPropertySetHelper
+
+ @param "nHandle" handle of property to change
+ @param "aValue" current value of property
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+void SAL_CALL Desktop::getFastPropertyValue( css::uno::Any& aValue ,
+ sal_Int32 nHandle ) const
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ switch( nHandle )
+ {
+ case PropHandle::ActiveFrame : aValue <<= m_aChildTaskContainer.getActive();
+ break;
+ case PropHandle::IsPlugged : aValue <<= false;
+ break;
+ case PropHandle::SuspendQuickstartVeto: aValue <<= m_bSuspendQuickstartVeto;
+ break;
+ case PropHandle::DispatchRecorderSupplier: aValue <<= m_xDispatchRecorderSupplier;
+ break;
+ case PropHandle::Title: aValue <<= m_sTitle;
+ break;
+ }
+}
+
+::cppu::IPropertyArrayHelper& SAL_CALL Desktop::getInfoHelper()
+{
+ static cppu::OPropertyArrayHelper HELPER =
+ [] () {
+ return cppu::OPropertyArrayHelper {
+ {{"ActiveFrame", PropHandle::ActiveFrame,
+ cppu::UnoType<css::lang::XComponent>::get(),
+ (css::beans::PropertyAttribute::TRANSIENT
+ | css::beans::PropertyAttribute::READONLY)},
+ {"DispatchRecorderSupplier",
+ PropHandle::DispatchRecorderSupplier,
+ cppu::UnoType<css::frame::XDispatchRecorderSupplier>::get(),
+ css::beans::PropertyAttribute::TRANSIENT},
+ {"IsPlugged",
+ PropHandle::IsPlugged, cppu::UnoType<bool>::get(),
+ (css::beans::PropertyAttribute::TRANSIENT
+ | css::beans::PropertyAttribute::READONLY)},
+ {"SuspendQuickstartVeto", PropHandle::SuspendQuickstartVeto,
+ cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::TRANSIENT},
+ {"Title", PropHandle::Title, cppu::UnoType<OUString>::get(),
+ css::beans::PropertyAttribute::TRANSIENT}},
+ true};
+ }();
+ return HELPER;
+}
+
+/*-************************************************************************************************************
+ @short return propertysetinfo
+ @descr You can call this method to get information about transient properties
+ of this object.
+
+ @attention You must use global lock (method use static variable) ... and it must be the shareable osl mutex of it.
+ Because; our baseclass use this mutex to make his code threadsafe. We use our lock!
+ So we could have two different mutex/lock mechanism at the same object.
+
+ @seealso class OPropertySetHelper
+ @seealso interface XPropertySet
+ @seealso interface XMultiPropertySet
+ @return reference to object with information [XPropertySetInfo]
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL Desktop::getPropertySetInfo()
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ // Create structure of propertysetinfo for baseclass "OPropertySetHelper".
+ // (Use method "getInfoHelper()".)
+ static css::uno::Reference< css::beans::XPropertySetInfo > xInfo(
+ cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() ) );
+
+ return xInfo;
+}
+
+/*-************************************************************************************************************
+ @short return current component of current frame
+ @descr The desktop himself has no component. But every frame in subtree.
+ If somewhere call getCurrentComponent() at this class, we try to find the right frame and
+ then we try to become his component. It can be a VCL-component, the model or the controller
+ of founded frame.
+
+ @attention We don't work on internal member ... so we don't need any lock here.
+
+ @seealso method getCurrentComponent();
+
+ @param "xFrame", reference to valid frame in hierarchy. Method is not defined for invalid values.
+ But we don't check these. It's an IMPL-method and caller must use it right!
+ @return A reference to found component.
+
+ @onerror A null reference is returned.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+css::uno::Reference< css::lang::XComponent > Desktop::impl_getFrameComponent( const css::uno::Reference< css::frame::XFrame >& xFrame ) const
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Register transaction and reject wrong calls.
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ // Set default return value, if method failed.
+ css::uno::Reference< css::lang::XComponent > xComponent;
+ // Does no controller exists?
+ css::uno::Reference< css::frame::XController > xController = xFrame->getController();
+ if( !xController.is() )
+ {
+ // Controller not exist - use the VCL-component.
+ xComponent = xFrame->getComponentWindow();
+ }
+ else
+ {
+ // Does no model exists?
+ css::uno::Reference< css::frame::XModel > xModel = xController->getModel();
+ if( xModel.is() )
+ {
+ // Model exist - use the model as component.
+ xComponent = xModel;
+ }
+ else
+ {
+ // Model not exist - use the controller as component.
+ xComponent = xController;
+ }
+ }
+
+ return xComponent;
+}
+
+bool Desktop::impl_sendQueryTerminationEvent(Desktop::TTerminateListenerList& lCalledListener)
+{
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( cppu::UnoType<css::frame::XTerminateListener>::get());
+ if ( ! pContainer )
+ return true;
+
+ css::lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >(this) );
+
+ comphelper::OInterfaceIteratorHelper2 aIterator( *pContainer );
+ while ( aIterator.hasMoreElements() )
+ {
+ try
+ {
+ css::uno::Reference< css::frame::XTerminateListener > xListener(aIterator.next(), css::uno::UNO_QUERY);
+ if ( ! xListener.is() )
+ continue;
+ xListener->queryTermination( aEvent );
+ lCalledListener.push_back(xListener);
+ }
+ catch( const css::frame::TerminationVetoException& )
+ {
+ // first veto will stop the query loop.
+ return false;
+ }
+ catch( const css::uno::Exception& )
+ {
+ // clean up container.
+ // E.g. dead remote listener objects can make trouble otherwise.
+ // Iterator implementation allows removing objects during it's used !
+ aIterator.remove();
+ }
+ }
+
+ return true;
+}
+
+void Desktop::impl_sendCancelTerminationEvent(const Desktop::TTerminateListenerList& lCalledListener)
+{
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ css::lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >(this) );
+ for (const css::uno::Reference<css::frame::XTerminateListener>& xListener : lCalledListener)
+ {
+ try
+ {
+ // Note: cancelTermination() is a new and optional interface method !
+ css::uno::Reference< css::frame::XTerminateListener2 > xListenerGeneration2(xListener, css::uno::UNO_QUERY);
+ if ( ! xListenerGeneration2.is() )
+ continue;
+ xListenerGeneration2->cancelTermination( aEvent );
+ }
+ catch( const css::uno::Exception& )
+ {}
+ }
+}
+
+void Desktop::impl_sendTerminateToClipboard()
+{
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( cppu::UnoType<css::frame::XTerminateListener>::get());
+ if ( ! pContainer )
+ return;
+
+ comphelper::OInterfaceIteratorHelper2 aIterator( *pContainer );
+ while ( aIterator.hasMoreElements() )
+ {
+ try
+ {
+ css::frame::XTerminateListener* pTerminateListener =
+ static_cast< css::frame::XTerminateListener* >(aIterator.next());
+ css::uno::Reference< css::lang::XServiceInfo > xInfo( pTerminateListener, css::uno::UNO_QUERY );
+ if ( !xInfo.is() )
+ continue;
+
+ if ( xInfo->getImplementationName() != "com.sun.star.comp.svt.TransferableHelperTerminateListener" )
+ continue;
+
+ css::lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >(this) );
+ pTerminateListener->notifyTermination( aEvent );
+
+ // don't notify twice
+ aIterator.remove();
+ }
+ catch( const css::uno::Exception& )
+ {
+ // clean up container.
+ // E.g. dead remote listener objects can make trouble otherwise.
+ // Iterator implementation allows removing objects during it's used !
+ aIterator.remove();
+ }
+ }
+}
+
+void Desktop::impl_sendNotifyTerminationEvent()
+{
+ TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS );
+
+ comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( cppu::UnoType<css::frame::XTerminateListener>::get());
+ if ( ! pContainer )
+ return;
+
+ css::lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >(this) );
+
+ comphelper::OInterfaceIteratorHelper2 aIterator( *pContainer );
+ while ( aIterator.hasMoreElements() )
+ {
+ try
+ {
+ static_cast< css::frame::XTerminateListener* >(aIterator.next())->notifyTermination( aEvent );
+ }
+ catch( const css::uno::Exception& )
+ {
+ // clean up container.
+ // E.g. dead remote listener objects can make trouble otherwise.
+ // Iterator implementation allows removing objects during it's used !
+ aIterator.remove();
+ }
+ }
+}
+
+bool Desktop::impl_closeFrames(bool bAllowUI)
+{
+ SolarMutexClearableGuard aReadLock;
+ css::uno::Sequence< css::uno::Reference< css::frame::XFrame > > lFrames = m_aChildTaskContainer.getAllElements();
+ aReadLock.clear();
+
+ ::sal_Int32 c = lFrames.getLength();
+ ::sal_Int32 i = 0;
+ ::sal_Int32 nNonClosedFrames = 0;
+
+ for( i=0; i<c; ++i )
+ {
+ try
+ {
+ css::uno::Reference< css::frame::XFrame > xFrame = lFrames[i];
+
+ // XController.suspend() will show a UI ...
+ // Use it in case it was allowed from outside only.
+ bool bSuspended = false;
+ css::uno::Reference< css::frame::XController > xController = xFrame->getController();
+ if ( bAllowUI && xController.is() )
+ {
+ bSuspended = xController->suspend( true );
+ if ( ! bSuspended )
+ {
+ ++nNonClosedFrames;
+ if(m_bSession)
+ break;
+ else
+ continue;
+ }
+ }
+
+ // Try to close frame (in case no UI was allowed without calling XController->suspend() before!)
+ // But don't deliver ownership to any other one!
+ // This method can be called again.
+ css::uno::Reference< css::util::XCloseable > xClose( xFrame, css::uno::UNO_QUERY );
+ if ( xClose.is() )
+ {
+ try
+ {
+ xClose->close(false);
+ }
+ catch(const css::util::CloseVetoException&)
+ {
+ // Any internal process of this frame disagree with our request.
+ // Safe this state but don't break these loop. Other frames has to be closed!
+ ++nNonClosedFrames;
+
+ // Reactivate controller.
+ // It can happen that XController.suspend() returned true... but a registered close listener
+ // threw these veto exception. Then the controller has to be reactivated. Otherwise
+ // these document doesn't work any more.
+ if ( bSuspended && xController.is())
+ xController->suspend(false);
+ }
+
+ // If interface XClosable interface exists and was used...
+ // it's not allowed to use XComponent->dispose() also!
+ continue;
+ }
+
+ // XClosable not supported ?
+ // Then we have to dispose these frame hardly.
+ if ( xFrame.is() )
+ xFrame->dispose();
+
+ // Don't remove these frame from our child container!
+ // A frame do it by itself inside close()/dispose() method.
+ }
+ catch(const css::lang::DisposedException&)
+ {
+ // Dispose frames are closed frames.
+ // So we can count it here .-)
+ }
+ }
+
+ // reset the session
+ m_bSession = false;
+
+ return (nNonClosedFrames < 1);
+}
+
+} // namespace framework
+
+namespace {
+
+rtl::Reference<framework::Desktop> createDesktop(
+ css::uno::Reference<css::uno::XComponentContext> const & context)
+{
+ SolarMutexGuard g; // tdf#114025 init with SolarMutex to avoid deadlock
+ rtl::Reference<framework::Desktop> desktop(new framework::Desktop(context));
+ desktop->constructorInit();
+ return desktop;
+}
+
+}
+
+const rtl::Reference<framework::Desktop> & framework::getDesktop(
+ css::uno::Reference<css::uno::XComponentContext> const & context)
+{
+ static auto const instance = createDesktop(context);
+ return instance;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_Desktop_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(framework::getDesktop(context).get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/services/dispatchhelper.cxx b/framework/source/services/dispatchhelper.cxx
new file mode 100644
index 000000000..8f3d77d32
--- /dev/null
+++ b/framework/source/services/dispatchhelper.cxx
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/dispatchhelper.hxx>
+
+#include <com/sun/star/frame/XNotifyingDispatch.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+#include <comphelper/profilezone.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <utility>
+#include <vcl/threadex.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+namespace framework
+{
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL DispatchHelper::getImplementationName()
+{
+ return "com.sun.star.comp.framework.services.DispatchHelper";
+}
+
+sal_Bool SAL_CALL DispatchHelper::supportsService(const OUString& sServiceName)
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence<OUString> SAL_CALL DispatchHelper::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.DispatchHelper" };
+}
+
+/** ctor.
+
+ @param xSMGR the global uno service manager, which can be used to create own needed services.
+*/
+DispatchHelper::DispatchHelper(css::uno::Reference<css::uno::XComponentContext> xContext)
+ : m_xContext(std::move(xContext))
+ , m_aBlockFlag(false)
+{
+}
+
+/** dtor.
+*/
+DispatchHelper::~DispatchHelper() {}
+
+/** capsulate all steps of a dispatch request and provide so an easy way for dispatches.
+
+ @param xDispatchProvider
+ identifies the object, which provides may be valid dispatch objects for this execute.
+
+ @param sURL
+ describes the requested feature.
+
+ @param sTargetFrameName
+ points to the frame, which must be used (or may be created) for this dispatch.
+
+ @param nSearchFlags
+ in case the <var>sTargetFrameName</var> isn't unique, these flags regulate further searches.
+
+ @param lArguments
+ optional arguments for this request.
+
+ @return An Any which capsulate a possible result of the internal wrapped dispatch.
+ */
+css::uno::Any SAL_CALL DispatchHelper::executeDispatch(
+ const css::uno::Reference<css::frame::XDispatchProvider>& xDispatchProvider,
+ const OUString& sURL, const OUString& sTargetFrameName, sal_Int32 nSearchFlags,
+ const css::uno::Sequence<css::beans::PropertyValue>& lArguments)
+{
+ // check for valid parameters
+ if ((!xDispatchProvider.is()) || (!m_xContext.is()) || (sURL.isEmpty()))
+ {
+ return css::uno::Any();
+ }
+
+ // parse given URL
+ css::uno::Reference<css::util::XURLTransformer> xParser;
+ /* SAFE { */
+ {
+ std::scoped_lock aReadLock(m_mutex);
+ xParser = css::util::URLTransformer::create(m_xContext);
+ }
+ /* } SAFE */
+
+ css::util::URL aURL;
+ aURL.Complete = sURL;
+ xParser->parseStrict(aURL);
+
+ // search dispatcher
+ css::uno::Reference<css::frame::XDispatch> xDispatch
+ = xDispatchProvider->queryDispatch(aURL, sTargetFrameName, nSearchFlags);
+
+ utl::MediaDescriptor aDescriptor(lArguments);
+ bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault("OnMainThread", false);
+
+ if (bOnMainThread)
+ return vcl::solarthread::syncExecute([this, &xDispatch, &aURL, &lArguments]() {
+ return executeDispatch(xDispatch, aURL, true, lArguments);
+ });
+ else
+ return executeDispatch(xDispatch, aURL, true, lArguments);
+}
+
+const css::uno::Any&
+DispatchHelper::executeDispatch(const css::uno::Reference<css::frame::XDispatch>& xDispatch,
+ const css::util::URL& aURL, bool SyncronFlag,
+ const css::uno::Sequence<css::beans::PropertyValue>& lArguments)
+{
+ comphelper::ProfileZone aZone("executeDispatch");
+ css::uno::Reference<css::uno::XInterface> xTHIS(static_cast<::cppu::OWeakObject*>(this),
+ css::uno::UNO_QUERY);
+ m_aResult.clear();
+
+ // check for valid parameters
+ if (xDispatch.is())
+ {
+ css::uno::Reference<css::frame::XNotifyingDispatch> xNotifyDispatch(xDispatch,
+ css::uno::UNO_QUERY);
+
+ // make sure that synchronous execution is used (if possible)
+ css::uno::Sequence<css::beans::PropertyValue> aArguments(lArguments);
+ sal_Int32 nLength = lArguments.getLength();
+ aArguments.realloc(nLength + 1);
+ auto pArguments = aArguments.getArray();
+ pArguments[nLength].Name = "SynchronMode";
+ pArguments[nLength].Value <<= SyncronFlag;
+
+ if (xNotifyDispatch.is())
+ {
+ // dispatch it with guaranteed notification
+ // Here we can hope for a result ... instead of the normal dispatch.
+ css::uno::Reference<css::frame::XDispatchResultListener> xListener(xTHIS,
+ css::uno::UNO_QUERY);
+ /* SAFE { */
+ {
+ std::scoped_lock aWriteLock(m_mutex);
+ m_xBroadcaster = xNotifyDispatch;
+ m_aBlockFlag = false;
+ }
+ /* } SAFE */
+
+ // dispatch it and wait for a notification
+ // TODO/MBA: waiting in main thread?!
+ xNotifyDispatch->dispatchWithNotification(aURL, aArguments, xListener);
+
+ std::unique_lock aWriteLock(m_mutex);
+ m_aBlock.wait(aWriteLock, [this] { return m_aBlockFlag; }); // wait for result
+ }
+ else
+ {
+ // dispatch it without any chance to get a result
+ xDispatch->dispatch(aURL, aArguments);
+ }
+ }
+
+ return m_aResult;
+}
+
+/** callback for started dispatch with guaranteed notifications.
+
+ We must save the result, so the method executeDispatch() can return it.
+ Further we must release the broadcaster (otherwise it can't die)
+ and unblock the waiting executeDispatch() request.
+
+ @param aResult
+ describes the result of the dispatch operation
+ */
+void SAL_CALL DispatchHelper::dispatchFinished(const css::frame::DispatchResultEvent& aResult)
+{
+ std::scoped_lock g(m_mutex);
+ m_aResult <<= aResult;
+ m_aBlockFlag = true;
+ m_aBlock.notify_one();
+ m_xBroadcaster.clear();
+}
+
+/** we have to release our broadcaster reference.
+
+ @param aEvent
+ describe the source of this event and MUST be our save broadcaster!
+ */
+void SAL_CALL DispatchHelper::disposing(const css::lang::EventObject&)
+{
+ std::scoped_lock g(m_mutex);
+ m_aResult.clear();
+ m_aBlockFlag = true;
+ m_aBlock.notify_one();
+ m_xBroadcaster.clear();
+}
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_DispatchHelper_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new framework::DispatchHelper(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/services/frame.cxx b/framework/source/services/frame.cxx
new file mode 100644
index 000000000..5fc6309aa
--- /dev/null
+++ b/framework/source/services/frame.cxx
@@ -0,0 +1,3340 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <utility>
+
+#include <dispatch/dispatchprovider.hxx>
+#include <dispatch/interceptionhelper.hxx>
+#include <dispatch/windowcommanddispatch.hxx>
+#include <loadenv/loadenv.hxx>
+#include <helper/oframes.hxx>
+#include <framework/framecontainer.hxx>
+#include <framework/titlehelper.hxx>
+#include <svtools/openfiledroptargetlistener.hxx>
+#include <classes/taskcreator.hxx>
+#include <loadenv/targethelper.hxx>
+#include <framework/framelistanalyzer.hxx>
+#include <helper/dockingareadefaultacceptor.hxx>
+#include <dispatch/dispatchinformationprovider.hxx>
+
+#include <pattern/window.hxx>
+#include <properties.h>
+#include <targets.h>
+
+#include <com/sun/star/awt/Toolkit.hpp>
+#include <com/sun/star/awt/XDevice.hpp>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
+#include <com/sun/star/frame/XFrame2.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XTitleChangeBroadcaster.hpp>
+#include <com/sun/star/frame/LayoutManager.hpp>
+#include <com/sun/star/frame/XDesktop.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/task/StatusIndicatorFactory.hpp>
+#include <com/sun/star/task/theJobExecutor.hpp>
+#include <com/sun/star/task/XJobExecutor.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <comphelper/multiinterfacecontainer3.hxx>
+#include <comphelper/multicontainer2.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <sal/log.hxx>
+#include <vcl/window.hxx>
+#include <vcl/wrkwin.hxx>
+#include <vcl/svapp.hxx>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/cmdoptions.hxx>
+#include <vcl/threadex.hxx>
+#include <mutex>
+
+using namespace framework;
+
+namespace {
+
+// This enum can be used to set different active states of frames
+enum EActiveState
+{
+ E_INACTIVE, // I am not a member of active path in tree and i don't have the focus.
+ E_ACTIVE, // I am in the middle of an active path in tree and i don't have the focus.
+ E_FOCUS // I have the focus now. I must a member of an active path!
+};
+
+/*-************************************************************************************************************
+ @short implements a normal frame of hierarchy
+ @descr An instance of these class can be a normal node in frame tree. A frame support influencing of his
+ subtree, find of subframes, activate- and deactivate-mechanism as well as
+ set/get of a frame window, component or controller.
+*//*-*************************************************************************************************************/
+class XFrameImpl:
+ private cppu::BaseMutex,
+ public cppu::PartialWeakComponentImplHelper<
+ css::lang::XServiceInfo, css::frame::XFrame2, css::awt::XWindowListener,
+ css::awt::XTopWindowListener, css::awt::XFocusListener,
+ css::document::XActionLockable, css::util::XCloseable,
+ css::frame::XComponentLoader, css::frame::XTitle,
+ css::frame::XTitleChangeBroadcaster, css::beans::XPropertySet,
+ css::beans::XPropertySetInfo>
+{
+public:
+
+ explicit XFrameImpl(css::uno::Reference< css::uno::XComponentContext > xContext);
+
+ /// Initialization function after having acquire()'d.
+ void initListeners();
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.Frame";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.frame.Frame"};
+ }
+
+ // XComponentLoader
+
+ virtual css::uno::Reference< css::lang::XComponent > SAL_CALL loadComponentFromURL(
+ const OUString& sURL,
+ const OUString& sTargetFrameName,
+ sal_Int32 nSearchFlags,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override;
+
+ // XFramesSupplier
+
+ virtual css::uno::Reference < css::frame::XFrames > SAL_CALL getFrames() override;
+ virtual css::uno::Reference < css::frame::XFrame > SAL_CALL getActiveFrame() override;
+ virtual void SAL_CALL setActiveFrame(const css::uno::Reference < css::frame::XFrame > & xFrame) override;
+
+ // XFrame
+
+ virtual void SAL_CALL initialize(const css::uno::Reference < css::awt::XWindow > & xWindow) override;
+ virtual css::uno::Reference < css::awt::XWindow > SAL_CALL getContainerWindow() override;
+ virtual void SAL_CALL setCreator(const css::uno::Reference < css::frame::XFramesSupplier > & xCreator) override;
+ virtual css::uno::Reference < css::frame::XFramesSupplier > SAL_CALL getCreator() override;
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName(const OUString & sName) override;
+ virtual css::uno::Reference < css::frame::XFrame > SAL_CALL findFrame(
+ const OUString & sTargetFrameName,
+ sal_Int32 nSearchFlags) override;
+ virtual sal_Bool SAL_CALL isTop() override;
+ virtual void SAL_CALL activate() override;
+ virtual void SAL_CALL deactivate() override;
+ virtual sal_Bool SAL_CALL isActive() override;
+ virtual void SAL_CALL contextChanged() override;
+ virtual sal_Bool SAL_CALL setComponent(
+ const css::uno::Reference < css::awt::XWindow > & xComponentWindow,
+ const css::uno::Reference < css::frame::XController > & xController) override;
+ virtual css::uno::Reference < css::awt::XWindow > SAL_CALL getComponentWindow() override;
+ virtual css::uno::Reference < css::frame::XController > SAL_CALL getController() override;
+ virtual void SAL_CALL addFrameActionListener(const css::uno::Reference < css::frame::XFrameActionListener > & xListener) override;
+ virtual void SAL_CALL removeFrameActionListener(const css::uno::Reference < css::frame::XFrameActionListener > & xListener) override;
+
+ // XComponent
+
+ virtual void SAL_CALL disposing() override;
+ virtual void SAL_CALL addEventListener(const css::uno::Reference < css::lang::XEventListener > & xListener) override;
+ virtual void SAL_CALL removeEventListener(const css::uno::Reference < css::lang::XEventListener > & xListener) override;
+
+ // XStatusIndicatorFactory
+
+ virtual css::uno::Reference < css::task::XStatusIndicator > SAL_CALL createStatusIndicator() override;
+
+ // XDispatchProvider
+
+ virtual css::uno::Reference < css::frame::XDispatch > SAL_CALL queryDispatch(const css::util::URL & aURL,
+ const OUString & sTargetFrameName,
+ sal_Int32 nSearchFlags) override;
+ virtual css::uno::Sequence < css::uno::Reference < css::frame::XDispatch > > SAL_CALL queryDispatches(
+ const css::uno::Sequence < css::frame::DispatchDescriptor > & lDescriptor) override;
+
+ // XDispatchProviderInterception
+
+ virtual void SAL_CALL registerDispatchProviderInterceptor(
+ const css::uno::Reference < css::frame::XDispatchProviderInterceptor > & xInterceptor) override;
+ virtual void SAL_CALL releaseDispatchProviderInterceptor(
+ const css::uno::Reference < css::frame::XDispatchProviderInterceptor > & xInterceptor) override;
+
+ // XDispatchInformationProvider
+
+ virtual css::uno::Sequence < sal_Int16 > SAL_CALL getSupportedCommandGroups() override;
+ virtual css::uno::Sequence < css::frame::DispatchInformation > SAL_CALL getConfigurableDispatchInformation(sal_Int16 nCommandGroup) override;
+
+ // XWindowListener
+ // Attention: windowResized() and windowShown() are implement only! All other are empty!
+
+ virtual void SAL_CALL windowResized(const css::awt::WindowEvent & aEvent) override;
+ virtual void SAL_CALL windowMoved(const css::awt::WindowEvent & /*aEvent*/ ) override {};
+ virtual void SAL_CALL windowShown(const css::lang::EventObject & aEvent) override;
+ virtual void SAL_CALL windowHidden(const css::lang::EventObject & aEvent) override;
+
+ // XFocusListener
+ // Attention: focusLost() not implemented yet!
+
+ virtual void SAL_CALL focusGained(const css::awt::FocusEvent & aEvent) override;
+ virtual void SAL_CALL focusLost(const css::awt::FocusEvent & /*aEvent*/ ) override {};
+
+ // XTopWindowListener
+ // Attention: windowActivated(), windowDeactivated() and windowClosing() are implement only! All other are empty!
+
+ virtual void SAL_CALL windowActivated(const css::lang::EventObject & aEvent) override;
+ virtual void SAL_CALL windowDeactivated(const css::lang::EventObject & aEvent) override;
+ virtual void SAL_CALL windowOpened(const css::lang::EventObject & /*aEvent*/ ) override {};
+ virtual void SAL_CALL windowClosing(const css::lang::EventObject & aEvent) override;
+ virtual void SAL_CALL windowClosed(const css::lang::EventObject & /*aEvent*/ ) override {};
+ virtual void SAL_CALL windowMinimized(const css::lang::EventObject & /*aEvent*/ ) override {};
+ virtual void SAL_CALL windowNormalized(const css::lang::EventObject & /*aEvent*/ ) override {};
+
+ // XEventListener
+
+ virtual void SAL_CALL disposing(const css::lang::EventObject & aEvent) override;
+
+ // XActionLockable
+
+ virtual sal_Bool SAL_CALL isActionLocked() override;
+ virtual void SAL_CALL addActionLock() override;
+ virtual void SAL_CALL removeActionLock() override;
+ virtual void SAL_CALL setActionLocks(sal_Int16 nLock) override;
+ virtual sal_Int16 SAL_CALL resetActionLocks() override;
+
+ // XCloseable
+
+ virtual void SAL_CALL close(sal_Bool bDeliverOwnership) override;
+
+ // XCloseBroadcaster
+
+ virtual void SAL_CALL addCloseListener(const css::uno::Reference < css::util::XCloseListener > & xListener) override;
+ virtual void SAL_CALL removeCloseListener(const css::uno::Reference < css::util::XCloseListener > & xListener) override;
+
+ // XTitle
+
+ virtual OUString SAL_CALL getTitle() override;
+ virtual void SAL_CALL setTitle(const OUString & sTitle) override;
+
+ // XTitleChangeBroadcaster
+
+ virtual void SAL_CALL addTitleChangeListener(const css::uno::Reference < css::frame::XTitleChangeListener > & xListener) override;
+ virtual void SAL_CALL removeTitleChangeListener(const css::uno::Reference < css::frame::XTitleChangeListener > & xListenr) override;
+
+ // XFrame2 attributes
+
+ virtual css::uno::Reference < css::container::XNameContainer > SAL_CALL getUserDefinedAttributes() override;
+
+ virtual css::uno::Reference < css::frame::XDispatchRecorderSupplier > SAL_CALL getDispatchRecorderSupplier() override;
+ virtual void SAL_CALL setDispatchRecorderSupplier(const css::uno::Reference < css::frame::XDispatchRecorderSupplier > & ) override;
+
+ virtual css::uno::Reference < css::uno::XInterface > SAL_CALL getLayoutManager() override;
+ virtual void SAL_CALL setLayoutManager(const css::uno::Reference < css::uno::XInterface > & ) override;
+
+ // XPropertySet
+ virtual css::uno::Reference < css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
+
+ virtual void SAL_CALL setPropertyValue(const OUString & sProperty, const css::uno::Any & aValue) override;
+
+ virtual css::uno::Any SAL_CALL getPropertyValue(const OUString & sProperty) override;
+
+ virtual void SAL_CALL addPropertyChangeListener(
+ const OUString & sProperty,
+ const css::uno::Reference < css::beans::XPropertyChangeListener > & xListener) override;
+
+ virtual void SAL_CALL removePropertyChangeListener(
+ const OUString & sProperty,
+ const css::uno::Reference < css::beans::XPropertyChangeListener > & xListener) override;
+
+ virtual void SAL_CALL addVetoableChangeListener(
+ const OUString & sProperty,
+ const css::uno::Reference < css::beans::XVetoableChangeListener > & xListener) override;
+
+ virtual void SAL_CALL removeVetoableChangeListener(
+ const OUString & sProperty,
+ const css::uno::Reference < css::beans::XVetoableChangeListener > & xListener) override;
+
+ // XPropertySetInfo
+ virtual css::uno::Sequence < css::beans::Property > SAL_CALL getProperties() override;
+
+ virtual css::beans::Property SAL_CALL getPropertyByName(const OUString & sName) override;
+
+ virtual sal_Bool SAL_CALL hasPropertyByName(const OUString & sName) override;
+
+
+private:
+
+ void impl_setPropertyValue(sal_Int32 nHandle,
+ const css::uno::Any& aValue);
+
+ css::uno::Any impl_getPropertyValue(sal_Int32 nHandle);
+
+ /** set a new owner for this helper.
+ *
+ * This owner is used as source for all broadcasted events.
+ * Further we hold it weak, because we don't wish to be disposed() .-)
+ */
+ void impl_setPropertyChangeBroadcaster(const css::uno::Reference< css::uno::XInterface >& xBroadcaster);
+
+ /** add a new property info to the set of supported ones.
+ *
+ * @param aProperty
+ * describes the new property.
+ *
+ * @throw [css::beans::PropertyExistException]
+ * if a property with the same name already exists.
+ *
+ * Note: The consistence of the whole set of properties is not checked here.
+ * Means e.g. ... a handle which exists more than once is not detected.
+ * The owner of this class has to be sure, that every new property does
+ * not clash with any existing one.
+ */
+ void impl_addPropertyInfo(const css::beans::Property& aProperty);
+
+ /** mark the object as "dead".
+ */
+ void impl_disablePropertySet();
+
+ bool impl_existsVeto(const css::beans::PropertyChangeEvent& aEvent);
+
+ void impl_notifyChangeListener(const css::beans::PropertyChangeEvent& aEvent);
+
+ /*-****************************************************************************************************
+ @short helper methods
+ @descr Follow methods are needed at different points of our code (more than ones!).
+
+ @attention Threadsafe methods are signed by "implts_..."!
+ *//*-*****************************************************************************************************/
+
+ // threadsafe
+ void implts_sendFrameActionEvent ( const css::frame::FrameAction& aAction );
+ void implts_resizeComponentWindow ( );
+ void implts_setIconOnWindow ( );
+ void implts_startWindowListening ( );
+ void implts_stopWindowListening ( );
+ void implts_checkSuicide ( );
+ void implts_forgetSubFrames ( );
+
+ // non threadsafe
+ void impl_checkMenuCloser ( );
+ void impl_setCloser ( const css::uno::Reference< css::frame::XFrame2 >& xFrame , bool bState );
+
+ void disableLayoutManager(const css::uno::Reference< css::frame::XLayoutManager2 >& xLayoutManager);
+
+ void checkDisposed() {
+ osl::MutexGuard g(rBHelper.rMutex);
+ if (rBHelper.bInDispose || rBHelper.bDisposed) {
+ throw css::lang::DisposedException("Frame disposed");
+ }
+ }
+
+// variables
+// -threadsafe by SolarMutex
+
+ /// reference to factory, which has create this instance
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ /// reference to factory helper to create status indicator objects
+ css::uno::Reference< css::task::XStatusIndicatorFactory > m_xIndicatorFactoryHelper;
+ /// points to an external set progress, which should be used instead of the internal one.
+ css::uno::WeakReference< css::task::XStatusIndicator > m_xIndicatorInterception;
+ /// helper for XDispatch/Provider and interception interfaces
+ css::uno::Reference< css::frame::XDispatchProvider > m_xDispatchHelper;
+ /// helper for XFrames, XIndexAccess and XElementAccess interfaces
+ css::uno::Reference< css::frame::XFrames > m_xFramesHelper;
+ /// container for ALL Listener
+ comphelper::OMultiTypeInterfaceContainerHelper2 m_aListenerContainer;
+ /// parent of this frame
+ css::uno::Reference< css::frame::XFramesSupplier > m_xParent;
+ /// containerwindow of this frame for embedded components
+ css::uno::Reference< css::awt::XWindow > m_xContainerWindow;
+ /// window of the actual component
+ css::uno::Reference< css::awt::XWindow > m_xComponentWindow;
+ /// controller of the actual frame
+ css::uno::Reference< css::frame::XController > m_xController;
+ /// listen to drag & drop
+ css::uno::Reference< css::datatransfer::dnd::XDropTargetListener > m_xDropTargetListener;
+ /// state, if I am a member of active path in tree or I have the focus or...
+ EActiveState m_eActiveState;
+ /// name of this frame
+ OUString m_sName;
+ /// frame has no parent or the parent is a task or the desktop
+ bool m_bIsFrameTop;
+ /// due to FrameActionEvent
+ bool m_bConnected;
+ sal_Int16 m_nExternalLockCount;
+ /// is used for dispatch recording and will be set/get from outside. Frame provide it only!
+ css::uno::Reference< css::frame::XDispatchRecorderSupplier > m_xDispatchRecorderSupplier;
+ /// ref counted class to support disabling commands defined by configuration file
+ SvtCommandOptions m_aCommandOptions;
+ /// in case of CloseVetoException on method close() was thrown by ourself - we must close ourself later if no internal processes are running
+ bool m_bSelfClose;
+ /// indicates, if this frame is used in hidden mode or not
+ bool m_bIsHidden;
+ /// The container window has WindowExtendedStyle::DocHidden set.
+ bool m_bDocHidden = false;
+ /// is used to layout the child windows of the frame.
+ css::uno::Reference< css::frame::XLayoutManager2 > m_xLayoutManager;
+ css::uno::Reference< css::frame::XDispatchInformationProvider > m_xDispatchInfoHelper;
+ css::uno::Reference< css::frame::XTitle > m_xTitleHelper;
+
+ std::unique_ptr<WindowCommandDispatch> m_pWindowCommandDispatch;
+
+ typedef std::unordered_map<OUString, css::beans::Property> TPropInfoHash;
+ TPropInfoHash m_lProps;
+
+ comphelper::OMultiTypeInterfaceContainerHelperVar3<css::beans::XPropertyChangeListener, OUString> m_lSimpleChangeListener;
+ comphelper::OMultiTypeInterfaceContainerHelperVar3<css::beans::XVetoableChangeListener, OUString> m_lVetoChangeListener;
+
+ // hold it weak ... otherwise this helper has to be "killed" explicitly .-)
+ css::uno::WeakReference< css::uno::XInterface > m_xBroadcaster;
+
+ FrameContainer m_aChildFrameContainer; /// array of child frames
+ /**
+ * URL of the file that is being loaded, when the load already started by we have no controller
+ * yet.
+ */
+ OUString m_aURL;
+};
+
+
+/*-****************************************************************************************************
+ @short standard constructor to create instance by factory
+ @descr This constructor initialize a new instance of this class by valid factory,
+ and will be set valid values on his member and baseclasses.
+
+ @attention a) Don't use your own reference during a UNO-Service-ctor! There is no guarantee, that you
+ will get over this. (e.g. using of your reference as parameter to initialize some member)
+ Do such things in DEFINE_INIT_SERVICE() method, which is called automatically after your ctor!!!
+ b) Baseclass OBroadcastHelper is a typedef in namespace cppu!
+ The microsoft compiler has some problems to handle it right BY using namespace explicitly ::cppu::OBroadcastHelper.
+ If we write it without a namespace or expand the typedef to OBroadcastHelperVar<...> -> it will be OK!?
+ I don't know why! (other compiler not tested .. but it works!)
+
+ @seealso method DEFINE_INIT_SERVICE()
+
+ @param xContext is the multi service manager, which creates this instance.
+ The value must be different from NULL!
+ @onerror ASSERT in debug version or nothing in release version.
+*//*-*****************************************************************************************************/
+XFrameImpl::XFrameImpl( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : PartialWeakComponentImplHelper(m_aMutex)
+ // init member
+ , m_xContext (std::move( xContext ))
+ , m_aListenerContainer ( m_aMutex )
+ , m_eActiveState ( E_INACTIVE )
+ , m_bIsFrameTop ( true ) // I think we are top without a parent ... and there is no parent yet!
+ , m_bConnected ( false ) // There exist no component inside of use => sal_False, we are not connected!
+ , m_nExternalLockCount ( 0 )
+ , m_bSelfClose ( false ) // Important!
+ , m_bIsHidden ( true )
+ , m_lSimpleChangeListener ( m_aMutex )
+ , m_lVetoChangeListener ( m_aMutex )
+{
+}
+
+void XFrameImpl::initListeners()
+{
+ css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY_THROW);
+
+ // Initialize a new dispatchhelper-object to handle dispatches.
+ // We use these helper as slave for our interceptor helper ... not directly!
+ // But he is event listener on THIS instance!
+ rtl::Reference<DispatchProvider> xDispatchProvider = new DispatchProvider( m_xContext, this );
+
+ m_xDispatchInfoHelper = new DispatchInformationProvider(m_xContext, this);
+
+ // Initialize a new interception helper object to handle dispatches and implement an interceptor mechanism.
+ // Set created dispatch provider as slowest slave of it.
+ // Hold interception helper by reference only - not by pointer!
+ // So it's easier to destroy it.
+ m_xDispatchHelper = new InterceptionHelper( this, xDispatchProvider );
+
+ // Initialize a new XFrames-helper-object to handle XIndexAccess and XElementAccess.
+ // We hold member as reference ... not as pointer too!
+ // Attention: We share our frame container with this helper. Container is threadsafe himself ... So I think we can do that.
+ // But look on dispose() for right order of deinitialization.
+ m_xFramesHelper = new OFrames( this, &m_aChildFrameContainer );
+
+ // Initialize the drop target listener.
+ // We hold member as reference ... not as pointer too!
+ m_xDropTargetListener = new OpenFileDropTargetListener( m_xContext, this );
+
+ // Safe impossible cases
+ // We can't work without these helpers!
+ SAL_WARN_IF( !xDispatchProvider.is(), "fwk.frame", "XFrameImpl::XFrameImpl(): Slowest slave for dispatch- and interception helper "
+ "is not valid. XDispatchProvider, XDispatch, XDispatchProviderInterception are not full supported!" );
+ SAL_WARN_IF( !m_xDispatchHelper.is(), "fwk.frame", "XFrameImpl::XFrameImpl(): Interception helper is not valid. XDispatchProvider, "
+ "XDispatch, XDispatchProviderInterception are not full supported!" );
+ SAL_WARN_IF( !m_xFramesHelper.is(), "fwk.frame", "XFrameImpl::XFrameImpl(): Frames helper is not valid. XFrames, "
+ "XIndexAccess and XElementAccess are not supported!" );
+ SAL_WARN_IF( !m_xDropTargetListener.is(), "fwk.frame", "XFrameImpl::XFrameImpl(): DropTarget helper is not valid. "
+ "Drag and drop without functionality!" );
+
+ // establish notifies for changing of "disabled commands" configuration during runtime
+ m_aCommandOptions.EstablishFrameCallback(this);
+
+ // Create an initial layout manager
+ // Create layout manager and connect it to the newly created frame
+ m_xLayoutManager = css::frame::LayoutManager::create(m_xContext);
+
+ // set information about all supported properties
+ impl_setPropertyChangeBroadcaster(static_cast< css::frame::XFrame* >(this));
+ impl_addPropertyInfo(
+ css::beans::Property(
+ FRAME_PROPNAME_ASCII_DISPATCHRECORDERSUPPLIER,
+ FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER,
+ cppu::UnoType<css::frame::XDispatchRecorderSupplier>::get(),
+ css::beans::PropertyAttribute::TRANSIENT));
+ impl_addPropertyInfo(
+ css::beans::Property(
+ FRAME_PROPNAME_ASCII_INDICATORINTERCEPTION,
+ FRAME_PROPHANDLE_INDICATORINTERCEPTION,
+ cppu::UnoType<css::task::XStatusIndicator>::get(),
+ css::beans::PropertyAttribute::TRANSIENT));
+ impl_addPropertyInfo(
+ css::beans::Property(
+ FRAME_PROPNAME_ASCII_ISHIDDEN,
+ FRAME_PROPHANDLE_ISHIDDEN,
+ cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY));
+ impl_addPropertyInfo(
+ css::beans::Property(
+ FRAME_PROPNAME_ASCII_LAYOUTMANAGER,
+ FRAME_PROPHANDLE_LAYOUTMANAGER,
+ cppu::UnoType<css::frame::XLayoutManager>::get(),
+ css::beans::PropertyAttribute::TRANSIENT));
+ impl_addPropertyInfo(
+ css::beans::Property(
+ FRAME_PROPNAME_ASCII_TITLE,
+ FRAME_PROPHANDLE_TITLE,
+ cppu::UnoType<OUString>::get(),
+ css::beans::PropertyAttribute::TRANSIENT));
+ impl_addPropertyInfo(css::beans::Property(FRAME_PROPNAME_ASCII_URL, FRAME_PROPHANDLE_URL,
+ cppu::UnoType<OUString>::get(),
+ css::beans::PropertyAttribute::TRANSIENT));
+}
+
+/*-************************************************************************************************************
+ @interface XComponentLoader
+ @short try to load given URL into a task
+ @descr You can give us some information about the content, which you will load into a frame.
+ We search or create this target for you, make a type detection of given URL and try to load it.
+ As result of this operation we return the new created component or nothing, if loading failed.
+ @param "sURL" , URL, which represent the content
+ @param "sTargetFrameName" , name of target frame or special value like "_self", "_blank" ...
+ @param "nSearchFlags" , optional arguments for frame search, if target is not a special one
+ @param "lArguments" , optional arguments for loading
+ @return A valid component reference, if loading was successful.
+ A null reference otherwise.
+
+ @onerror We return a null reference.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+css::uno::Reference< css::lang::XComponent > SAL_CALL XFrameImpl::loadComponentFromURL(
+ const OUString& sURL,
+ const OUString& sTargetFrameName,
+ sal_Int32 nSearchFlags,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
+{
+ checkDisposed();
+
+ css::uno::Reference< css::frame::XComponentLoader > xThis(this);
+
+ utl::MediaDescriptor aDescriptor(lArguments);
+ bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault("OnMainThread", false);
+
+ if (bOnMainThread)
+ {
+ // Make sure that we own the solar mutex, otherwise later
+ // vcl::SolarThreadExecutor::execute() will release the solar mutex, even if it's owned by
+ // another thread, leading to an std::abort() at the end.
+ SolarMutexGuard g;
+
+ return vcl::solarthread::syncExecute(std::bind(&LoadEnv::loadComponentFromURL, xThis,
+ m_xContext, sURL, sTargetFrameName,
+ nSearchFlags, lArguments));
+ }
+ else
+ return LoadEnv::loadComponentFromURL(xThis, m_xContext, sURL, sTargetFrameName,
+ nSearchFlags, lArguments);
+}
+
+/*-****************************************************************************************************
+ @short return access to append or remove children on desktop
+ @descr We don't implement these interface directly. We use a helper class to do this.
+ If you wish to add or delete children to/from the container, call these method to get
+ a reference to the helper.
+
+ @seealso class OFrames
+ @return A reference to the helper which answer your queries.
+
+ @onerror A null reference is returned.
+*//*-*****************************************************************************************************/
+css::uno::Reference< css::frame::XFrames > SAL_CALL XFrameImpl::getFrames()
+{
+ checkDisposed();
+
+ SolarMutexGuard g;
+ // Return access to all child frames to caller.
+ // Our childframe container is implemented in helper class OFrames and used as a reference m_xFramesHelper!
+ return m_xFramesHelper;
+}
+
+/*-****************************************************************************************************
+ @short get the current active child frame
+ @descr It must be a frameto. Direct children of a frame are frames only! No task or desktop is accepted.
+ We don't save this information directly in this class. We use our container-helper
+ to do that.
+
+ @seealso class OFrameContainer
+ @seealso method setActiveFrame()
+ @return A reference to our current active childframe, if anyone exist.
+ @return A null reference, if nobody is active.
+
+ @onerror A null reference is returned.
+*//*-*****************************************************************************************************/
+css::uno::Reference< css::frame::XFrame > SAL_CALL XFrameImpl::getActiveFrame()
+{
+ checkDisposed();
+
+ SolarMutexGuard g;
+ // Return current active frame.
+ // This information is available on the container.
+ return m_aChildFrameContainer.getActive();
+}
+
+/*-****************************************************************************************************
+ @short set the new active direct child frame
+ @descr It must be a frame to. Direct children of frame are frames only! No task or desktop is accepted.
+ We don't save this information directly in this class. We use our container-helper
+ to do that.
+
+ @seealso class OFrameContainer
+ @seealso method getActiveFrame()
+
+ @param "xFrame", reference to new active child. It must be an already existing child!
+ @onerror An assertion is thrown and element is ignored, if given frame isn't already a child of us.
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::setActiveFrame( const css::uno::Reference< css::frame::XFrame >& xFrame )
+{
+ checkDisposed();
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexResettableGuard aWriteLock;
+
+ // Copy necessary member for threadsafe access!
+ // m_aChildFrameContainer is threadsafe himself and he live if we live!!!
+ css::uno::Reference< css::frame::XFrame > xActiveChild = m_aChildFrameContainer.getActive();
+ EActiveState eActiveState = m_eActiveState;
+
+ aWriteLock.clear();
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+
+ // Don't work, if "new" active frame isn't different from current one!
+ // (xFrame==NULL is allowed to UNSET it!)
+ if( xActiveChild != xFrame )
+ {
+ // ... otherwise set new and deactivate old one.
+ m_aChildFrameContainer.setActive( xFrame );
+ if (
+ ( eActiveState != E_INACTIVE ) &&
+ xActiveChild.is()
+ )
+ {
+ xActiveChild->deactivate();
+ }
+ }
+
+ if( xFrame.is() )
+ {
+ // If last active frame had focus ...
+ // ... reset state to ACTIVE and send right FrameActionEvent for focus lost.
+ if( eActiveState == E_FOCUS )
+ {
+ aWriteLock.reset();
+ eActiveState = E_ACTIVE;
+ m_eActiveState = eActiveState;
+ aWriteLock.clear();
+ implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_DEACTIVATING );
+ }
+
+ // If last active frame was active ...
+ // but new one is not it ...
+ // ... set it as active one.
+ if ( eActiveState == E_ACTIVE && !xFrame->isActive() )
+ {
+ xFrame->activate();
+ }
+ }
+ else
+ // If this frame is active and has no active subframe anymore it is UI active too
+ if( eActiveState == E_ACTIVE )
+ {
+ aWriteLock.reset();
+ eActiveState = E_FOCUS;
+ m_eActiveState = eActiveState;
+ aWriteLock.clear();
+ implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_ACTIVATED );
+ }
+}
+
+/*-****************************************************************************************************
+ initialize new created layout manager
+**/
+void lcl_enableLayoutManager(const css::uno::Reference< css::frame::XLayoutManager2 >& xLayoutManager,
+ const css::uno::Reference< css::frame::XFrame >& xFrame )
+{
+ // Provide container window to our layout manager implementation
+ xLayoutManager->attachFrame(xFrame);
+
+ xFrame->addFrameActionListener(xLayoutManager);
+
+ rtl::Reference<DockingAreaDefaultAcceptor> xDockingAreaAcceptor = new DockingAreaDefaultAcceptor(xFrame);
+ xLayoutManager->setDockingAreaAcceptor(xDockingAreaAcceptor);
+}
+
+/*-****************************************************************************************************
+ deinitialize layout manager
+**/
+void XFrameImpl::disableLayoutManager(const css::uno::Reference< css::frame::XLayoutManager2 >& xLayoutManager)
+{
+ removeFrameActionListener(xLayoutManager);
+ xLayoutManager->setDockingAreaAcceptor(css::uno::Reference< css::ui::XDockingAreaAcceptor >());
+ xLayoutManager->attachFrame(css::uno::Reference< css::frame::XFrame >());
+}
+
+/*-****************************************************************************************************
+ @short initialize frame instance
+ @descr A frame needs a window. This method set a new one ... but should called one times only!
+ We use this window to listen for window events and forward it to our set component.
+ It's used as parent of component window too.
+
+ @seealso method getContainerWindow()
+ @seealso method setComponent()
+ @seealso member m_xContainerWindow
+
+ @param "xWindow", reference to new container window - must be valid!
+ @onerror We do nothing.
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::initialize( const css::uno::Reference< css::awt::XWindow >& xWindow )
+{
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ if (!xWindow.is())
+ throw css::uno::RuntimeException(
+ "XFrameImpl::initialize() called without a valid container window reference.",
+ static_cast< css::frame::XFrame* >(this));
+
+ checkDisposed();
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexResettableGuard aWriteLock;
+
+ if ( m_xContainerWindow.is() )
+ throw css::uno::RuntimeException(
+ "XFrameImpl::initialized() is called more than once, which is not useful nor allowed.",
+ static_cast< css::frame::XFrame* >(this));
+
+ // This must be the first call of this method!
+ // We should initialize our object and open it for working.
+ // Set the new window.
+ SAL_WARN_IF( m_xContainerWindow.is(), "fwk.frame", "XFrameImpl::initialize(): Leak detected! This state should never occur ..." );
+ m_xContainerWindow = xWindow;
+
+ // if window is initially visible, we will never get a windowShowing event
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ if (pWindow)
+ {
+ if (pWindow->IsVisible())
+ m_bIsHidden = false;
+ m_bDocHidden
+ = static_cast<bool>(pWindow->GetExtendedStyle() & WindowExtendedStyle::DocHidden);
+ }
+
+ css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager = m_xLayoutManager;
+
+ // Release lock ... because we call some impl methods, which are threadsafe by himself.
+ // If we hold this lock - we will produce our own deadlock!
+ aWriteLock.clear();
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+
+ // Avoid enabling the layout manager for hidden frames: it's expensive and
+ // provides little value.
+ if (xLayoutManager.is() && !m_bDocHidden)
+ lcl_enableLayoutManager(xLayoutManager, this);
+
+ // create progress helper
+ css::uno::Reference< css::frame::XFrame > xThis (static_cast< css::frame::XFrame* >(this),
+ css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::task::XStatusIndicatorFactory > xIndicatorFactory =
+ css::task::StatusIndicatorFactory::createWithFrame(m_xContext, xThis,
+ false/*DisableReschedule*/, true/*AllowParentShow*/ );
+
+ // SAFE -> ----------------------------------
+ aWriteLock.reset();
+ m_xIndicatorFactoryHelper = xIndicatorFactory;
+ aWriteLock.clear();
+ // <- SAFE ----------------------------------
+
+ // Start listening for events after setting it on helper class ...
+ // So superfluous messages are filtered to NULL :-)
+ implts_startWindowListening();
+
+ m_pWindowCommandDispatch.reset(new WindowCommandDispatch(m_xContext, this));
+
+ // Initialize title functionality
+ m_xTitleHelper = new TitleHelper( m_xContext, xThis, nullptr );
+}
+
+/*-****************************************************************************************************
+ @short returns current set container window
+ @descr The ContainerWindow property is used as a container for the component
+ in this frame. So this object implements a container interface too.
+ The instantiation of the container window is done by the user of this class.
+ The frame is the owner of its container window.
+
+ @seealso method initialize()
+ @return A reference to current set containerwindow.
+
+ @onerror A null reference is returned.
+*//*-*****************************************************************************************************/
+css::uno::Reference< css::awt::XWindow > SAL_CALL XFrameImpl::getContainerWindow()
+{
+ SolarMutexGuard g;
+ return m_xContainerWindow;
+}
+
+/*-****************************************************************************************************
+ @short set parent frame
+ @descr We need a parent to support some functionality! e.g. findFrame()
+ By the way we use the chance to set an internal information about our top state.
+ So we must not check this information during every isTop() call.
+ We are top, if our parent is the desktop instance or we haven't any parent.
+
+ @seealso getCreator()
+ @seealso findFrame()
+ @seealso isTop()
+ @seealso m_bIsFrameTop
+
+ @param xCreator
+ valid reference to our new owner frame, which should implement a supplier interface
+
+ @threadsafe yes
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::setCreator( const css::uno::Reference< css::frame::XFramesSupplier >& xCreator )
+{
+ checkDisposed();
+
+ /* SAFE { */
+ {
+ SolarMutexGuard aWriteLock;
+ m_xParent = xCreator;
+ }
+ /* } SAFE */
+
+ css::uno::Reference< css::frame::XDesktop > xIsDesktop( xCreator, css::uno::UNO_QUERY );
+ m_bIsFrameTop = ( xIsDesktop.is() || ! xCreator.is() );
+}
+
+/*-****************************************************************************************************
+ @short returns current parent frame
+ @descr The Creator is the parent frame container. If it is NULL, the frame is the uppermost one.
+
+ @seealso method setCreator()
+ @return A reference to current set parent frame container.
+
+ @onerror A null reference is returned.
+*//*-*****************************************************************************************************/
+css::uno::Reference< css::frame::XFramesSupplier > SAL_CALL XFrameImpl::getCreator()
+{
+ checkDisposed();
+ SolarMutexGuard g;
+ return m_xParent;
+}
+
+/*-****************************************************************************************************
+ @short returns current set name of frame
+ @descr This name is used to find target of findFrame() or queryDispatch() calls.
+
+ @seealso method setName()
+ @return Current set name of frame.
+
+ @onerror An empty string is returned.
+*//*-*****************************************************************************************************/
+OUString SAL_CALL XFrameImpl::getName()
+{
+ SolarMutexGuard g;
+ return m_sName;
+}
+
+/*-****************************************************************************************************
+ @short set new name for frame
+ @descr This name is used to find target of findFrame() or queryDispatch() calls.
+
+ @attention Special names like "_blank", "_self" aren't allowed...
+ "_beamer" excepts this rule!
+
+ @seealso method getName()
+
+ @param "sName", new frame name.
+ @onerror We do nothing.
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::setName( const OUString& sName )
+{
+ SolarMutexGuard g;
+ // Set new name... but look for invalid special target names!
+ // They are not allowed to set.
+ if (TargetHelper::isValidNameForFrame(sName))
+ m_sName = sName;
+}
+
+/*-****************************************************************************************************
+ @short search for frames
+ @descr This method searches for a frame with the specified name.
+ Frames may contain other frames (e.g. a frameset) and may
+ be contained in other frames. This hierarchy is searched by
+ this method.
+ First some special names are taken into account, i.e. "",
+ "_self", "_top", "_blank" etc. The nSearchFlags are ignored
+ when comparing these names with sTargetFrameName, further steps are
+ controlled by the search flags. If allowed, the name of the frame
+ itself is compared with the desired one, then ( again if allowed )
+ the method findFrame() is called for all children, for siblings
+ and as last for the parent frame.
+ If no frame with the given name is found until the top frames container,
+ a new top one is created, if this is allowed by a special
+ flag. The new frame also gets the desired name.
+
+ @param sTargetFrameName
+ special names (_blank, _self) or real name of target frame
+ @param nSearchFlags
+ optional flags which regulate search for non special target frames
+
+ @return A reference to found or may be new created frame.
+ @threadsafe yes
+*//*-*****************************************************************************************************/
+css::uno::Reference< css::frame::XFrame > SAL_CALL XFrameImpl::findFrame( const OUString& sTargetFrameName,
+ sal_Int32 nSearchFlags )
+{
+ css::uno::Reference< css::frame::XFrame > xTarget;
+
+ // 0) Ignore wrong parameter!
+ // We don't support search for following special targets.
+ // If we reject this requests - we must not check for such names
+ // in following code again and again. If we do not so -wrong
+ // search results can occur!
+
+ if ( sTargetFrameName == SPECIALTARGET_DEFAULT ) // valid for dispatches - not for findFrame()!
+ {
+ return nullptr;
+ }
+
+ // I) check for special defined targets first which must be handled exclusive.
+ // force using of "if() else if() ..."
+
+ // get threadsafe some necessary member which are necessary for following functionality
+ /* SAFE { */
+ SolarMutexResettableGuard aReadLock;
+ css::uno::Reference< css::frame::XFrame > xParent = m_xParent;
+ bool bIsTopFrame = m_bIsFrameTop;
+ bool bIsTopWindow = WindowHelper::isTopWindow(m_xContainerWindow);
+ aReadLock.clear();
+ /* } SAFE */
+
+ // I.I) "_blank"
+ // Not allowed for a normal frame - but for the desktop.
+ // Use helper class to do so. It use the desktop automatically.
+
+ if ( sTargetFrameName==SPECIALTARGET_BLANK )
+ {
+ TaskCreator aCreator(m_xContext);
+ xTarget = aCreator.createTask(sTargetFrameName, utl::MediaDescriptor());
+ }
+
+ // I.II) "_parent"
+ // It doesn't matter if we have a valid parent or not. User ask for him and get it.
+ // An empty result is a valid result too.
+
+ else if ( sTargetFrameName==SPECIALTARGET_PARENT )
+ {
+ xTarget = xParent;
+ }
+
+ // I.III) "_top"
+ // If we are not the top frame in this hierarchy, we must forward request to our parent.
+ // Otherwise we must return ourself.
+
+ else if ( sTargetFrameName==SPECIALTARGET_TOP )
+ {
+ if (bIsTopFrame)
+ xTarget = this;
+ else if (xParent.is()) // If we are not top - the parent MUST exist. But may it's better to check it again .-)
+ xTarget = xParent->findFrame(SPECIALTARGET_TOP,0);
+ }
+
+ // I.IV) "_self", ""
+ // This mean this frame in every case.
+
+ else if (
+ ( sTargetFrameName==SPECIALTARGET_SELF ) ||
+ ( sTargetFrameName.isEmpty() )
+ )
+ {
+ xTarget = this;
+ }
+
+ // I.V) "_beamer"
+ // This is a special sub frame of any task. We must return it if we found it on our direct children
+ // or create it there if it not already exists.
+ // Note: Such beamer exists for task(top) frames only!
+
+ else if ( sTargetFrameName==SPECIALTARGET_BEAMER )
+ {
+ // We are a task => search or create the beamer
+ if (bIsTopWindow)
+ {
+ xTarget = m_aChildFrameContainer.searchOnDirectChildrens(SPECIALTARGET_BEAMER);
+ if ( ! xTarget.is() )
+ {
+ /* TODO
+ Creation not supported yet!
+ Wait for new layout manager service because we can't plug it
+ inside already opened document of this frame...
+ */
+ }
+ }
+ // We aren't a task => forward request to our parent or ignore it.
+ else if (xParent.is())
+ xTarget = xParent->findFrame(SPECIALTARGET_BEAMER,0);
+ }
+
+ else
+ {
+
+ // II) otherwise use optional given search flags
+ // force using of combinations of such flags. means no "else" part of use if() statements.
+ // But we ust break further searches if target was already found.
+ // Order of using flags is fix: SELF - CHILDREN - SIBLINGS - PARENT
+ // TASK and CREATE are handled special.
+
+ // get threadsafe some necessary member which are necessary for following functionality
+ /* SAFE { */
+ aReadLock.reset();
+ OUString sOwnName = m_sName;
+ aReadLock.clear();
+ /* } SAFE */
+
+ // II.I) SELF
+ // Check for right name. If it's the searched one return ourself - otherwise
+ // ignore this flag.
+
+ if (
+ (nSearchFlags & css::frame::FrameSearchFlag::SELF) &&
+ (sOwnName == sTargetFrameName )
+ )
+ {
+ xTarget = this;
+ }
+
+ // II.II) CHILDREN
+ // Search on all children for the given target name.
+ // An empty name value can't occur here - because it must be already handled as "_self"
+ // before. Used helper function of container doesn't create any frame.
+ // It makes a deep search only.
+
+ if (
+ ( ! xTarget.is() ) &&
+ (nSearchFlags & css::frame::FrameSearchFlag::CHILDREN)
+ )
+ {
+ xTarget = m_aChildFrameContainer.searchOnAllChildrens(sTargetFrameName);
+ }
+
+ // II.III) TASKS
+ // This is a special flag. It regulate search on this task tree only or allow search on
+ // all other ones (which are sibling trees of us) too.
+ // Upper search must stop at this frame if we are the topest one and the TASK flag is not set
+ // or we can ignore it if we have no valid parent.
+
+ if (
+ ( bIsTopFrame && (nSearchFlags & css::frame::FrameSearchFlag::TASKS) ) ||
+ ( ! bIsTopFrame )
+ )
+ {
+
+ // II.III.I) SIBLINGS
+ // Search on all our direct siblings - means all children of our parent.
+ // Use this flag in combination with TASK. We must suppress such upper search if
+ // user has not set it and if we are a top frame.
+ // Attention: Don't forward this request to our parent as a findFrame() call.
+ // In such case we must protect us against recursive calls.
+ // Use snapshot of our parent. But don't use queryFrames() of XFrames interface.
+ // Because it's return all siblings and all her children including our children too
+ // if we call it with the CHILDREN flag. We don't need that - we need the direct container
+ // items of our parent only to start searches there. So we must use the container interface
+ // XIndexAccess instead of XFrames.
+
+ if (
+ ( ! xTarget.is() ) &&
+ (nSearchFlags & css::frame::FrameSearchFlag::SIBLINGS) &&
+ ( xParent.is() ) // search on siblings is impossible without a parent
+ )
+ {
+ css::uno::Reference< css::frame::XFramesSupplier > xSupplier( xParent, css::uno::UNO_QUERY );
+ if (xSupplier.is())
+ {
+ css::uno::Reference< css::container::XIndexAccess > xContainer = xSupplier->getFrames();
+ if (xContainer.is())
+ {
+ sal_Int32 nCount = xContainer->getCount();
+ for( sal_Int32 i=0; i<nCount; ++i )
+ {
+ css::uno::Reference< css::frame::XFrame > xSibling;
+ if (
+ // control unpacking
+ ( !(xContainer->getByIndex(i)>>=xSibling) ) ||
+ // check for valid items
+ ( ! xSibling.is() ) ||
+ // ignore ourself! (We are a part of this container too - but search on our children was already done.)
+ ( xSibling==static_cast< ::cppu::OWeakObject* >(this) )
+ )
+ {
+ continue;
+ }
+
+ // Don't allow upper search here! Use right flags to regulate it.
+ // And allow deep search on children only - if it was allowed for us too.
+ sal_Int32 nRightFlags = css::frame::FrameSearchFlag::SELF;
+ if (nSearchFlags & css::frame::FrameSearchFlag::CHILDREN)
+ nRightFlags |= css::frame::FrameSearchFlag::CHILDREN;
+ xTarget = xSibling->findFrame(sTargetFrameName, nRightFlags );
+ // perform search be breaking further search if a result exist.
+ if (xTarget.is())
+ break;
+ }
+ }
+ }
+ }
+
+ // II.III.II) PARENT
+ // Forward search to our parent (if he exists.)
+ // To prevent us against recursive and superfluous calls (which can occur if we allow him
+ // to search on his children too) we must change used search flags.
+
+ if (
+ ( ! xTarget.is() ) &&
+ (nSearchFlags & css::frame::FrameSearchFlag::PARENT) &&
+ ( xParent.is() )
+ )
+ {
+ if (xParent->getName() == sTargetFrameName)
+ xTarget = xParent;
+ else
+ {
+ sal_Int32 nRightFlags = nSearchFlags & ~css::frame::FrameSearchFlag::CHILDREN;
+ xTarget = xParent->findFrame(sTargetFrameName, nRightFlags);
+ }
+ }
+ }
+
+ // II.IV) CREATE
+ // If we haven't found any valid target frame by using normal flags - but user allowed us to create
+ // a new one ... we should do that. Used TaskCreator use Desktop instance automatically as parent!
+
+ if (
+ ( ! xTarget.is() ) &&
+ (nSearchFlags & css::frame::FrameSearchFlag::CREATE)
+ )
+ {
+ TaskCreator aCreator(m_xContext);
+ xTarget = aCreator.createTask(sTargetFrameName, utl::MediaDescriptor());
+ }
+ }
+
+ return xTarget;
+}
+
+/*-****************************************************************************************************
+ @descr Returns sal_True, if this frame is a "top frame", otherwise sal_False.
+ The "m_bIsFrameTop" member must be set in the ctor or setCreator() method.
+ A top frame is a member of the top frame container or a member of the
+ task frame container. Both containers can create new frames if the findFrame()
+ method of their css::frame::XFrame interface is called with a frame name not yet known.
+
+ @seealso ctor
+ @seealso method setCreator()
+ @seealso method findFrame()
+ @return true, if is it a top frame ... false otherwise.
+
+ @onerror No error should occur!
+*//*-*****************************************************************************************************/
+sal_Bool SAL_CALL XFrameImpl::isTop()
+{
+ checkDisposed();
+ SolarMutexGuard g;
+ // This information is set in setCreator().
+ // We are top, if our parent is a task or the desktop or if no parent exist!
+ return m_bIsFrameTop;
+}
+
+/*-****************************************************************************************************
+ @short activate frame in hierarchy
+ @descr This feature is used to mark active paths in our frame hierarchy.
+ You can be a listener for this event to react for it ... change some internal states or something else.
+
+ @seealso method deactivate()
+ @seealso method isActivate()
+ @seealso enum EActiveState
+ @seealso listener mechanism
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::activate()
+{
+ checkDisposed();
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexResettableGuard aWriteLock;
+
+ // Copy necessary member and free the lock.
+ // It's not necessary for m_aChildFrameContainer ... because
+ // he is threadsafe himself and live if we live.
+ css::uno::Reference< css::frame::XFrame > xActiveChild = m_aChildFrameContainer.getActive();
+ css::uno::Reference< css::frame::XFramesSupplier > xParent = m_xParent;
+ css::uno::Reference< css::frame::XFrame > xThis(this);
+ EActiveState eState = m_eActiveState;
+
+ aWriteLock.clear();
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+
+ // 1) If I am not active before...
+ if( eState == E_INACTIVE )
+ {
+ // ... do it then.
+ aWriteLock.reset();
+ eState = E_ACTIVE;
+ m_eActiveState = eState;
+ aWriteLock.clear();
+ // Deactivate sibling path and forward activation to parent ... if any parent exist!
+ if( xParent.is() )
+ {
+ // Every time set THIS frame as active child of parent and activate it.
+ // We MUST have a valid path from bottom to top as active path!
+ // But we must deactivate the old active sibling path first.
+
+ // Attention: Deactivation of an active path, deactivate the whole path ... from bottom to top!
+ // But we wish to deactivate founded sibling-tree only.
+ // [ see deactivate() / step 4) for further information! ]
+
+ xParent->setActiveFrame( xThis );
+
+ // Then we can activate from here to top.
+ // Attention: We are ACTIVE now. And the parent will call activate() at us!
+ // But we do nothing then! We are already activated.
+ xParent->activate();
+ }
+ // It's necessary to send event NOW - not before.
+ // Activation goes from bottom to top!
+ // That's the reason to activate parent first and send event now.
+ implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_ACTIVATED );
+ }
+
+ // 2) I was active before or current activated and there is a path from here to bottom, who CAN be active.
+ // But our direct child of path is not active yet.
+ // (It can be, if activation occur in the middle of a current path!)
+ // In these case we activate path to bottom to set focus on right frame!
+ if ( eState == E_ACTIVE && xActiveChild.is() && !xActiveChild->isActive() )
+ {
+ xActiveChild->activate();
+ }
+
+ // 3) I was active before or current activated. But if I have no active child => I will get the focus!
+ if ( eState == E_ACTIVE && !xActiveChild.is() )
+ {
+ aWriteLock.reset();
+ eState = E_FOCUS;
+ m_eActiveState = eState;
+ aWriteLock.clear();
+ implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_ACTIVATED );
+ }
+}
+
+/*-****************************************************************************************************
+ @short deactivate frame in hierarchy
+ @descr This feature is used to deactivate paths in our frame hierarchy.
+ You can be a listener for this event to react for it... change some internal states or something else.
+
+ @seealso method activate()
+ @seealso method isActivate()
+ @seealso enum EActiveState
+ @seealso listener mechanism
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::deactivate()
+{
+ checkDisposed();
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexResettableGuard aWriteLock;
+
+ // Copy necessary member and free the lock.
+ css::uno::Reference< css::frame::XFrame > xActiveChild = m_aChildFrameContainer.getActive();
+ css::uno::Reference< css::frame::XFramesSupplier > xParent = m_xParent;
+ css::uno::Reference< css::frame::XFrame > xThis(this);
+ EActiveState eState = m_eActiveState;
+
+ aWriteLock.clear();
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+
+ // Work only, if there something to do!
+ if( eState == E_INACTIVE )
+ return;
+
+
+ // 1) Deactivate all active children.
+ if ( xActiveChild.is() && xActiveChild->isActive() )
+ {
+ xActiveChild->deactivate();
+ }
+
+ // 2) If I have the focus - I will lost it now.
+ if( eState == E_FOCUS )
+ {
+ // Set new state INACTIVE(!) and send message to all listener.
+ // Don't set ACTIVE as new state. This frame is deactivated for next time - due to activate().
+ aWriteLock.reset();
+ eState = E_ACTIVE;
+ m_eActiveState = eState;
+ aWriteLock.clear();
+ implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_DEACTIVATING );
+ }
+
+ // 3) If I am active - I will be deactivated now.
+ if( eState == E_ACTIVE )
+ {
+ // Set new state and send message to all listener.
+ aWriteLock.reset();
+ eState = E_INACTIVE;
+ m_eActiveState = eState;
+ aWriteLock.clear();
+ implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_DEACTIVATING );
+ }
+
+ // 4) If there is a path from here to my parent...
+ // ... I am on the top or in the middle of deactivated subtree and action was started here.
+ // I must deactivate all frames from here to top, which are members of current path.
+ // Stop, if THESE frame not the active frame of our parent!
+ if ( xParent.is() && xParent->getActiveFrame() == xThis )
+ {
+ // We MUST break the path - otherwise we will get the focus - not our parent! ...
+ // Attention: Our parent don't call us again - WE ARE NOT ACTIVE YET!
+ // [ see step 3 and condition "if ( m_eActiveState!=INACTIVE ) ..." in this method! ]
+ xParent->deactivate();
+ }
+}
+
+/*-****************************************************************************************************
+ @short returns active state
+ @descr Call it to get information about current active state of this frame.
+
+ @seealso method activate()
+ @seealso method deactivate()
+ @seealso enum EActiveState
+ @return true if active, false otherwise.
+
+ @onerror No error should occur.
+*//*-*****************************************************************************************************/
+sal_Bool SAL_CALL XFrameImpl::isActive()
+{
+ checkDisposed();
+ SolarMutexGuard g;
+ return m_eActiveState == E_ACTIVE || m_eActiveState == E_FOCUS;
+}
+
+/*-****************************************************************************************************
+ @short ???
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::contextChanged()
+{
+ // Sometimes called during closing object...
+ // Impl-method is threadsafe himself!
+ // Send event to all listener for frame actions.
+ implts_sendFrameActionEvent( css::frame::FrameAction_CONTEXT_CHANGED );
+}
+
+/*-****************************************************************************************************
+ @short set new component inside the frame
+ @descr A frame is a container for a component. Use this method to set, change or release it!
+ We accept null references! The xComponentWindow will be a child of our container window
+ and get all window events from us.
+
+ @attention (a) A current set component can disagree with the suspend() request!
+ We don't set the new one and return with false then.
+ (b) It's possible to set:
+ (b1) a simple component here which supports the window only - no controller;
+ (b2) a full featured component which supports window and controller;
+ (b3) or both to NULL if outside code which to forget this component.
+
+ @seealso method getComponentWindow()
+ @seealso method getController()
+
+ @param xComponentWindow
+ valid reference to new component window which will be a child of internal container window
+ May <NULL/> for releasing.
+ @param xController
+ reference to new component controller
+ (may <NULL/> for releasing or setting of a simple component)
+
+ @return <TRUE/> if operation was successful, <FALSE/> otherwise.
+
+ @onerror We return <FALSE/>.
+ @threadsafe yes
+*//*-*****************************************************************************************************/
+sal_Bool SAL_CALL XFrameImpl::setComponent(const css::uno::Reference< css::awt::XWindow >& xComponentWindow,
+ const css::uno::Reference< css::frame::XController >& xController )
+{
+
+ // Ignore this HACK of sfx2!
+ // He call us with a valid controller without a valid window... that's not allowed!
+ if ( xController.is() && ! xComponentWindow.is() )
+ return true;
+
+ checkDisposed();
+
+ // Get threadsafe some copies of used members.
+ /* SAFE { */
+ SolarMutexClearableGuard aReadLock;
+ css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow;
+ css::uno::Reference< css::awt::XWindow > xOldComponentWindow = m_xComponentWindow;
+ css::uno::Reference< css::frame::XController > xOldController = m_xController;
+ VclPtr<vcl::Window> pOwnWindow = VCLUnoHelper::GetWindow( xContainerWindow );
+ bool bHadFocus = pOwnWindow != nullptr && pOwnWindow->HasChildPathFocus();
+ bool bWasConnected = m_bConnected;
+ aReadLock.clear();
+ /* } SAFE */
+
+ // stop listening on old window
+ // May it produce some trouble.
+ // But don't forget to listen on new window again ... or reactivate listening
+ // if we reject this setComponent() request and leave this method without changing the old window.
+ implts_stopWindowListening();
+
+ // Notify all listener, that this component (if current one exist) will be unloaded.
+ if (bWasConnected)
+ implts_sendFrameActionEvent( css::frame::FrameAction_COMPONENT_DETACHING );
+
+ // otherwise release old component first
+ // Always release controller before releasing window,
+ // because controller may want to access its window!
+ // But check for real changes - may the new controller is the old one.
+ if (
+ (xOldController.is() ) &&
+ (xOldController != xController)
+ )
+ {
+ /* ATTENTION
+ Don't suspend the old controller here. Because the outside caller must do that
+ by definition. We have to dispose it here only.
+ */
+
+ // Before we dispose this controller we should hide it inside this frame instance.
+ // We hold it alive for next calls by using xOldController!
+ /* SAFE {*/
+ {
+ SolarMutexGuard aWriteLock;
+ m_xController = nullptr;
+
+ auto pInterceptionHelper = dynamic_cast<InterceptionHelper*>(m_xDispatchHelper.get());
+ if (pInterceptionHelper)
+ {
+ css::uno::Reference<css::frame::XDispatchProvider> xDispatchProvider = pInterceptionHelper->GetSlave();
+ auto pDispatchProvider = dynamic_cast<DispatchProvider*>(xDispatchProvider.get());
+ if (pDispatchProvider)
+ {
+ pDispatchProvider->ClearProtocolHandlers();
+ }
+ }
+ }
+ /* } SAFE */
+
+ if (xOldController.is())
+ {
+ try
+ {
+ xOldController->dispose();
+ }
+ catch(const css::lang::DisposedException&)
+ {}
+ }
+ xOldController = nullptr;
+ }
+
+ // Now it's time to release the component window.
+ // If controller wasn't released successfully - this code line shouldn't be reached.
+ // Because in case of "suspend()==false" we return immediately with false ...
+ // see before
+ // Check for real changes too.
+ if (
+ (xOldComponentWindow.is() ) &&
+ (xOldComponentWindow != xComponentWindow)
+ )
+ {
+ /* SAFE { */
+ {
+ SolarMutexGuard aWriteLock;
+ m_xComponentWindow = nullptr;
+ }
+ /* } SAFE */
+
+ if (xOldComponentWindow.is())
+ {
+ try
+ {
+ xOldComponentWindow->dispose();
+ }
+ catch(const css::lang::DisposedException&)
+ {
+ }
+ }
+ xOldComponentWindow = nullptr;
+ }
+
+ // Now it's time to set the new component ...
+ // By the way - find out our new "load state" - means if we have a valid component inside.
+ /* SAFE { */
+ SolarMutexResettableGuard aWriteLock;
+ m_xComponentWindow = xComponentWindow;
+ m_xController = xController;
+
+ // Clear the URL on the frame itself, now that the controller has it.
+ m_aURL.clear();
+
+ m_bConnected = (m_xComponentWindow.is() || m_xController.is());
+ bool bIsConnected = m_bConnected;
+ aWriteLock.clear();
+ /* } SAFE */
+
+ // notifies all interest listener, that current component was changed or a new one was loaded
+ if (bIsConnected && bWasConnected)
+ implts_sendFrameActionEvent( css::frame::FrameAction_COMPONENT_REATTACHED );
+ else if (bIsConnected && !bWasConnected)
+ implts_sendFrameActionEvent( css::frame::FrameAction_COMPONENT_ATTACHED );
+
+ // A new component window doesn't know anything about current active/focus states.
+ // Set this information on it!
+ if ( bHadFocus && xComponentWindow.is() )
+ {
+ xComponentWindow->setFocus();
+ }
+
+ // If it was a new component window - we must resize it to fill out
+ // our container window.
+ implts_resizeComponentWindow();
+ // New component should change our current icon ...
+ implts_setIconOnWindow();
+ // OK - start listening on new window again - or do nothing if it is an empty one.
+ implts_startWindowListening();
+
+ /* SAFE { */
+ aWriteLock.reset();
+ impl_checkMenuCloser();
+ aWriteLock.clear();
+ /* } SAFE */
+
+ return true;
+}
+
+/*-****************************************************************************************************
+ @short returns current set component window
+ @descr Frames are used to display components. The actual displayed component is
+ held by the m_xComponentWindow property. If the component implements only a
+ XComponent interface, the communication between the frame and the
+ component is very restricted. Better integration is achievable through a
+ XController interface.
+ If the component wants other objects to be able to get information about its
+ ResourceDescriptor it has to implement a XModel interface.
+ This frame is the owner of the component window.
+
+ @seealso method setComponent()
+ @return css::uno::Reference to current set component window.
+
+ @onerror A null reference is returned.
+*//*-*****************************************************************************************************/
+css::uno::Reference< css::awt::XWindow > SAL_CALL XFrameImpl::getComponentWindow()
+{
+ checkDisposed();
+ SolarMutexGuard g;
+ return m_xComponentWindow;
+}
+
+/*-****************************************************************************************************
+ @short returns current set controller
+ @descr Frames are used to display components. The actual displayed component is
+ held by the m_xComponentWindow property. If the component implements only a
+ XComponent interface, the communication between the frame and the
+ component is very restricted. Better integration is achievable through a
+ XController interface.
+ If the component wants other objects to be able to get information about its
+ ResourceDescriptor it has to implement a XModel interface.
+ This frame is the owner of the component window.
+
+ @seealso method setComponent()
+ @return css::uno::Reference to current set controller.
+
+ @onerror A null reference is returned.
+*//*-*****************************************************************************************************/
+css::uno::Reference< css::frame::XController > SAL_CALL XFrameImpl::getController()
+{
+ SolarMutexGuard g;
+ return m_xController;
+}
+
+/*-****************************************************************************************************
+ @short add/remove listener for activate/deactivate/contextChanged events
+ @seealso method activate()
+ @seealso method deactivate()
+ @seealso method contextChanged()
+
+ @param "xListener" reference to your listener object
+ @onerror Listener is ignored.
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::addFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& xListener )
+{
+ checkDisposed();
+ m_aListenerContainer.addInterface( cppu::UnoType<css::frame::XFrameActionListener>::get(), xListener );
+}
+
+void SAL_CALL XFrameImpl::removeFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& xListener )
+{
+ m_aListenerContainer.removeInterface( cppu::UnoType<css::frame::XFrameActionListener>::get(), xListener );
+}
+
+/*-****************************************************************************************************
+ @short support two way mechanism to release a frame
+ @descr This method ask internal component (controller) if he accept this close request.
+ In case of <TRUE/> nothing will be happen (from point of caller of this close method).
+ In case of <FALSE/> a CloseVetoException is thrown. After such exception given parameter
+ <var>bDeliverOwnership</var> regulate which will be the new owner of this instance.
+
+ @attention It's the replacement for XTask::close() which is marked as obsolete method.
+
+ @param bDeliverOwnership
+ If parameter is set to <FALSE/> the original caller will be the owner after thrown
+ veto exception and must try to close this frame at later time again. Otherwise the
+ source of thrown exception is the right one. May it will be the frame himself.
+
+ @throws CloseVetoException
+ if any internal things will not be closed
+
+ @threadsafe yes
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::close( sal_Bool bDeliverOwnership )
+{
+ checkDisposed();
+
+ // At the end of this method may we must dispose ourself...
+ // and may nobody from outside hold a reference to us...
+ // then it's a good idea to do that by ourself.
+ css::uno::Reference< css::uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >(this) );
+
+ // Check any close listener before we look for currently running internal processes.
+ // Because if a listener disagree with this close() request - we have time to finish this
+ // internal operations too...
+ // Note: container is threadsafe himself.
+ css::lang::EventObject aSource (static_cast< ::cppu::OWeakObject*>(this));
+ comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer( cppu::UnoType<css::util::XCloseListener>::get());
+ if (pContainer!=nullptr)
+ {
+ comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer);
+ while (pIterator.hasMoreElements())
+ {
+ try
+ {
+ static_cast<css::util::XCloseListener*>(pIterator.next())->queryClosing( aSource, bDeliverOwnership );
+ }
+ catch( const css::uno::RuntimeException& )
+ {
+ pIterator.remove();
+ }
+ }
+ }
+
+ // Ok - no listener disagreed with this close() request
+ // check if this frame is used for any load process currently
+ if (isActionLocked())
+ {
+ if (bDeliverOwnership)
+ {
+ SolarMutexGuard g;
+ m_bSelfClose = true;
+ }
+
+ throw css::util::CloseVetoException("Frame in use for loading document...",static_cast< ::cppu::OWeakObject*>(this));
+ }
+
+ if ( ! setComponent(nullptr,nullptr) )
+ throw css::util::CloseVetoException("Component couldn't be detached...",static_cast< ::cppu::OWeakObject*>(this));
+
+ // If closing is allowed... inform all listeners and dispose this frame!
+ pContainer = m_aListenerContainer.getContainer( cppu::UnoType<css::util::XCloseListener>::get());
+ if (pContainer!=nullptr)
+ {
+ comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer);
+ while (pIterator.hasMoreElements())
+ {
+ try
+ {
+ static_cast<css::util::XCloseListener*>(pIterator.next())->notifyClosing( aSource );
+ }
+ catch( const css::uno::RuntimeException& )
+ {
+ pIterator.remove();
+ }
+ }
+ }
+
+ /* SAFE { */
+ {
+ SolarMutexGuard aWriteLock;
+ m_bIsHidden = true;
+ }
+ /* } SAFE */
+ impl_checkMenuCloser();
+
+ dispose();
+}
+
+/*-****************************************************************************************************
+ @short be a listener for close events!
+ @descr Adds/remove a CloseListener at this frame instance. If the close() method is called on
+ this object, the such listener are informed and can disagree with that by throwing
+ a CloseVetoException.
+
+ @seealso XFrameImpl::close()
+
+ @param xListener
+ reference to your listener object
+
+ @onerror Listener is ignored.
+
+ @threadsafe yes
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::addCloseListener( const css::uno::Reference< css::util::XCloseListener >& xListener )
+{
+ checkDisposed();
+ m_aListenerContainer.addInterface( cppu::UnoType<css::util::XCloseListener>::get(), xListener );
+}
+
+void SAL_CALL XFrameImpl::removeCloseListener( const css::uno::Reference< css::util::XCloseListener >& xListener )
+{
+ m_aListenerContainer.removeInterface( cppu::UnoType<css::util::XCloseListener>::get(), xListener );
+}
+
+OUString SAL_CALL XFrameImpl::getTitle()
+{
+ checkDisposed();
+
+ // SAFE ->
+ SolarMutexClearableGuard aReadLock;
+ css::uno::Reference< css::frame::XTitle > xTitle(m_xTitleHelper, css::uno::UNO_SET_THROW);
+ aReadLock.clear();
+ // <- SAFE
+
+ return xTitle->getTitle();
+}
+
+void SAL_CALL XFrameImpl::setTitle( const OUString& sTitle )
+{
+ checkDisposed();
+
+ // SAFE ->
+ SolarMutexClearableGuard aReadLock;
+ css::uno::Reference< css::frame::XTitle > xTitle(m_xTitleHelper, css::uno::UNO_SET_THROW);
+ aReadLock.clear();
+ // <- SAFE
+
+ xTitle->setTitle(sTitle);
+}
+
+void SAL_CALL XFrameImpl::addTitleChangeListener( const css::uno::Reference< css::frame::XTitleChangeListener >& xListener)
+{
+ checkDisposed();
+
+ // SAFE ->
+ SolarMutexClearableGuard aReadLock;
+ css::uno::Reference< css::frame::XTitleChangeBroadcaster > xTitle(m_xTitleHelper, css::uno::UNO_QUERY_THROW);
+ aReadLock.clear();
+ // <- SAFE
+
+ xTitle->addTitleChangeListener(xListener);
+}
+
+void SAL_CALL XFrameImpl::removeTitleChangeListener( const css::uno::Reference< css::frame::XTitleChangeListener >& xListener )
+{
+ checkDisposed();
+
+ // SAFE ->
+ SolarMutexClearableGuard aReadLock;
+ css::uno::Reference< css::frame::XTitleChangeBroadcaster > xTitle(m_xTitleHelper, css::uno::UNO_QUERY_THROW);
+ aReadLock.clear();
+ // <- SAFE
+
+ xTitle->removeTitleChangeListener(xListener);
+}
+
+css::uno::Reference<css::container::XNameContainer> SAL_CALL XFrameImpl::getUserDefinedAttributes()
+{
+ // optional attribute
+ return nullptr;
+}
+
+css::uno::Reference<css::frame::XDispatchRecorderSupplier> SAL_CALL XFrameImpl::getDispatchRecorderSupplier()
+{
+ SolarMutexGuard g;
+ return m_xDispatchRecorderSupplier;
+}
+
+void SAL_CALL XFrameImpl::setDispatchRecorderSupplier(const css::uno::Reference<css::frame::XDispatchRecorderSupplier>& p)
+{
+ checkDisposed();
+ SolarMutexGuard g;
+ m_xDispatchRecorderSupplier.set(p);
+}
+
+css::uno::Reference<css::uno::XInterface> SAL_CALL XFrameImpl::getLayoutManager()
+{
+ SolarMutexGuard g;
+ return m_xLayoutManager;
+}
+
+void SAL_CALL XFrameImpl::setLayoutManager(const css::uno::Reference<css::uno::XInterface>& p1)
+{
+ checkDisposed();
+ SolarMutexGuard g;
+
+ css::uno::Reference<css::frame::XLayoutManager2> xOldLayoutManager = m_xLayoutManager;
+ css::uno::Reference<css::frame::XLayoutManager2> xNewLayoutManager(p1, css::uno::UNO_QUERY);
+
+ if (xOldLayoutManager != xNewLayoutManager)
+ {
+ m_xLayoutManager = xNewLayoutManager;
+ if (xOldLayoutManager.is())
+ disableLayoutManager(xOldLayoutManager);
+ if (xNewLayoutManager.is() && !m_bDocHidden)
+ lcl_enableLayoutManager(xNewLayoutManager, this);
+ }
+}
+
+css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL XFrameImpl::getPropertySetInfo()
+{
+ checkDisposed();
+ return css::uno::Reference< css::beans::XPropertySetInfo >(this);
+}
+
+void SAL_CALL XFrameImpl::setPropertyValue(const OUString& sProperty,
+ const css::uno::Any& aValue )
+{
+ // TODO look for e.g. readonly props and reject setProp() call!
+
+ checkDisposed();
+
+ // SAFE ->
+ SolarMutexGuard g;
+
+ TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty);
+ if (pIt == m_lProps.end())
+ throw css::beans::UnknownPropertyException(sProperty);
+
+ css::beans::Property aPropInfo = pIt->second;
+
+ css::uno::Any aCurrentValue = impl_getPropertyValue(aPropInfo.Handle);
+
+ bool bWillBeChanged = (aCurrentValue != aValue);
+ if (! bWillBeChanged)
+ return;
+
+ css::beans::PropertyChangeEvent aEvent;
+ aEvent.PropertyName = aPropInfo.Name;
+ aEvent.Further = false;
+ aEvent.PropertyHandle = aPropInfo.Handle;
+ aEvent.OldValue = aCurrentValue;
+ aEvent.NewValue = aValue;
+ aEvent.Source.set(m_xBroadcaster.get(), css::uno::UNO_QUERY);
+
+ if (impl_existsVeto(aEvent))
+ throw css::beans::PropertyVetoException();
+
+ impl_setPropertyValue(aPropInfo.Handle, aValue);
+
+ impl_notifyChangeListener(aEvent);
+}
+
+css::uno::Any SAL_CALL XFrameImpl::getPropertyValue(const OUString& sProperty)
+{
+ checkDisposed();
+
+ // SAFE ->
+ SolarMutexGuard aReadLock;
+
+ TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty);
+ if (pIt == m_lProps.end())
+ throw css::beans::UnknownPropertyException(sProperty);
+
+ css::beans::Property aPropInfo = pIt->second;
+
+ return impl_getPropertyValue(aPropInfo.Handle);
+}
+
+void SAL_CALL XFrameImpl::addPropertyChangeListener(
+ const OUString& sProperty,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener)
+{
+ checkDisposed();
+
+ // SAFE ->
+ {
+ SolarMutexGuard aReadLock;
+
+ TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty);
+ if (pIt == m_lProps.end())
+ throw css::beans::UnknownPropertyException(sProperty);
+ }
+ // <- SAFE
+
+ m_lSimpleChangeListener.addInterface(sProperty, xListener);
+}
+
+void SAL_CALL XFrameImpl::removePropertyChangeListener(
+ const OUString& sProperty,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener)
+{
+ // SAFE ->
+ {
+ SolarMutexGuard aReadLock;
+
+ TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty);
+ if (pIt == m_lProps.end())
+ throw css::beans::UnknownPropertyException(sProperty);
+ }
+ // <- SAFE
+
+ m_lSimpleChangeListener.removeInterface(sProperty, xListener);
+}
+
+void SAL_CALL XFrameImpl::addVetoableChangeListener(
+ const OUString& sProperty,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener)
+{
+ checkDisposed();
+
+ // SAFE ->
+ {
+ SolarMutexGuard aReadLock;
+
+ TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty);
+ if (pIt == m_lProps.end())
+ throw css::beans::UnknownPropertyException(sProperty);
+ }
+ // <- SAFE
+
+ m_lVetoChangeListener.addInterface(sProperty, xListener);
+}
+
+void SAL_CALL XFrameImpl::removeVetoableChangeListener(
+ const OUString& sProperty,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener)
+{
+ // SAFE ->
+ {
+ SolarMutexGuard aReadLock;
+
+ TPropInfoHash::const_iterator pIt = m_lProps.find(sProperty);
+ if (pIt == m_lProps.end())
+ throw css::beans::UnknownPropertyException(sProperty);
+ }
+ // <- SAFE
+
+ m_lVetoChangeListener.removeInterface(sProperty, xListener);
+}
+
+css::uno::Sequence< css::beans::Property > SAL_CALL XFrameImpl::getProperties()
+{
+ checkDisposed();
+
+ SolarMutexGuard g;
+
+ sal_Int32 c = static_cast<sal_Int32>(m_lProps.size());
+ css::uno::Sequence< css::beans::Property > lProps(c);
+ auto lPropsRange = asNonConstRange(lProps);
+ for (auto const& elem : m_lProps)
+ {
+ lPropsRange[--c] = elem.second;
+ }
+
+ return lProps;
+}
+
+css::beans::Property SAL_CALL XFrameImpl::getPropertyByName(const OUString& sName)
+{
+ checkDisposed();
+
+ SolarMutexGuard g;
+
+ TPropInfoHash::const_iterator pIt = m_lProps.find(sName);
+ if (pIt == m_lProps.end())
+ throw css::beans::UnknownPropertyException(sName);
+
+ return pIt->second;
+}
+
+sal_Bool SAL_CALL XFrameImpl::hasPropertyByName(const OUString& sName)
+{
+ checkDisposed();
+
+ SolarMutexGuard g;
+
+ TPropInfoHash::iterator pIt = m_lProps.find(sName);
+ bool bExist = (pIt != m_lProps.end());
+
+ return bExist;
+}
+
+/*-****************************************************************************************************/
+void XFrameImpl::implts_forgetSubFrames()
+{
+ // SAFE ->
+ SolarMutexClearableGuard aReadLock;
+ css::uno::Reference< css::container::XIndexAccess > xContainer(m_xFramesHelper, css::uno::UNO_QUERY_THROW);
+ aReadLock.clear();
+ // <- SAFE
+
+ sal_Int32 c = xContainer->getCount();
+ sal_Int32 i = 0;
+
+ for (i=0; i<c; ++i)
+ {
+ try
+ {
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ xContainer->getByIndex(i) >>= xFrame;
+ if (xFrame.is())
+ xFrame->setCreator(css::uno::Reference< css::frame::XFramesSupplier >());
+ }
+ catch(const css::uno::Exception&)
+ {
+ // Ignore errors here.
+ // Nobody can guarantee a stable index in multi threaded environments .-)
+ }
+ }
+
+ SolarMutexGuard g;
+ m_xFramesHelper.clear(); // clear uno reference
+ m_aChildFrameContainer.clear(); // clear container content
+}
+
+/*-****************************************************************************************************
+ @short destroy instance
+ @descr The owner of this object calls the dispose method if the object
+ should be destroyed. All other objects and components, that are registered
+ as an EventListener are forced to release their references to this object.
+ Furthermore this frame is removed from its parent frame container to release
+ this reference. The reference attributes are disposed and released also.
+
+ @attention Look for globale description at beginning of file too!
+ (DisposedException, FairRWLock ..., initialize, dispose)
+
+ @seealso method initialize()
+ @seealso baseclass FairRWLockBase!
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::disposing()
+{
+ // We should hold a reference to ourself ...
+ // because our owner dispose us and release our reference ...
+ // May be we will die before we could finish this method ...
+ css::uno::Reference< css::frame::XFrame > xThis(this);
+
+ SAL_INFO("fwk.frame", "[Frame] " << m_sName << " send dispose event to listener");
+
+ // First operation should be... "stop all listening for window events on our container window".
+ // These events are superfluous but can make trouble!
+ // We will die, die and die...
+ implts_stopWindowListening();
+
+ css::uno::Reference<css::frame::XLayoutManager2> layoutMgr;
+ {
+ SolarMutexGuard g;
+ layoutMgr = m_xLayoutManager;
+ }
+ if (layoutMgr.is()) {
+ disableLayoutManager(layoutMgr);
+ }
+
+ std::unique_ptr<WindowCommandDispatch> disp;
+ {
+ SolarMutexGuard g;
+ std::swap(disp, m_pWindowCommandDispatch);
+ }
+ disp.reset();
+
+ // Send message to all listener and forget her references.
+ css::lang::EventObject aEvent( xThis );
+ m_aListenerContainer.disposeAndClear( aEvent );
+
+ // set "end of live" for our property set helper
+ impl_disablePropertySet();
+
+ // interception/dispatch chain must be destructed explicitly
+ // Otherwise some dispatches and/or interception objects won't die.
+ css::uno::Reference< css::lang::XEventListener > xDispatchHelper;
+ {
+ SolarMutexGuard g;
+ xDispatchHelper.set(m_xDispatchHelper, css::uno::UNO_QUERY_THROW);
+ }
+ xDispatchHelper->disposing(aEvent);
+ xDispatchHelper.clear();
+
+ // Don't show any dialogs, errors or something else any more!
+ // If somewhere called dispose() without close() before - normally no dialogs
+ // should exist. Otherwise it's the problem of the outside caller.
+ // Note:
+ // (a) Do it after stopWindowListening(). May that force some active/deactivate
+ // notifications which we don't need here really.
+ // (b) Don't forget to save the old value of IsDialogCancelEnabled() to
+ // restore it afterwards (to not kill headless mode).
+ DialogCancelMode old = Application::GetDialogCancelMode();
+ Application::SetDialogCancelMode( DialogCancelMode::Silent );
+
+ // We should be alone for ever and further dispose calls are rejected by lines before ...
+ // I hope it :-)
+
+ // Free references of our frame tree.
+ // Force parent container to forget this frame too ...
+ // ( It's contained in m_xParent and so no css::lang::XEventListener for m_xParent! )
+ // It's important to do that before we free some other internal structures.
+ // Because if our parent gets an activate and found us as last possible active frame
+ // he try to deactivate us ... and we run into some trouble (DisposedExceptions!).
+ css::uno::Reference<css::frame::XFramesSupplier> parent;
+ {
+ SolarMutexGuard g;
+ std::swap(parent, m_xParent);
+ }
+ if( parent.is() )
+ {
+ parent->getFrames()->remove( xThis );
+ }
+
+ /* } SAFE */
+ // Forget our internal component and her window first.
+ // So we can release our container window later without problems.
+ // Because this container window is the parent of the component window ...
+ // Note: Dispose it hard - because suspending must be done inside close() call!
+ // But try to dispose the controller first before you destroy the window.
+ // Because the window is used by the controller too ...
+ css::uno::Reference< css::lang::XComponent > xDisposableCtrl;
+ css::uno::Reference< css::lang::XComponent > xDisposableComp;
+ {
+ SolarMutexGuard g;
+ xDisposableCtrl = m_xController;
+ xDisposableComp = m_xComponentWindow;
+ }
+ if (xDisposableCtrl.is())
+ xDisposableCtrl->dispose();
+ if (xDisposableComp.is())
+ xDisposableComp->dispose();
+
+ impl_checkMenuCloser();
+
+ css::uno::Reference<css::awt::XWindow> contWin;
+ {
+ SolarMutexGuard g;
+ std::swap(contWin, m_xContainerWindow);
+ }
+ if( contWin.is() )
+ {
+ contWin->setVisible( false );
+ // All VclComponents are XComponents; so call dispose before discarding
+ // a css::uno::Reference< XVclComponent >, because this frame is the owner of the window
+ contWin->dispose();
+ }
+
+ /*ATTENTION
+ Clear container after successful removing from parent container ...
+ because our parent could be the desktop which stand in dispose too!
+ If we have already cleared our own container we lost our child before this could be
+ remove himself at this instance ...
+ Release m_xFramesHelper after that ... it's the same problem between parent and child!
+ "m_xParent->getFrames()->remove( xThis );" needs this helper ...
+ Otherwise we get a null reference and could finish removing successfully.
+ => You see: Order of calling operations is important!!!
+ */
+ implts_forgetSubFrames();
+
+ {
+ SolarMutexGuard g;
+
+ // Release some other references.
+ // This calls should be easy ... I hope it :-)
+ m_xDispatchHelper.clear();
+ m_xDropTargetListener.clear();
+ m_xDispatchRecorderSupplier.clear();
+ m_xLayoutManager.clear();
+ m_xIndicatorFactoryHelper.clear();
+
+ // It's important to set default values here!
+ // If may be later somewhere change the disposed-behaviour of this implementation
+ // and doesn't throw any DisposedExceptions we must guarantee best matching default values ...
+ m_eActiveState = E_INACTIVE;
+ m_sName.clear();
+ m_bIsFrameTop = false;
+ m_bConnected = false;
+ m_nExternalLockCount = 0;
+ m_bSelfClose = false;
+ m_bIsHidden = true;
+ }
+
+ // Don't forget it restore old value -
+ // otherwise no dialogs can be shown anymore in other frames.
+ Application::SetDialogCancelMode( old );
+}
+
+/*-****************************************************************************************************
+ @short Be a listener for dispose events!
+ @descr Adds/remove an EventListener to this object. If the dispose method is called on
+ this object, the disposing method of the listener is called.
+ @param "xListener" reference to your listener object.
+ @onerror Listener is ignored.
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener )
+{
+ checkDisposed();
+ m_aListenerContainer.addInterface( cppu::UnoType<css::lang::XEventListener>::get(), xListener );
+}
+
+void SAL_CALL XFrameImpl::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener )
+{
+ m_aListenerContainer.removeInterface( cppu::UnoType<css::lang::XEventListener>::get(), xListener );
+}
+
+/*-****************************************************************************************************
+ @short create new status indicator
+ @descr Use returned status indicator to show progresses and some text information.
+ All created objects share the same dialog! Only the last one can show his information.
+
+ @seealso class StatusIndicatorFactory
+ @seealso class StatusIndicator
+ @return A reference to created object.
+
+ @onerror We return a null reference.
+*//*-*****************************************************************************************************/
+css::uno::Reference< css::task::XStatusIndicator > SAL_CALL XFrameImpl::createStatusIndicator()
+{
+ checkDisposed();
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexClearableGuard aReadLock;
+
+ // Make snapshot of necessary member and define default return value.
+ css::uno::Reference< css::task::XStatusIndicator > xExternal(m_xIndicatorInterception.get(), css::uno::UNO_QUERY);
+ css::uno::Reference< css::task::XStatusIndicatorFactory > xFactory = m_xIndicatorFactoryHelper;
+
+ aReadLock.clear();
+ /* UNSAFE AREA ----------------------------------------------------------------------------------------- */
+
+ // Was set from outside to intercept any progress activities!
+ if (xExternal.is())
+ return xExternal;
+
+ // Or use our own factory as fallback, to create such progress.
+ if (xFactory.is())
+ return xFactory->createStatusIndicator();
+
+ return css::uno::Reference< css::task::XStatusIndicator >();
+}
+
+/*-****************************************************************************************************
+ @short search for target to load URL
+ @descr This method searches for a dispatch for the specified DispatchDescriptor.
+ The FrameSearchFlags and the FrameName of the DispatchDescriptor are
+ treated as described for findFrame.
+
+ @seealso method findFrame()
+ @seealso method queryDispatches()
+ @seealso method set/getName()
+ @seealso class TargetFinder
+
+ @param "aURL" , URL for loading
+ @param "sTargetFrameName" , name of target frame
+ @param "nSearchFlags" , additional flags to regulate search if sTargetFrameName is not clear
+ @return css::uno::Reference to dispatch handler.
+
+ @onerror A null reference is returned.
+*//*-*****************************************************************************************************/
+css::uno::Reference< css::frame::XDispatch > SAL_CALL XFrameImpl::queryDispatch( const css::util::URL& aURL,
+ const OUString& sTargetFrameName,
+ sal_Int32 nSearchFlags)
+{
+ // Don't check incoming parameter here! Our helper do it for us and it is not a good idea to do it more than once!
+
+ checkDisposed();
+
+ // Remove uno and cmd protocol part as we want to support both of them. We store only the command part
+ // in our hash map. All other protocols are stored with the protocol part.
+ OUString aCommand( aURL.Main );
+ if ( aURL.Protocol.equalsIgnoreAsciiCase(".uno:") )
+ aCommand = aURL.Path;
+
+ // Make std::unordered_map lookup if the current URL is in the disabled list
+ if ( m_aCommandOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, aCommand ) )
+ return css::uno::Reference< css::frame::XDispatch >();
+ else
+ {
+ // We use a helper to support these interface and an interceptor mechanism.
+ css::uno::Reference<css::frame::XDispatchProvider> disp;
+ {
+ SolarMutexGuard g;
+ disp = m_xDispatchHelper;
+ }
+ if (!disp.is()) {
+ throw css::lang::DisposedException("Frame disposed");
+ }
+ return disp->queryDispatch( aURL, sTargetFrameName, nSearchFlags );
+ }
+}
+
+/*-****************************************************************************************************
+ @short handle more than ones dispatches at same call
+ @descr Returns a sequence of dispatches. For details see the queryDispatch method.
+ For failed dispatches we return empty items in list!
+
+ @seealso method queryDispatch()
+
+ @param "lDescriptor" list of dispatch arguments for queryDispatch()!
+ @return List of dispatch references. Some elements can be NULL!
+
+ @onerror An empty list is returned.
+*//*-*****************************************************************************************************/
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL XFrameImpl::queryDispatches(
+ const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor )
+{
+ // Don't check incoming parameter here! Our helper do it for us and it is not a good idea to do it more than ones!
+
+ checkDisposed();
+
+ // We use a helper to support these interface and an interceptor mechanism.
+ css::uno::Reference<css::frame::XDispatchProvider> disp;
+ {
+ SolarMutexGuard g;
+ disp = m_xDispatchHelper;
+ }
+ if (!disp.is()) {
+ throw css::lang::DisposedException("Frame disposed");
+ }
+ return disp->queryDispatches( lDescriptor );
+}
+
+/*-****************************************************************************************************
+ @short register/unregister interceptor for dispatch calls
+ @descr If you wish to handle some dispatches by himself ... you should be
+ an interceptor for it. Please see class OInterceptionHelper for further information.
+
+ @seealso class OInterceptionHelper
+
+ @param "xInterceptor", reference to your interceptor implementation.
+ @onerror Interceptor is ignored.
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::registerDispatchProviderInterceptor(
+ const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor )
+{
+ // We use a helper to support these interface and an interceptor mechanism.
+ // This helper is threadsafe himself and check incoming parameter too.
+ // I think we don't need any lock here!
+
+ checkDisposed();
+
+ css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper;
+ {
+ SolarMutexGuard g;
+ xInterceptionHelper.set( m_xDispatchHelper, css::uno::UNO_QUERY );
+ }
+ if (xInterceptionHelper.is()) {
+ xInterceptionHelper->registerDispatchProviderInterceptor( xInterceptor );
+ }
+}
+
+void SAL_CALL XFrameImpl::releaseDispatchProviderInterceptor(
+ const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor )
+{
+ // We use a helper to support these interface and an interceptor mechanism.
+ // This helper is threadsafe himself and check incoming parameter too.
+ // I think we don't need any lock here!
+
+ // Sometimes we are called during our dispose() method
+
+ css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper;
+ {
+ SolarMutexGuard g;
+ xInterceptionHelper.set( m_xDispatchHelper, css::uno::UNO_QUERY );
+ }
+ if (xInterceptionHelper.is()) {
+ xInterceptionHelper->releaseDispatchProviderInterceptor( xInterceptor );
+ }
+}
+
+/*-****************************************************************************************************
+ @short provides information about all possible dispatch functions
+ inside the current frame environment
+*//*-*****************************************************************************************************/
+css::uno::Sequence< sal_Int16 > SAL_CALL XFrameImpl::getSupportedCommandGroups()
+{
+ return m_xDispatchInfoHelper->getSupportedCommandGroups();
+}
+
+css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL XFrameImpl::getConfigurableDispatchInformation(
+ sal_Int16 nCommandGroup)
+{
+ return m_xDispatchInfoHelper->getConfigurableDispatchInformation(nCommandGroup);
+}
+
+/*-****************************************************************************************************
+ @short notifications for window events
+ @descr We are a listener on our container window to forward it to our component window.
+
+ @seealso method setComponent()
+ @seealso member m_xContainerWindow
+ @seealso member m_xComponentWindow
+
+ @param "aEvent" describe source of detected event
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::windowResized( const css::awt::WindowEvent& )
+{
+ // Part of dispose-mechanism
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ // Impl-method is threadsafe!
+ // If we have a current component window - we must resize it!
+ implts_resizeComponentWindow();
+}
+
+void SAL_CALL XFrameImpl::focusGained( const css::awt::FocusEvent& )
+{
+ // Part of dispose() mechanism
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexClearableGuard aReadLock;
+ // Make snapshot of member!
+ css::uno::Reference< css::awt::XWindow > xComponentWindow = m_xComponentWindow;
+ aReadLock.clear();
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+
+ if( xComponentWindow.is() )
+ {
+ xComponentWindow->setFocus();
+ }
+}
+
+/*-****************************************************************************************************
+ @short notifications for window events
+ @descr We are a listener on our container window to forward it to our component window ...
+ but a XTopWindowListener we are only if we are a top frame!
+
+ @seealso method setComponent()
+ @seealso member m_xContainerWindow
+ @seealso member m_xComponentWindow
+
+ @param "aEvent" describe source of detected event
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::windowActivated( const css::lang::EventObject& )
+{
+ checkDisposed();
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexClearableGuard aReadLock;
+ // Make snapshot of member!
+ EActiveState eState = m_eActiveState;
+ aReadLock.clear();
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+ // Activate the new active path from here to top.
+ if( eState == E_INACTIVE )
+ {
+ setActiveFrame( css::uno::Reference< css::frame::XFrame >() );
+ activate();
+ }
+}
+
+void SAL_CALL XFrameImpl::windowDeactivated( const css::lang::EventObject& )
+{
+ // Sometimes called during dispose()
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexClearableGuard aReadLock;
+
+ css::uno::Reference< css::frame::XFrame > xParent = m_xParent;
+ css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow;
+ EActiveState eActiveState = m_eActiveState;
+
+ aReadLock.clear();
+
+ if( eActiveState == E_INACTIVE )
+ return;
+
+ // Deactivation is always done implicitly by activation of another frame.
+ // Only if no activation is done, deactivations have to be processed if the activated window
+ // is a parent window of the last active Window!
+ SolarMutexClearableGuard aSolarGuard;
+ vcl::Window* pFocusWindow = Application::GetFocusWindow();
+ if ( !xContainerWindow.is() || !xParent.is() ||
+ css::uno::Reference< css::frame::XDesktop >( xParent, css::uno::UNO_QUERY ).is()
+ )
+ return;
+
+ css::uno::Reference< css::awt::XWindow > xParentWindow = xParent->getContainerWindow();
+ VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow( xParentWindow );
+ //#i70261#: dialogs opened from an OLE object will cause a deactivate on the frame of the OLE object
+ // on Solaris/Linux at that time pFocusWindow is still NULL because the focus handling is different; right after
+ // the deactivation the focus will be set into the dialog!
+ // currently I see no case where a sub frame could get a deactivate with pFocusWindow being NULL permanently
+ // so for now this case is omitted from handled deactivations
+ if( pFocusWindow && pParentWindow->IsChild( pFocusWindow ) )
+ {
+ css::uno::Reference< css::frame::XFramesSupplier > xSupplier( xParent, css::uno::UNO_QUERY );
+ if( xSupplier.is() )
+ {
+ aSolarGuard.clear();
+ xSupplier->setActiveFrame( css::uno::Reference< css::frame::XFrame >() );
+ }
+ }
+}
+
+void SAL_CALL XFrameImpl::windowClosing( const css::lang::EventObject& )
+{
+ checkDisposed();
+
+ // deactivate this frame ...
+ deactivate();
+
+ // ... and try to close it
+ // But do it asynchronous inside the main thread.
+ // VCL has no fun to do such things outside his main thread :-(
+ // Note: The used dispatch make it asynchronous for us .-)
+
+ /*ATTENTION!
+ Don't try to suspend the controller here! Because it's done inside used dispatch().
+ Otherwise the dialog "would you save your changes?" will be shown more than once ...
+ */
+
+ css::util::URL aURL;
+ aURL.Complete = ".uno:CloseFrame";
+ css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
+ xParser->parseStrict(aURL);
+
+ css::uno::Reference< css::frame::XDispatch > xCloser = queryDispatch(aURL, SPECIALTARGET_SELF, 0);
+ if (xCloser.is())
+ xCloser->dispatch(aURL, css::uno::Sequence< css::beans::PropertyValue >());
+
+ // Attention: If this dispatch works synchronous ... and fulfill its job ...
+ // this line of code will never be reached ...
+ // Or if it will be reached it will be for sure that all your member are gone .-)
+}
+
+/*-****************************************************************************************************
+ @short react for a show event for the internal container window
+ @descr Normally we don't need this information really. But we can use it to
+ implement the special feature "trigger first visible task".
+
+ Algorithm: - first we have to check if we are a top (task) frame
+ It's not enough to be a top frame! Because we MUST have the desktop as parent.
+ But frames without a parent are top too. So it's not possible to check isTop() here!
+ We have to look for the type of our parent.
+ - if we are a task frame, then we have to check if we are the first one.
+ We use a static variable to do so. They will be reset to afterwards be sure
+ that further calls of this method doesn't do anything then.
+ - Then we have to trigger the right event string on the global job executor.
+
+ @seealso css::task::JobExecutor
+
+ @param aEvent
+ describes the source of this event
+ We are not interested on this information. We are interested on the visible state only.
+
+ @threadsafe yes
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::windowShown( const css::lang::EventObject& )
+{
+ static std::mutex aFirstVisibleLock;
+
+ /* SAFE { */
+ SolarMutexClearableGuard aReadLock;
+ css::uno::Reference< css::frame::XDesktop > xDesktopCheck( m_xParent, css::uno::UNO_QUERY );
+ m_bIsHidden = false;
+ aReadLock.clear();
+ /* } SAFE */
+
+ impl_checkMenuCloser();
+
+ if (!xDesktopCheck.is())
+ return;
+
+ static bool bFirstVisibleTask = true;
+ std::unique_lock aGuard(aFirstVisibleLock);
+ bool bMustBeTriggered = bFirstVisibleTask;
+ bFirstVisibleTask = false;
+ aGuard.unlock();
+
+ if (bMustBeTriggered)
+ {
+ css::uno::Reference< css::task::XJobExecutor > xExecutor
+ = css::task::theJobExecutor::get( m_xContext );
+ xExecutor->trigger( "onFirstVisibleTask" );
+ }
+}
+
+void SAL_CALL XFrameImpl::windowHidden( const css::lang::EventObject& )
+{
+ /* SAFE { */
+ {
+ SolarMutexGuard aReadLock;
+ m_bIsHidden = true;
+ }
+ /* } SAFE */
+
+ impl_checkMenuCloser();
+}
+
+/*-****************************************************************************************************
+ @short called by dispose of our windows!
+ @descr This object is forced to release all references to the interfaces given
+ by the parameter source. We are a listener at our container window and
+ should listen for his disposing.
+
+ @seealso XWindowListener
+ @seealso XTopWindowListener
+ @seealso XFocusListener
+*//*-*****************************************************************************************************/
+void SAL_CALL XFrameImpl::disposing( const css::lang::EventObject& aEvent )
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexResettableGuard aWriteLock;
+
+ if( aEvent.Source == m_xContainerWindow )
+ {
+ // NECESSARY: Impl-method is threadsafe by himself!
+ aWriteLock.clear();
+ implts_stopWindowListening();
+ aWriteLock.reset();
+ m_xContainerWindow.clear();
+ }
+}
+
+/*-************************************************************************************************************
+ @interface com.sun.star.document.XActionLockable
+ @short implement locking of frame/task from outside
+ @descr Sometimes we have problems to decide if closing of task is allowed. Because; frame/task
+ could be used for pending loading jobs. So you can lock this object from outside and
+ prevent instance against closing during using! But - don't do it in a wrong or expensive manner.
+ Otherwise task couldn't die anymore!!!
+
+ @seealso interface XActionLockable
+ @seealso method BaseDispatcher::implts_loadIt()
+ @seealso method Desktop::loadComponentFromURL()
+ @return true if frame/task is locked
+ false otherwise
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+sal_Bool SAL_CALL XFrameImpl::isActionLocked()
+{
+ SolarMutexGuard g;
+ return( m_nExternalLockCount!=0);
+}
+
+void SAL_CALL XFrameImpl::addActionLock()
+{
+ SolarMutexGuard g;
+ ++m_nExternalLockCount;
+}
+
+void SAL_CALL XFrameImpl::removeActionLock()
+{
+ {
+ SolarMutexGuard g;
+ SAL_WARN_IF( m_nExternalLockCount<=0, "fwk.frame", "XFrameImpl::removeActionLock(): Frame is not locked! "
+ "Possible multithreading problem detected." );
+ --m_nExternalLockCount;
+ }
+
+ implts_checkSuicide();
+}
+
+void SAL_CALL XFrameImpl::setActionLocks( sal_Int16 nLock )
+{
+ SolarMutexGuard g;
+ // Attention: If somewhere called resetActionLocks() before and get e.g. 5 locks ...
+ // and tried to set these 5 ones here after his operations ...
+ // we can't ignore set requests during these two calls!
+ // So we must add(!) these 5 locks here.
+ m_nExternalLockCount = m_nExternalLockCount + nLock;
+}
+
+sal_Int16 SAL_CALL XFrameImpl::resetActionLocks()
+{
+ sal_Int16 nCurrentLocks = 0;
+ {
+ SolarMutexGuard g;
+ nCurrentLocks = m_nExternalLockCount;
+ m_nExternalLockCount = 0;
+ }
+
+ // Attention:
+ // external lock count is 0 here every time... but if
+ // member m_bSelfClose is set to true too... we call our own close()/dispose().
+ // See close() for further information
+ implts_checkSuicide();
+
+ return nCurrentLocks;
+}
+
+void XFrameImpl::impl_setPropertyValue(sal_Int32 nHandle,
+ const css::uno::Any& aValue)
+
+{
+ /* There is no need to lock any mutex here. Because we share the
+ solar mutex with our base class. And we said to our base class: "don't release it on calling us" .-)
+ */
+
+ /* Attention: You can use nHandle only, if you are sure that all supported
+ properties has a unique handle. That must be guaranteed
+ inside method initListeners()!
+ */
+ switch (nHandle)
+ {
+ case FRAME_PROPHANDLE_TITLE :
+ {
+ OUString sExternalTitle;
+ aValue >>= sExternalTitle;
+ setTitle (sExternalTitle);
+ }
+ break;
+
+ case FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER :
+ aValue >>= m_xDispatchRecorderSupplier;
+ break;
+
+ case FRAME_PROPHANDLE_LAYOUTMANAGER :
+ {
+ css::uno::Reference< css::frame::XLayoutManager2 > xOldLayoutManager = m_xLayoutManager;
+ css::uno::Reference< css::frame::XLayoutManager2 > xNewLayoutManager;
+ aValue >>= xNewLayoutManager;
+
+ if (xOldLayoutManager != xNewLayoutManager)
+ {
+ m_xLayoutManager = xNewLayoutManager;
+ if (xOldLayoutManager.is())
+ disableLayoutManager(xOldLayoutManager);
+ if (xNewLayoutManager.is() && !m_bDocHidden)
+ lcl_enableLayoutManager(xNewLayoutManager, this);
+ }
+ }
+ break;
+
+ case FRAME_PROPHANDLE_INDICATORINTERCEPTION :
+ {
+ css::uno::Reference< css::task::XStatusIndicator > xProgress;
+ aValue >>= xProgress;
+ m_xIndicatorInterception = xProgress;
+ }
+ break;
+
+ case FRAME_PROPHANDLE_URL:
+ {
+ aValue >>= m_aURL;
+ }
+ break;
+ default :
+ SAL_INFO("fwk.frame", "XFrameImpl::setFastPropertyValue_NoBroadcast(): Invalid handle detected!" );
+ break;
+ }
+}
+
+css::uno::Any XFrameImpl::impl_getPropertyValue(sal_Int32 nHandle)
+{
+ /* There is no need to lock any mutex here. Because we share the
+ solar mutex with our base class. And we said to our base class: "don't release it on calling us" .-)
+ */
+
+ /* Attention: You can use nHandle only, if you are sure that all supported
+ properties has a unique handle. That must be guaranteed
+ inside method initListeners()!
+ */
+ css::uno::Any aValue;
+ switch (nHandle)
+ {
+ case FRAME_PROPHANDLE_TITLE :
+ aValue <<= getTitle ();
+ break;
+
+ case FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER :
+ aValue <<= m_xDispatchRecorderSupplier;
+ break;
+
+ case FRAME_PROPHANDLE_ISHIDDEN :
+ aValue <<= m_bIsHidden;
+ break;
+
+ case FRAME_PROPHANDLE_LAYOUTMANAGER :
+ aValue <<= m_xLayoutManager;
+ break;
+
+ case FRAME_PROPHANDLE_INDICATORINTERCEPTION :
+ {
+ css::uno::Reference< css::task::XStatusIndicator > xProgress(m_xIndicatorInterception.get(),
+ css::uno::UNO_QUERY);
+ aValue <<= xProgress;
+ }
+ break;
+
+ case FRAME_PROPHANDLE_URL:
+ {
+ aValue <<= m_aURL;
+ }
+ break;
+ default :
+ SAL_INFO("fwk.frame", "XFrameImpl::getFastPropertyValue(): Invalid handle detected!" );
+ break;
+ }
+
+ return aValue;
+}
+
+void XFrameImpl::impl_setPropertyChangeBroadcaster(const css::uno::Reference< css::uno::XInterface >& xBroadcaster)
+{
+ SolarMutexGuard g;
+ m_xBroadcaster = xBroadcaster;
+}
+
+void XFrameImpl::impl_addPropertyInfo(const css::beans::Property& aProperty)
+{
+ SolarMutexGuard g;
+
+ TPropInfoHash::const_iterator pIt = m_lProps.find(aProperty.Name);
+ if (pIt != m_lProps.end())
+ throw css::beans::PropertyExistException();
+
+ m_lProps[aProperty.Name] = aProperty;
+}
+
+void XFrameImpl::impl_disablePropertySet()
+{
+ SolarMutexGuard g;
+
+ css::uno::Reference< css::uno::XInterface > xThis(static_cast< css::beans::XPropertySet* >(this), css::uno::UNO_QUERY);
+ css::lang::EventObject aEvent(xThis);
+
+ m_lSimpleChangeListener.disposeAndClear(aEvent);
+ m_lVetoChangeListener.disposeAndClear(aEvent);
+ m_lProps.clear();
+}
+
+bool XFrameImpl::impl_existsVeto(const css::beans::PropertyChangeEvent& aEvent)
+{
+ /* Don't use the lock here!
+ The used helper is threadsafe and it lives for the whole lifetime of
+ our own object.
+ */
+ ::comphelper::OInterfaceContainerHelper3<css::beans::XVetoableChangeListener>* pVetoListener = m_lVetoChangeListener.getContainer(aEvent.PropertyName);
+ if (! pVetoListener)
+ return false;
+
+ ::comphelper::OInterfaceIteratorHelper3 pListener(*pVetoListener);
+ while (pListener.hasMoreElements())
+ {
+ try
+ {
+ pListener.next()->vetoableChange(aEvent);
+ }
+ catch(const css::uno::RuntimeException&)
+ { pListener.remove(); }
+ catch(const css::beans::PropertyVetoException&)
+ { return true; }
+ }
+
+ return false;
+}
+
+void XFrameImpl::impl_notifyChangeListener(const css::beans::PropertyChangeEvent& aEvent)
+{
+ /* Don't use the lock here!
+ The used helper is threadsafe and it lives for the whole lifetime of
+ our own object.
+ */
+ ::comphelper::OInterfaceContainerHelper3<css::beans::XPropertyChangeListener>* pSimpleListener = m_lSimpleChangeListener.getContainer(aEvent.PropertyName);
+ if (! pSimpleListener)
+ return;
+
+ ::comphelper::OInterfaceIteratorHelper3 pListener(*pSimpleListener);
+ while (pListener.hasMoreElements())
+ {
+ try
+ {
+ pListener.next()->propertyChange(aEvent);
+ }
+ catch(const css::uno::RuntimeException&)
+ { pListener.remove(); }
+ }
+}
+
+/*-****************************************************************************************************
+ @short send frame action event to our listener
+ @descr This method is threadsafe AND can be called by our dispose method too!
+ @param "aAction", describe the event for sending
+*//*-*****************************************************************************************************/
+void XFrameImpl::implts_sendFrameActionEvent( const css::frame::FrameAction& aAction )
+{
+ // Sometimes used by dispose()
+
+ // Log information about order of events to file!
+ // (only activated in debug version!)
+ SAL_INFO( "fwk.frame",
+ "[Frame] " << m_sName << " send event " <<
+ (aAction == css::frame::FrameAction_COMPONENT_ATTACHED ? OUString("COMPONENT ATTACHED") :
+ (aAction == css::frame::FrameAction_COMPONENT_DETACHING ? OUString("COMPONENT DETACHING") :
+ (aAction == css::frame::FrameAction_COMPONENT_REATTACHED ? OUString("COMPONENT REATTACHED") :
+ (aAction == css::frame::FrameAction_FRAME_ACTIVATED ? OUString("FRAME ACTIVATED") :
+ (aAction == css::frame::FrameAction_FRAME_DEACTIVATING ? OUString("FRAME DEACTIVATING") :
+ (aAction == css::frame::FrameAction_CONTEXT_CHANGED ? OUString("CONTEXT CHANGED") :
+ (aAction == css::frame::FrameAction_FRAME_UI_ACTIVATED ? OUString("FRAME UI ACTIVATED") :
+ (aAction == css::frame::FrameAction_FRAME_UI_DEACTIVATING ? OUString("FRAME UI DEACTIVATING") :
+ (aAction == css::frame::FrameAction::FrameAction_MAKE_FIXED_SIZE ? OUString("MAKE_FIXED_SIZE") :
+ OUString("*invalid*")))))))))));
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ // Send css::frame::FrameAction event to all listener.
+ // Get container for right listener.
+ // FOLLOW LINES ARE THREADSAFE!!!
+ // ( OInterfaceContainerHelper2 is synchronized with m_aListenerContainer! )
+ comphelper::OInterfaceContainerHelper2* pContainer = m_aListenerContainer.getContainer(
+ cppu::UnoType<css::frame::XFrameActionListener>::get());
+
+ if( pContainer == nullptr )
+ return;
+
+ // Build action event.
+ css::frame::FrameActionEvent aFrameActionEvent( static_cast< ::cppu::OWeakObject* >(this), this, aAction );
+
+ // Get iterator for access to listener.
+ comphelper::OInterfaceIteratorHelper2 aIterator( *pContainer );
+ // Send message to all listener.
+ while( aIterator.hasMoreElements() )
+ {
+ try
+ {
+ static_cast<css::frame::XFrameActionListener*>(aIterator.next())->frameAction( aFrameActionEvent );
+ }
+ catch( const css::uno::RuntimeException& )
+ {
+ aIterator.remove();
+ }
+ }
+}
+
+/*-****************************************************************************************************
+ @short helper to resize our component window
+ @descr A frame contains 2 windows - a container ~ and a component window.
+ This method resize inner component window to full size of outer container window.
+ This method is threadsafe AND can be called by our dispose method too!
+*//*-*****************************************************************************************************/
+void XFrameImpl::implts_resizeComponentWindow()
+{
+ // usually the LayoutManager does the resizing
+ // in case there is no LayoutManager resizing has to be done here
+ if ( m_xLayoutManager.is() )
+ return;
+
+ css::uno::Reference< css::awt::XWindow > xComponentWindow( getComponentWindow() );
+ if( !xComponentWindow.is() )
+ return;
+
+ css::uno::Reference< css::awt::XDevice > xDevice( getContainerWindow(), css::uno::UNO_QUERY );
+
+ // Convert relative size to output size.
+ css::awt::Rectangle aRectangle = getContainerWindow()->getPosSize();
+ css::awt::DeviceInfo aInfo = xDevice->getInfo();
+ css::awt::Size aSize( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset,
+ aRectangle.Height - aInfo.TopInset - aInfo.BottomInset );
+
+ // Resize our component window.
+ xComponentWindow->setPosSize( 0, 0, aSize.Width, aSize.Height, css::awt::PosSize::POSSIZE );
+}
+
+/*-****************************************************************************************************
+ @short helper to set icon on our container window (if it is a system window!)
+ @descr We use our internal set controller (if it exist) to specify which factory he represented.
+ This information can be used to find right icon. But our controller can say it us directly
+ too ... we should ask his optional property set first ...
+
+ @seealso method Window::SetIcon()
+ @onerror We do nothing.
+*//*-*****************************************************************************************************/
+void XFrameImpl::implts_setIconOnWindow()
+{
+ checkDisposed();
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ // Make snapshot of necessary members and release lock.
+ SolarMutexClearableGuard aReadLock;
+ css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow;
+ css::uno::Reference< css::frame::XController > xController = m_xController;
+ aReadLock.clear();
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+
+ if( !(xContainerWindow.is() && xController.is()) )
+ return;
+
+
+ // a) set default value to an invalid one. So we can start further searches for right icon id, if
+ // first steps failed!
+ // We must reset it to any fallback value - if no search step returns a valid result.
+ sal_Int32 nIcon = -1;
+
+ // b) try to find information on controller propertyset directly
+ // Don't forget to catch possible exceptions - because these property is an optional one!
+ css::uno::Reference< css::beans::XPropertySet > xSet( xController, css::uno::UNO_QUERY );
+ if( xSet.is() )
+ {
+ try
+ {
+ css::uno::Reference< css::beans::XPropertySetInfo > const xPSI( xSet->getPropertySetInfo(),
+ css::uno::UNO_SET_THROW );
+ if ( xPSI->hasPropertyByName( "IconId" ) )
+ xSet->getPropertyValue( "IconId" ) >>= nIcon;
+ }
+ catch( css::uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ }
+
+ // c) if b) failed... analyze argument list of currently loaded document inside the frame to find the filter.
+ // He can be used to detect right factory - and these can be used to match factory to icon...
+ if( nIcon == -1 )
+ {
+ css::uno::Reference< css::frame::XModel > xModel = xController->getModel();
+ if( xModel.is() )
+ {
+ SvtModuleOptions::EFactory eFactory = SvtModuleOptions::ClassifyFactoryByModel(xModel);
+ if (eFactory != SvtModuleOptions::EFactory::UNKNOWN_FACTORY)
+ nIcon = SvtModuleOptions().GetFactoryIcon( eFactory );
+ }
+ }
+
+ // d) if all steps failed - use fallback!
+ if( nIcon == -1 )
+ {
+ nIcon = 0;
+ }
+
+ // e) set icon on container window now
+ // Don't forget SolarMutex! We use vcl directly :-(
+ // Check window pointer for right WorkWindow class too!!!
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ {
+ SolarMutexGuard aSolarGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xContainerWindow );
+ if(
+ ( pWindow != nullptr ) &&
+ ( pWindow->GetType() == WindowType::WORKWINDOW )
+ )
+ {
+ WorkWindow* pWorkWindow = static_cast<WorkWindow*>(pWindow.get());
+ pWorkWindow->SetIcon( static_cast<sal_uInt16>(nIcon) );
+ }
+ }
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+}
+
+/*-************************************************************************************************************
+ @short helper to start/stop listening for window events on container window
+ @descr If we get a new container window, we must set it on internal member ...
+ and stop listening at old one ... and start listening on new one!
+ But sometimes (in dispose() call!) it's necessary to stop listening without starting
+ on new connections. So we split this functionality to make it easier at use.
+
+ @seealso method initialize()
+ @seealso method dispose()
+ @onerror We do nothing!
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+void XFrameImpl::implts_startWindowListening()
+{
+ checkDisposed();
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ // Make snapshot of necessary member!
+ SolarMutexClearableGuard aReadLock;
+ css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow;
+ css::uno::Reference< css::datatransfer::dnd::XDropTargetListener > xDragDropListener = m_xDropTargetListener;
+ css::uno::Reference< css::awt::XWindowListener > xWindowListener(this);
+ css::uno::Reference< css::awt::XFocusListener > xFocusListener(this);
+ css::uno::Reference< css::awt::XTopWindowListener > xTopWindowListener(this);
+ aReadLock.clear();
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+
+ if( !xContainerWindow.is() )
+ return;
+
+ xContainerWindow->addWindowListener( xWindowListener);
+ xContainerWindow->addFocusListener ( xFocusListener );
+
+ css::uno::Reference< css::awt::XTopWindow > xTopWindow( xContainerWindow, css::uno::UNO_QUERY );
+ if( xTopWindow.is() )
+ {
+ xTopWindow->addTopWindowListener( xTopWindowListener );
+
+ css::uno::Reference< css::awt::XToolkit2 > xToolkit = css::awt::Toolkit::create( m_xContext );
+ css::uno::Reference< css::datatransfer::dnd::XDropTarget > xDropTarget = xToolkit->getDropTarget( xContainerWindow );
+ if( xDropTarget.is() )
+ {
+ xDropTarget->addDropTargetListener( xDragDropListener );
+ xDropTarget->setActive( true );
+ }
+ }
+}
+
+void XFrameImpl::implts_stopWindowListening()
+{
+ // Sometimes used by dispose()
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ // Make snapshot of necessary member!
+ SolarMutexClearableGuard aReadLock;
+ css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow;
+ css::uno::Reference< css::datatransfer::dnd::XDropTargetListener > xDragDropListener = m_xDropTargetListener;
+ css::uno::Reference< css::awt::XWindowListener > xWindowListener(this);
+ css::uno::Reference< css::awt::XFocusListener > xFocusListener(this);
+ css::uno::Reference< css::awt::XTopWindowListener > xTopWindowListener(this);
+ aReadLock.clear();
+ /* UNSAFE AREA --------------------------------------------------------------------------------------------- */
+
+ if( !xContainerWindow.is() )
+ return;
+
+ xContainerWindow->removeWindowListener( xWindowListener);
+ xContainerWindow->removeFocusListener ( xFocusListener );
+
+ css::uno::Reference< css::awt::XTopWindow > xTopWindow( xContainerWindow, css::uno::UNO_QUERY );
+ if( !xTopWindow.is() )
+ return;
+
+ xTopWindow->removeTopWindowListener( xTopWindowListener );
+
+ css::uno::Reference< css::awt::XToolkit2 > xToolkit = css::awt::Toolkit::create( m_xContext );
+ css::uno::Reference< css::datatransfer::dnd::XDropTarget > xDropTarget =
+ xToolkit->getDropTarget( xContainerWindow );
+ if( xDropTarget.is() )
+ {
+ xDropTarget->removeDropTargetListener( xDragDropListener );
+ xDropTarget->setActive( false );
+ }
+}
+
+/*-****************************************************************************************************
+ @short helper to force broken close() request again
+ @descr If we self disagree with a close() request, and detect that all external locks are gone ...
+ then we must try to close this frame again.
+
+ @seealso XCloseable::close()
+ @seealso XFrameImpl::close()
+ @seealso XFrameImpl::removeActionLock()
+ @seealso XFrameImpl::resetActionLock()
+ @seealso m_bSelfClose
+ @seealso m_nExternalLockCount
+
+ @threadsafe yes
+*//*-*****************************************************************************************************/
+void XFrameImpl::implts_checkSuicide()
+{
+ /* SAFE */
+ SolarMutexClearableGuard aReadLock;
+ // in case of lock==0 and safed state of previous close() request m_bSelfClose
+ // we must force close() again. Because we had disagreed with that before.
+ bool bSuicide = (m_nExternalLockCount==0 && m_bSelfClose);
+ m_bSelfClose = false;
+ aReadLock.clear();
+ /* } SAFE */
+ // force close and deliver ownership to source of possible thrown veto exception
+ // Attention: Because this method is not designed to throw such exception we must suppress
+ // it for outside code!
+ try
+ {
+ if (bSuicide)
+ close(true);
+ }
+ catch(const css::util::CloseVetoException&)
+ {}
+ catch(const css::lang::DisposedException&)
+ {}
+}
+
+/** little helper to enable/disable the menu closer at the menubar of the given frame.
+
+ @param xFrame
+ we use its layout manager to set/reset a special callback.
+ Its existence regulate visibility of this closer item.
+
+ @param bState
+ <TRUE/> enable; <FALSE/> disable this state
+ */
+
+void XFrameImpl::impl_setCloser( /*IN*/ const css::uno::Reference< css::frame::XFrame2 >& xFrame ,
+ /*IN*/ bool bState )
+{
+ // Note: If start module is not installed - no closer has to be shown!
+ if (!SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE))
+ return;
+
+ try
+ {
+ css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ xFrameProps->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
+ css::uno::Reference< css::beans::XPropertySet > xLayoutProps(xLayoutManager, css::uno::UNO_QUERY_THROW);
+ xLayoutProps->setPropertyValue(LAYOUTMANAGER_PROPNAME_MENUBARCLOSER, css::uno::Any(bState));
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ {}
+}
+
+/** it checks, which of the top level task frames must have the special menu closer for
+ switching to the backing window mode.
+
+ It analyze the current list of visible top level frames. Only the last real document
+ frame can have this symbol. Not the help frame nor the backing task itself.
+ Here we do anything related to this closer. We remove it from the old frame and set it
+ for the new one.
+ */
+
+void XFrameImpl::impl_checkMenuCloser()
+{
+ /* SAFE { */
+ SolarMutexClearableGuard aReadLock;
+
+ // only top frames, which are part of our desktop hierarchy, can
+ // do so! By the way - we need the desktop instance to have access
+ // to all other top level frames too.
+ css::uno::Reference< css::frame::XDesktop > xDesktop (m_xParent, css::uno::UNO_QUERY);
+ css::uno::Reference< css::frame::XFramesSupplier > xTaskSupplier(xDesktop , css::uno::UNO_QUERY);
+ if ( !xDesktop.is() || !xTaskSupplier.is() )
+ return;
+
+ aReadLock.clear();
+ /* } SAFE */
+
+ // analyze the list of current open tasks
+ // Suppress search for other views to the same model ...
+ // It's not needed here and can be very expensive.
+ FrameListAnalyzer aAnalyzer(
+ xTaskSupplier,
+ this,
+ FrameAnalyzerFlags::Hidden | FrameAnalyzerFlags::Help | FrameAnalyzerFlags::BackingComponent);
+
+ // specify the new frame, which must have this special state...
+ css::uno::Reference< css::frame::XFrame2 > xNewCloserFrame;
+
+ // a)
+ // If there exist at least one other frame - there are two frames currently open.
+ // But we can enable this closer only, if one of these two tasks includes the help module.
+ // The "other frame" couldn't be the help. Because then it wouldn't be part of this "other list".
+ // In such case it will be separated to the reference aAnalyzer.m_xHelp!
+ // But we must check, if we include ourself the help...
+ // Check aAnalyzer.m_bReferenceIsHelp!
+ if (
+ (aAnalyzer.m_lOtherVisibleFrames.size()==1) &&
+ (
+ (aAnalyzer.m_bReferenceIsHelp ) ||
+ (aAnalyzer.m_bReferenceIsHidden)
+ )
+ )
+ {
+ // others[0] can't be the backing component!
+ // Because it's set at the special member aAnalyzer.m_xBackingComponent ... :-)
+ xNewCloserFrame.set( aAnalyzer.m_lOtherVisibleFrames[0], css::uno::UNO_QUERY_THROW );
+ }
+
+ // b)
+ // There is no other frame... means no other document frame. The help module
+ // will be handled separately and must(!) be ignored here... excepting if we include ourself the help.
+ else if (
+ (aAnalyzer.m_lOtherVisibleFrames.empty()) &&
+ (!aAnalyzer.m_bReferenceIsHelp) &&
+ (!aAnalyzer.m_bReferenceIsHidden) &&
+ (!aAnalyzer.m_bReferenceIsBacking)
+ )
+ {
+ xNewCloserFrame = this;
+ }
+
+ // Look for necessary actions ...
+ // Only if the closer state must be moved from one frame to another one
+ // or must be enabled/disabled at all.
+ SolarMutexGuard aGuard;
+ // Holds the only frame, which must show the special closer menu item (can be NULL!)
+ static css::uno::WeakReference< css::frame::XFrame2 > s_xCloserFrame;
+ css::uno::Reference< css::frame::XFrame2 > xCloserFrame (s_xCloserFrame.get(), css::uno::UNO_QUERY);
+ if (xCloserFrame!=xNewCloserFrame)
+ {
+ if (xCloserFrame.is())
+ impl_setCloser(xCloserFrame, false);
+ if (xNewCloserFrame.is())
+ impl_setCloser(xNewCloserFrame, true);
+ s_xCloserFrame = xNewCloserFrame;
+ }
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_Frame_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ rtl::Reference<XFrameImpl> inst = new XFrameImpl(context);
+ css::uno::XInterface *acquired_inst = cppu::acquire(inst.get());
+
+ inst->initListeners();
+
+ return acquired_inst;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/services/mediatypedetectionhelper.cxx b/framework/source/services/mediatypedetectionhelper.cxx
new file mode 100644
index 000000000..894f95740
--- /dev/null
+++ b/framework/source/services/mediatypedetectionhelper.cxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <services/mediatypedetectionhelper.hxx>
+#include <svl/inettype.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+namespace framework
+{
+
+using namespace ::com::sun::star;
+
+// constructor
+
+MediaTypeDetectionHelper::MediaTypeDetectionHelper()
+{
+}
+
+// destructor
+
+MediaTypeDetectionHelper::~MediaTypeDetectionHelper()
+{
+}
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL MediaTypeDetectionHelper::getImplementationName()
+{
+ return "com.sun.star.comp.framework.MediaTypeDetectionHelper";
+}
+
+sal_Bool SAL_CALL MediaTypeDetectionHelper::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL MediaTypeDetectionHelper::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.MediaTypeDetectionHelper" };
+}
+
+
+// XStringMapping
+
+sal_Bool SAL_CALL MediaTypeDetectionHelper::mapStrings(uno::Sequence< OUString >& rSeq)
+{
+ bool bModified = false;
+ auto rSeqRange = asNonConstRange(rSeq);
+ for( sal_Int32 i = rSeq.getLength(); i--; )
+ {
+
+ OUString& rUrl = rSeqRange[i];
+ INetContentType eType = INetContentTypes::GetContentTypeFromURL( rUrl );
+
+ OUString aType( INetContentTypes::GetContentType( eType ) );
+ if (!aType.isEmpty())
+ {
+ rUrl = aType;
+ bModified = true;
+ }
+ }
+ return bModified;
+}
+
+} // namespace framework
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_MediaTypeDetectionHelper_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::MediaTypeDetectionHelper());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/services/modulemanager.cxx b/framework/source/services/modulemanager.cxx
new file mode 100644
index 000000000..475084c4c
--- /dev/null
+++ b/framework/source/services/modulemanager.cxx
@@ -0,0 +1,350 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/frame/UnknownModuleException.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XModule.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/frame/XModuleManager2.hpp>
+#include <com/sun/star/container/XNameReplace.hpp>
+#include <com/sun/star/container/XContainerQuery.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/configurationhelper.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/enumhelper.hxx>
+#include <utility>
+
+namespace {
+
+class ModuleManager:
+ public cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::frame::XModuleManager2,
+ css::container::XContainerQuery >
+{
+private:
+
+ /** the global uno service manager.
+ Must be used to create own needed services.
+ */
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ /** points to the underlying configuration.
+ This ModuleManager does not cache - it calls directly the
+ configuration API!
+ */
+ css::uno::Reference< css::container::XNameAccess > m_xCFG;
+
+public:
+
+ explicit ModuleManager(css::uno::Reference< css::uno::XComponentContext > xContext);
+
+ ModuleManager(const ModuleManager&) = delete;
+ ModuleManager& operator=(const ModuleManager&) = delete;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(
+ OUString const & ServiceName) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XModuleManager
+ virtual OUString SAL_CALL identify(const css::uno::Reference< css::uno::XInterface >& xModule) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName(const OUString& sName ,
+ const css::uno::Any& aValue) override;
+
+ // XNameAccess
+ virtual css::uno::Any SAL_CALL getByName(const OUString& sName) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override;
+
+ virtual sal_Bool SAL_CALL hasByName(const OUString& sName) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XContainerQuery
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByQuery(const OUString& sQuery) override;
+
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByProperties(const css::uno::Sequence< css::beans::NamedValue >& lProperties) override;
+
+private:
+
+ /** @short makes the real identification of the module.
+
+ @descr It checks for the optional but preferred interface
+ XModule first. If this module does not exists at the
+ given component it tries to use XServiceInfo instead.
+
+ Note: This method try to locate a suitable module name.
+ Nothing else. Selecting the right component and throwing suitable
+ exceptions must be done outside.
+
+ @see identify()
+
+ @param xComponent
+ the module for identification.
+
+ @return The identifier of the given module.
+ Can be empty if given component is not a real module !
+
+ @threadsafe
+ */
+ OUString implts_identify(const css::uno::Reference< css::uno::XInterface >& xComponent);
+};
+
+ModuleManager::ModuleManager(css::uno::Reference< css::uno::XComponentContext > xContext)
+ : m_xContext(std::move(xContext))
+{
+ m_xCFG.set( comphelper::ConfigurationHelper::openConfig(
+ m_xContext, "/org.openoffice.Setup/Office/Factories",
+ comphelper::EConfigurationModes::ReadOnly ),
+ css::uno::UNO_QUERY_THROW );
+}
+
+OUString ModuleManager::getImplementationName()
+{
+ return "com.sun.star.comp.framework.ModuleManager";
+}
+
+sal_Bool ModuleManager::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > ModuleManager::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ModuleManager" };
+}
+
+OUString SAL_CALL ModuleManager::identify(const css::uno::Reference< css::uno::XInterface >& xModule)
+{
+ // valid parameter?
+ css::uno::Reference< css::frame::XFrame > xFrame (xModule, css::uno::UNO_QUERY);
+ css::uno::Reference< css::awt::XWindow > xWindow (xModule, css::uno::UNO_QUERY);
+ css::uno::Reference< css::frame::XController > xController(xModule, css::uno::UNO_QUERY);
+ css::uno::Reference< css::frame::XModel > xModel (xModule, css::uno::UNO_QUERY);
+
+ if (
+ (!xFrame.is() ) &&
+ (!xWindow.is() ) &&
+ (!xController.is()) &&
+ (!xModel.is() )
+ )
+ {
+ throw css::lang::IllegalArgumentException(
+ "Given module is not a frame nor a window, controller or model.",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 1);
+ }
+
+ if (xFrame.is())
+ {
+ xController = xFrame->getController();
+ xWindow = xFrame->getComponentWindow();
+ }
+ if (xController.is())
+ xModel = xController->getModel();
+
+ // modules are implemented by the deepest component in hierarchy ...
+ // Means: model -> controller -> window
+ // No fallbacks to higher components are allowed !
+ // Note : A frame provides access to module components only ... but it's not a module by himself.
+
+ OUString sModule;
+ if (xModel.is())
+ sModule = implts_identify(xModel);
+ else if (xController.is())
+ sModule = implts_identify(xController);
+ else if (xWindow.is())
+ sModule = implts_identify(xWindow);
+
+ if (sModule.isEmpty())
+ throw css::frame::UnknownModuleException(
+ "Can not find suitable module for the given component.",
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ return sModule;
+}
+
+void SAL_CALL ModuleManager::replaceByName(const OUString& sName ,
+ const css::uno::Any& aValue)
+{
+ ::comphelper::SequenceAsHashMap lProps(aValue);
+ if (lProps.empty() )
+ {
+ throw css::lang::IllegalArgumentException(
+ "No properties given to replace part of module.",
+ static_cast< cppu::OWeakObject * >(this),
+ 2);
+ }
+
+ // get access to the element
+ // Note: Don't use impl_getConfig() method here. Because it creates a readonly access only, further
+ // it cache it as a member of this module manager instance. If we change some props there ... but don't
+ // flush changes (because an error occurred) we will read them later. If we use a different config access
+ // we can close it without a flush... and our read data won't be affected .-)
+ css::uno::Reference< css::uno::XInterface > xCfg = ::comphelper::ConfigurationHelper::openConfig(
+ m_xContext,
+ "/org.openoffice.Setup/Office/Factories",
+ ::comphelper::EConfigurationModes::Standard);
+ css::uno::Reference< css::container::XNameAccess > xModules (xCfg, css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::container::XNameReplace > xModule ;
+
+ xModules->getByName(sName) >>= xModule;
+ if (!xModule.is())
+ {
+ throw css::uno::RuntimeException(
+ "Was not able to get write access to the requested module entry inside configuration.",
+ static_cast< cppu::OWeakObject * >(this));
+ }
+
+ for (auto const& prop : lProps)
+ {
+ // let "NoSuchElementException" out ! We support the same API ...
+ // and without a flush() at the end all changed data before will be ignored !
+ xModule->replaceByName(prop.first.maString, prop.second);
+ }
+
+ ::comphelper::ConfigurationHelper::flush(xCfg);
+}
+
+css::uno::Any SAL_CALL ModuleManager::getByName(const OUString& sName)
+{
+ // get access to the element
+ css::uno::Reference< css::container::XNameAccess > xModule;
+ m_xCFG->getByName(sName) >>= xModule;
+ if (!xModule.is())
+ {
+ throw css::uno::RuntimeException(
+ "Was not able to get write access to the requested module entry inside configuration.",
+ static_cast< cppu::OWeakObject * >(this));
+ }
+
+ // convert it to seq< PropertyValue >
+ const css::uno::Sequence< OUString > lPropNames = xModule->getElementNames();
+ comphelper::SequenceAsHashMap lProps;
+
+ lProps[OUString("ooSetupFactoryModuleIdentifier")] <<= sName;
+ for (const OUString& sPropName : lPropNames)
+ {
+ lProps[sPropName] = xModule->getByName(sPropName);
+ }
+
+ return css::uno::Any(lProps.getAsConstPropertyValueList());
+}
+
+css::uno::Sequence< OUString > SAL_CALL ModuleManager::getElementNames()
+{
+ return m_xCFG->getElementNames();
+}
+
+sal_Bool SAL_CALL ModuleManager::hasByName(const OUString& sName)
+{
+ return m_xCFG->hasByName(sName);
+}
+
+css::uno::Type SAL_CALL ModuleManager::getElementType()
+{
+ return cppu::UnoType<css::uno::Sequence< css::beans::PropertyValue >>::get();
+}
+
+sal_Bool SAL_CALL ModuleManager::hasElements()
+{
+ return m_xCFG->hasElements();
+}
+
+css::uno::Reference< css::container::XEnumeration > SAL_CALL ModuleManager::createSubSetEnumerationByQuery(const OUString&)
+{
+ return css::uno::Reference< css::container::XEnumeration >();
+}
+
+css::uno::Reference< css::container::XEnumeration > SAL_CALL ModuleManager::createSubSetEnumerationByProperties(const css::uno::Sequence< css::beans::NamedValue >& lProperties)
+{
+ ::comphelper::SequenceAsHashMap lSearchProps(lProperties);
+ const css::uno::Sequence< OUString > lModules = getElementNames();
+ ::std::vector< css::uno::Any > lResult;
+
+ for (const OUString& rModuleName : lModules)
+ {
+ try
+ {
+ ::comphelper::SequenceAsHashMap lModuleProps = getByName(rModuleName);
+ if (lModuleProps.match(lSearchProps))
+ lResult.push_back(css::uno::Any(lModuleProps.getAsConstPropertyValueList()));
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+ }
+
+ return new ::comphelper::OAnyEnumeration(comphelper::containerToSequence(lResult));
+}
+
+OUString ModuleManager::implts_identify(const css::uno::Reference< css::uno::XInterface >& xComponent)
+{
+ // Search for an optional (!) interface XModule first.
+ // It's used to overrule an existing service name. Used e.g. by our database form designer
+ // which uses a writer module internally.
+ css::uno::Reference< css::frame::XModule > xModule(xComponent, css::uno::UNO_QUERY);
+ if (xModule.is())
+ return xModule->getIdentifier();
+
+ // detect modules in a generic way...
+ // comparing service names with configured entries...
+ css::uno::Reference< css::lang::XServiceInfo > xInfo(xComponent, css::uno::UNO_QUERY);
+ if (!xInfo.is())
+ return OUString();
+
+ const css::uno::Sequence< OUString > lKnownModules = getElementNames();
+ for (const OUString& rName : lKnownModules)
+ {
+ if (xInfo->supportsService(rName))
+ return rName;
+ }
+
+ return OUString();
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_ModuleManager_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ModuleManager(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/services/pathsettings.cxx b/framework/source/services/pathsettings.cxx
new file mode 100644
index 000000000..9457ad862
--- /dev/null
+++ b/framework/source/services/pathsettings.cxx
@@ -0,0 +1,1422 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+#include <utility>
+#include <unordered_map>
+
+#include <properties.h>
+#include <helper/mischelper.hxx>
+
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/XProperty.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XChangesNotifier.hpp>
+#include <com/sun/star/util/PathSubstitution.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/util/XStringSubstitution.hpp>
+#include <com/sun/star/util/XChangesListener.hpp>
+#include <com/sun/star/util/XPathSettings.hpp>
+
+#include <tools/urlobj.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/propshlp.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/configurationhelper.hxx>
+#include <unotools/configpaths.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace framework;
+
+constexpr OUStringLiteral CFGPROP_USERPATHS = u"UserPaths";
+constexpr OUStringLiteral CFGPROP_WRITEPATH = u"WritePath";
+
+/*
+ 0 : old style "Template" string using ";" as separator
+ 1 : internal paths "Template_internal" string list
+ 2 : user paths "Template_user" string list
+ 3 : write path "Template_write" string
+ */
+
+constexpr OUStringLiteral POSTFIX_INTERNAL_PATHS = u"_internal";
+constexpr OUStringLiteral POSTFIX_USER_PATHS = u"_user";
+constexpr OUStringLiteral POSTFIX_WRITE_PATH = u"_writable";
+
+namespace {
+
+const sal_Int32 IDGROUP_OLDSTYLE = 0;
+const sal_Int32 IDGROUP_INTERNAL_PATHS = 1;
+const sal_Int32 IDGROUP_USER_PATHS = 2;
+const sal_Int32 IDGROUP_WRITE_PATH = 3;
+
+const sal_Int32 IDGROUP_COUNT = 4;
+
+sal_Int32 impl_getPropGroup(sal_Int32 nID)
+{
+ return (nID % IDGROUP_COUNT);
+}
+
+/* enable it if you wish to migrate old user settings (using the old cfg schema) on demand...
+ disable it in case only the new schema must be used.
+ */
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::lang::XServiceInfo,
+ css::util::XChangesListener, // => XEventListener
+ css::util::XPathSettings> // => XPropertySet
+ PathSettings_BASE;
+
+class PathSettings : private cppu::BaseMutex
+ , public PathSettings_BASE
+ , public ::cppu::OPropertySetHelper
+{
+ struct PathInfo
+ {
+ public:
+
+ PathInfo()
+ : bIsSinglePath (false)
+ , bIsReadonly (false)
+ {}
+
+ /// an internal name describing this path
+ OUString sPathName;
+
+ /// contains all paths, which are used internally - but are not visible for the user.
+ std::vector<OUString> lInternalPaths;
+
+ /// contains all paths configured by the user
+ std::vector<OUString> lUserPaths;
+
+ /// this special path is used to generate feature depending content there
+ OUString sWritePath;
+
+ /// indicates real single paths, which uses WritePath property only
+ bool bIsSinglePath;
+
+ /// simple handling of finalized/mandatory states ... => we know one state READONLY only .-)
+ bool bIsReadonly;
+ };
+
+ typedef std::unordered_map<OUString, PathSettings::PathInfo> PathHash;
+
+ enum EChangeOp
+ {
+ E_UNDEFINED,
+ E_ADDED,
+ E_CHANGED,
+ E_REMOVED
+ };
+
+private:
+
+ /** reference to factory, which has create this instance. */
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ /** list of all path variables and her corresponding values. */
+ PathSettings::PathHash m_lPaths;
+
+ /** describes all properties available on our interface.
+ Will be generated on demand based on our path list m_lPaths. */
+ css::uno::Sequence< css::beans::Property > m_lPropDesc;
+
+ /** helper needed to (re-)substitute all internal save path values. */
+ css::uno::Reference< css::util::XStringSubstitution > m_xSubstitution;
+
+ /** provides access to the old configuration schema (which will be migrated on demand). */
+ css::uno::Reference< css::container::XNameAccess > m_xCfgOld;
+
+ /** provides access to the new configuration schema. */
+ css::uno::Reference< css::container::XNameAccess > m_xCfgNew;
+
+ /** helper to listen for configuration changes without ownership cycle problems */
+ css::uno::Reference< css::util::XChangesListener > m_xCfgNewListener;
+
+ std::unique_ptr<::cppu::OPropertyArrayHelper> m_pPropHelp;
+
+public:
+
+ /** initialize a new instance of this class.
+ Attention: It's necessary for right function of this class, that the order of base
+ classes is the right one. Because we transfer information from one base to another
+ during this ctor runs! */
+ explicit PathSettings(css::uno::Reference< css::uno::XComponentContext > xContext);
+
+ /** free all used resources ... if it was not already done. */
+ virtual ~PathSettings() override;
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.PathSettings";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.util.PathSettings"};
+ }
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& type) override;
+ virtual void SAL_CALL acquire() noexcept override
+ { OWeakObject::acquire(); }
+ virtual void SAL_CALL release() noexcept override
+ { OWeakObject::release(); }
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+
+ // css::util::XChangesListener
+ virtual void SAL_CALL changesOccurred(const css::util::ChangesEvent& aEvent) override;
+
+ // css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& aSource) override;
+
+ /**
+ * XPathSettings attribute methods
+ */
+ virtual OUString SAL_CALL getAddin() override
+ { return getStringProperty("Addin"); }
+ virtual void SAL_CALL setAddin(const OUString& p1) override
+ { setStringProperty("Addin", p1); }
+ virtual OUString SAL_CALL getAutoCorrect() override
+ { return getStringProperty("AutoCorrect"); }
+ virtual void SAL_CALL setAutoCorrect(const OUString& p1) override
+ { setStringProperty("AutoCorrect", p1); }
+ virtual OUString SAL_CALL getAutoText() override
+ { return getStringProperty("AutoText"); }
+ virtual void SAL_CALL setAutoText(const OUString& p1) override
+ { setStringProperty("AutoText", p1); }
+ virtual OUString SAL_CALL getBackup() override
+ { return getStringProperty("Backup"); }
+ virtual void SAL_CALL setBackup(const OUString& p1) override
+ { setStringProperty("Backup", p1); }
+ virtual OUString SAL_CALL getBasic() override
+ { return getStringProperty("Basic"); }
+ virtual void SAL_CALL setBasic(const OUString& p1) override
+ { setStringProperty("Basic", p1); }
+ virtual OUString SAL_CALL getBitmap() override
+ { return getStringProperty("Bitmap"); }
+ virtual void SAL_CALL setBitmap(const OUString& p1) override
+ { setStringProperty("Bitmap", p1); }
+ virtual OUString SAL_CALL getConfig() override
+ { return getStringProperty("Config"); }
+ virtual void SAL_CALL setConfig(const OUString& p1) override
+ { setStringProperty("Config", p1); }
+ virtual OUString SAL_CALL getDictionary() override
+ { return getStringProperty("Dictionary"); }
+ virtual void SAL_CALL setDictionary(const OUString& p1) override
+ { setStringProperty("Dictionary", p1); }
+ virtual OUString SAL_CALL getFavorite() override
+ { return getStringProperty("Favorite"); }
+ virtual void SAL_CALL setFavorite(const OUString& p1) override
+ { setStringProperty("Favorite", p1); }
+ virtual OUString SAL_CALL getFilter() override
+ { return getStringProperty("Filter"); }
+ virtual void SAL_CALL setFilter(const OUString& p1) override
+ { setStringProperty("Filter", p1); }
+ virtual OUString SAL_CALL getGallery() override
+ { return getStringProperty("Gallery"); }
+ virtual void SAL_CALL setGallery(const OUString& p1) override
+ { setStringProperty("Gallery", p1); }
+ virtual OUString SAL_CALL getGraphic() override
+ { return getStringProperty("Graphic"); }
+ virtual void SAL_CALL setGraphic(const OUString& p1) override
+ { setStringProperty("Graphic", p1); }
+ virtual OUString SAL_CALL getHelp() override
+ { return getStringProperty("Help"); }
+ virtual void SAL_CALL setHelp(const OUString& p1) override
+ { setStringProperty("Help", p1); }
+ virtual OUString SAL_CALL getLinguistic() override
+ { return getStringProperty("Linguistic"); }
+ virtual void SAL_CALL setLinguistic(const OUString& p1) override
+ { setStringProperty("Linguistic", p1); }
+ virtual OUString SAL_CALL getModule() override
+ { return getStringProperty("Module"); }
+ virtual void SAL_CALL setModule(const OUString& p1) override
+ { setStringProperty("Module", p1); }
+ virtual OUString SAL_CALL getPalette() override
+ { return getStringProperty("Palette"); }
+ virtual void SAL_CALL setPalette(const OUString& p1) override
+ { setStringProperty("Palette", p1); }
+ virtual OUString SAL_CALL getPlugin() override
+ { return getStringProperty("Plugin"); }
+ virtual void SAL_CALL setPlugin(const OUString& p1) override
+ { setStringProperty("Plugin", p1); }
+ virtual OUString SAL_CALL getStorage() override
+ { return getStringProperty("Storage"); }
+ virtual void SAL_CALL setStorage(const OUString& p1) override
+ { setStringProperty("Storage", p1); }
+ virtual OUString SAL_CALL getTemp() override
+ { return getStringProperty("Temp"); }
+ virtual void SAL_CALL setTemp(const OUString& p1) override
+ { setStringProperty("Temp", p1); }
+ virtual OUString SAL_CALL getTemplate() override
+ { return getStringProperty("Template"); }
+ virtual void SAL_CALL setTemplate(const OUString& p1) override
+ { setStringProperty("Template", p1); }
+ virtual OUString SAL_CALL getUIConfig() override
+ { return getStringProperty("UIConfig"); }
+ virtual void SAL_CALL setUIConfig(const OUString& p1) override
+ { setStringProperty("UIConfig", p1); }
+ virtual OUString SAL_CALL getUserConfig() override
+ { return getStringProperty("UserConfig"); }
+ virtual void SAL_CALL setUserConfig(const OUString& p1) override
+ { setStringProperty("UserConfig", p1); }
+ virtual OUString SAL_CALL getUserDictionary() override
+ { return getStringProperty("UserDictionary"); }
+ virtual void SAL_CALL setUserDictionary(const OUString& p1) override
+ { setStringProperty("UserDictionary", p1); }
+ virtual OUString SAL_CALL getWork() override
+ { return getStringProperty("Work"); }
+ virtual void SAL_CALL setWork(const OUString& p1) override
+ { setStringProperty("Work", p1); }
+ virtual OUString SAL_CALL getBasePathShareLayer() override
+ { return getStringProperty("UIConfig"); }
+ virtual void SAL_CALL setBasePathShareLayer(const OUString& p1) override
+ { setStringProperty("UIConfig", p1); }
+ virtual OUString SAL_CALL getBasePathUserLayer() override
+ { return getStringProperty("UserConfig"); }
+ virtual void SAL_CALL setBasePathUserLayer(const OUString& p1) override
+ { setStringProperty("UserConfig", p1); }
+
+ /**
+ * overrides to resolve inheritance ambiguity
+ */
+ virtual void SAL_CALL setPropertyValue(const OUString& p1, const css::uno::Any& p2) override
+ { ::cppu::OPropertySetHelper::setPropertyValue(p1, p2); }
+ virtual css::uno::Any SAL_CALL getPropertyValue(const OUString& p1) override
+ { return ::cppu::OPropertySetHelper::getPropertyValue(p1); }
+ virtual void SAL_CALL addPropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
+ { ::cppu::OPropertySetHelper::addPropertyChangeListener(p1, p2); }
+ virtual void SAL_CALL removePropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
+ { ::cppu::OPropertySetHelper::removePropertyChangeListener(p1, p2); }
+ virtual void SAL_CALL addVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
+ { ::cppu::OPropertySetHelper::addVetoableChangeListener(p1, p2); }
+ virtual void SAL_CALL removeVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
+ { ::cppu::OPropertySetHelper::removeVetoableChangeListener(p1, p2); }
+ /** read all configured paths and create all needed internal structures. */
+ void impl_readAll();
+
+private:
+ virtual void SAL_CALL disposing() final override;
+
+ /// @throws css::uno::RuntimeException
+ OUString getStringProperty(const OUString& p1);
+
+ /// @throws css::uno::RuntimeException
+ void setStringProperty(const OUString& p1, const OUString& p2);
+
+ /** read a path info using the old cfg schema.
+ This is needed for "migration on demand" reasons only.
+ Can be removed for next major release .-) */
+ std::vector<OUString> impl_readOldFormat(const OUString& sPath);
+
+ /** read a path info using the new cfg schema. */
+ PathSettings::PathInfo impl_readNewFormat(const OUString& sPath);
+
+ /** filter "real user defined paths" from the old configuration schema
+ and set it as UserPaths on the new schema.
+ Can be removed with new major release ... */
+
+ void impl_mergeOldUserPaths( PathSettings::PathInfo& rPath,
+ const std::vector<OUString>& lOld );
+
+ /** reload one path directly from the new configuration schema (because
+ it was updated by any external code) */
+ PathSettings::EChangeOp impl_updatePath(const OUString& sPath ,
+ bool bNotifyListener);
+
+ /** replace all might existing placeholder variables inside the given path ...
+ or check if the given path value uses paths, which can be replaced with predefined
+ placeholder variables ...
+ */
+ void impl_subst(std::vector<OUString>& lVals ,
+ const css::uno::Reference< css::util::XStringSubstitution >& xSubst ,
+ bool bReSubst);
+
+ void impl_subst(PathSettings::PathInfo& aPath ,
+ bool bReSubst);
+
+ /** converts our new string list schema to the old ";" separated schema ... */
+ OUString impl_convertPath2OldStyle(const PathSettings::PathInfo& rPath ) const;
+ std::vector<OUString> impl_convertOldStyle2Path(std::u16string_view sOldStylePath) const;
+
+ /** remove still known paths from the given lList argument.
+ So real user defined paths can be extracted from the list of
+ fix internal paths !
+ */
+ void impl_purgeKnownPaths(PathSettings::PathInfo& rPath,
+ std::vector<OUString>& lList);
+
+ /** rebuild the member m_lPropDesc using the path list m_lPaths. */
+ void impl_rebuildPropertyDescriptor();
+
+ /** provides direct access to the list of path values
+ using its internal property id.
+ */
+ css::uno::Any impl_getPathValue( sal_Int32 nID ) const;
+ void impl_setPathValue( sal_Int32 nID ,
+ const css::uno::Any& aVal);
+
+ /** check the given handle and return the corresponding PathInfo reference.
+ These reference can be used then directly to manipulate these path. */
+ PathSettings::PathInfo* impl_getPathAccess (sal_Int32 nHandle);
+ const PathSettings::PathInfo* impl_getPathAccessConst(sal_Int32 nHandle) const;
+
+ /** it checks, if the given path value seems to be a valid URL or system path. */
+ bool impl_isValidPath(std::u16string_view sPath) const;
+ bool impl_isValidPath(const std::vector<OUString>& lPath) const;
+
+ void impl_storePath(const PathSettings::PathInfo& aPath);
+
+ css::uno::Sequence< sal_Int32 > impl_mapPathName2IDList(std::u16string_view sPath);
+
+ void impl_notifyPropListener( std::u16string_view sPath ,
+ const PathSettings::PathInfo* pPathOld,
+ const PathSettings::PathInfo* pPathNew);
+
+ // OPropertySetHelper
+ virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any& aConvertedValue,
+ css::uno::Any& aOldValue,
+ sal_Int32 nHandle,
+ const css::uno::Any& aValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle,
+ const css::uno::Any& aValue ) override;
+ virtual void SAL_CALL getFastPropertyValue( css::uno::Any& aValue,
+ sal_Int32 nHandle ) const override;
+ // Avoid:
+ // warning: 'virtual css::uno::Any cppu::OPropertySetHelper::getFastPropertyValue(sal_Int32)' was hidden [-Woverloaded-virtual]
+ // warning: by ‘virtual void {anonymous}::PathSettings::getFastPropertyValue(css::uno::Any&, sal_Int32) const’ [-Woverloaded-virtual]
+ using cppu::OPropertySetHelper::getFastPropertyValue;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
+
+ /** factory methods to guarantee right (but on demand) initialized members ... */
+ css::uno::Reference< css::util::XStringSubstitution > fa_getSubstitution();
+ css::uno::Reference< css::container::XNameAccess > fa_getCfgOld();
+ css::uno::Reference< css::container::XNameAccess > fa_getCfgNew();
+};
+
+PathSettings::PathSettings( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : PathSettings_BASE(m_aMutex)
+ , ::cppu::OPropertySetHelper(cppu::WeakComponentImplHelperBase::rBHelper)
+ , m_xContext (std::move(xContext))
+{
+}
+
+PathSettings::~PathSettings()
+{
+ disposing();
+}
+
+void SAL_CALL PathSettings::disposing()
+{
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ css::uno::Reference< css::util::XChangesNotifier >
+ xBroadcaster(m_xCfgNew, css::uno::UNO_QUERY);
+ if (xBroadcaster.is())
+ xBroadcaster->removeChangesListener(m_xCfgNewListener);
+
+ m_xSubstitution.clear();
+ m_xCfgOld.clear();
+ m_xCfgNew.clear();
+ m_xCfgNewListener.clear();
+
+ m_pPropHelp.reset();
+}
+
+css::uno::Any SAL_CALL PathSettings::queryInterface( const css::uno::Type& _rType )
+{
+ css::uno::Any aRet = PathSettings_BASE::queryInterface( _rType );
+ if ( !aRet.hasValue() )
+ aRet = ::cppu::OPropertySetHelper::queryInterface( _rType );
+ return aRet;
+}
+
+css::uno::Sequence< css::uno::Type > SAL_CALL PathSettings::getTypes( )
+{
+ return comphelper::concatSequences(
+ PathSettings_BASE::getTypes(),
+ ::cppu::OPropertySetHelper::getTypes()
+ );
+}
+
+void SAL_CALL PathSettings::changesOccurred(const css::util::ChangesEvent& aEvent)
+{
+ sal_Int32 c = aEvent.Changes.getLength();
+ sal_Int32 i = 0;
+ bool bUpdateDescriptor = false;
+
+ for (i=0; i<c; ++i)
+ {
+ const css::util::ElementChange& aChange = aEvent.Changes[i];
+
+ OUString sChanged;
+ aChange.Accessor >>= sChanged;
+
+ OUString sPath = ::utl::extractFirstFromConfigurationPath(sChanged);
+ if (!sPath.isEmpty())
+ {
+ PathSettings::EChangeOp eOp = impl_updatePath(sPath, true);
+ if (
+ (eOp == PathSettings::E_ADDED ) ||
+ (eOp == PathSettings::E_REMOVED)
+ )
+ bUpdateDescriptor = true;
+ }
+ }
+
+ if (bUpdateDescriptor)
+ impl_rebuildPropertyDescriptor();
+}
+
+void SAL_CALL PathSettings::disposing(const css::lang::EventObject& aSource)
+{
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ if (aSource.Source == m_xCfgNew)
+ m_xCfgNew.clear();
+}
+
+OUString PathSettings::getStringProperty(const OUString& p1)
+{
+ css::uno::Any a = ::cppu::OPropertySetHelper::getPropertyValue(p1);
+ OUString s;
+ a >>= s;
+ return s;
+}
+
+void PathSettings::setStringProperty(const OUString& p1, const OUString& p2)
+{
+ ::cppu::OPropertySetHelper::setPropertyValue(p1, css::uno::Any(p2));
+}
+
+void PathSettings::impl_readAll()
+{
+ try
+ {
+ // TODO think about me
+ css::uno::Reference< css::container::XNameAccess > xCfg = fa_getCfgNew();
+ css::uno::Sequence< OUString > lPaths = xCfg->getElementNames();
+
+ sal_Int32 c = lPaths.getLength();
+ for (sal_Int32 i = 0; i < c; ++i)
+ {
+ const OUString& sPath = lPaths[i];
+ impl_updatePath(sPath, false);
+ }
+ }
+ catch(const css::uno::RuntimeException& )
+ {
+ }
+
+ impl_rebuildPropertyDescriptor();
+}
+
+// NO substitution here ! It's done outside ...
+std::vector<OUString> PathSettings::impl_readOldFormat(const OUString& sPath)
+{
+ css::uno::Reference< css::container::XNameAccess > xCfg( fa_getCfgOld() );
+ std::vector<OUString> aPathVal;
+
+ if( xCfg->hasByName(sPath) )
+ {
+ css::uno::Any aVal( xCfg->getByName(sPath) );
+
+ OUString sStringVal;
+ css::uno::Sequence< OUString > lStringListVal;
+
+ if (aVal >>= sStringVal)
+ {
+ aPathVal.push_back(sStringVal);
+ }
+ else if (aVal >>= lStringListVal)
+ {
+ aPathVal = comphelper::sequenceToContainer<std::vector<OUString>>(lStringListVal);
+ }
+ }
+
+ return aPathVal;
+}
+
+// NO substitution here ! It's done outside ...
+PathSettings::PathInfo PathSettings::impl_readNewFormat(const OUString& sPath)
+{
+ css::uno::Reference< css::container::XNameAccess > xCfg = fa_getCfgNew();
+
+ // get access to the "queried" path
+ css::uno::Reference< css::container::XNameAccess > xPath;
+ xCfg->getByName(sPath) >>= xPath;
+
+ PathSettings::PathInfo aPathVal;
+
+ // read internal path list
+ css::uno::Reference< css::container::XNameAccess > xIPath;
+ xPath->getByName("InternalPaths") >>= xIPath;
+ aPathVal.lInternalPaths = comphelper::sequenceToContainer<std::vector<OUString>>(xIPath->getElementNames());
+
+ // read user defined path list
+ css::uno::Sequence<OUString> vTmpUserPathsSeq;
+ xPath->getByName(CFGPROP_USERPATHS) >>= vTmpUserPathsSeq;
+ aPathVal.lUserPaths = comphelper::sequenceToContainer<std::vector<OUString>>(vTmpUserPathsSeq);
+
+ // read the writeable path
+ xPath->getByName(CFGPROP_WRITEPATH) >>= aPathVal.sWritePath;
+
+ // avoid duplicates, by removing the writeable path from
+ // the user defined path list if it happens to be there too
+ std::vector<OUString>::iterator aI = std::find(aPathVal.lUserPaths.begin(), aPathVal.lUserPaths.end(), aPathVal.sWritePath);
+ if (aI != aPathVal.lUserPaths.end())
+ aPathVal.lUserPaths.erase(aI);
+
+ // read state props
+ xPath->getByName("IsSinglePath") >>= aPathVal.bIsSinglePath;
+
+ // analyze finalized/mandatory states
+ aPathVal.bIsReadonly = false;
+ css::uno::Reference< css::beans::XProperty > xInfo(xPath, css::uno::UNO_QUERY);
+ if (xInfo.is())
+ {
+ css::beans::Property aInfo = xInfo->getAsProperty();
+ bool bFinalized = ((aInfo.Attributes & css::beans::PropertyAttribute::READONLY ) == css::beans::PropertyAttribute::READONLY );
+
+ // Note: 'till we support finalized/mandatory on our API more in detail we handle
+ // all states simple as READONLY! But because all really needed paths are "mandatory" by default
+ // we have to handle "finalized" as the real "readonly" indicator.
+ aPathVal.bIsReadonly = bFinalized;
+ }
+
+ return aPathVal;
+}
+
+void PathSettings::impl_storePath(const PathSettings::PathInfo& aPath)
+{
+ css::uno::Reference< css::container::XNameAccess > xCfgNew = fa_getCfgNew();
+ css::uno::Reference< css::container::XNameAccess > xCfgOld = fa_getCfgOld();
+
+ // try to replace path-parts with well known and supported variables.
+ // So an office can be moved easily to another location without losing
+ // its related paths.
+ PathInfo aResubstPath(aPath);
+ impl_subst(aResubstPath, true);
+
+ // update new configuration
+ if (! aResubstPath.bIsSinglePath)
+ {
+ ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew,
+ aResubstPath.sPathName,
+ CFGPROP_USERPATHS,
+ css::uno::Any(comphelper::containerToSequence(aResubstPath.lUserPaths)));
+ }
+
+ ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew,
+ aResubstPath.sPathName,
+ CFGPROP_WRITEPATH,
+ css::uno::Any(aResubstPath.sWritePath));
+
+ ::comphelper::ConfigurationHelper::flush(xCfgNew);
+
+ // remove the whole path from the old configuration!
+ // Otherwise we can't make sure that the diff between new and old configuration
+ // on loading time really represents a user setting!!!
+
+ // Check if the given path exists inside the old configuration.
+ // Because our new configuration knows more than the list of old paths ... !
+ if (xCfgOld->hasByName(aResubstPath.sPathName))
+ {
+ css::uno::Reference< css::beans::XPropertySet > xProps(xCfgOld, css::uno::UNO_QUERY_THROW);
+ xProps->setPropertyValue(aResubstPath.sPathName, css::uno::Any());
+ ::comphelper::ConfigurationHelper::flush(xCfgOld);
+ }
+}
+
+void PathSettings::impl_mergeOldUserPaths( PathSettings::PathInfo& rPath,
+ const std::vector<OUString>& lOld )
+{
+ for (auto const& old : lOld)
+ {
+ if (rPath.bIsSinglePath)
+ {
+ SAL_WARN_IF(lOld.size()>1, "fwk", "PathSettings::impl_mergeOldUserPaths(): Single path has more than one path value inside old configuration (Common.xcu)!");
+ if ( rPath.sWritePath != old )
+ rPath.sWritePath = old;
+ }
+ else
+ {
+ if (
+ ( std::find(rPath.lInternalPaths.begin(), rPath.lInternalPaths.end(), old) == rPath.lInternalPaths.end()) &&
+ ( std::find(rPath.lUserPaths.begin(), rPath.lUserPaths.end(), old) == rPath.lUserPaths.end() ) &&
+ ( rPath.sWritePath != old )
+ )
+ rPath.lUserPaths.push_back(old);
+ }
+ }
+}
+
+PathSettings::EChangeOp PathSettings::impl_updatePath(const OUString& sPath ,
+ bool bNotifyListener)
+{
+ // SAFE ->
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ PathSettings::PathInfo* pPathOld = nullptr;
+ PathSettings::PathInfo* pPathNew = nullptr;
+ PathSettings::EChangeOp eOp = PathSettings::E_UNDEFINED;
+ PathSettings::PathInfo aPath;
+
+ try
+ {
+ aPath = impl_readNewFormat(sPath);
+ aPath.sPathName = sPath;
+ // replace all might existing variables with real values
+ // Do it before these old paths will be compared against the
+ // new path configuration. Otherwise some strings uses different variables ... but substitution
+ // will produce strings with same content (because some variables are redundant!)
+ impl_subst(aPath, false);
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::container::NoSuchElementException&)
+ { eOp = PathSettings::E_REMOVED; }
+ catch(const css::uno::Exception&)
+ { throw; }
+
+ try
+ {
+ // migration of old user defined values on demand
+ // can be disabled for a new major
+ std::vector<OUString> lOldVals = impl_readOldFormat(sPath);
+ // replace all might existing variables with real values
+ // Do it before these old paths will be compared against the
+ // new path configuration. Otherwise some strings uses different variables ... but substitution
+ // will produce strings with same content (because some variables are redundant!)
+ impl_subst(lOldVals, fa_getSubstitution(), false);
+ impl_mergeOldUserPaths(aPath, lOldVals);
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ // Normal(!) exceptions can be ignored!
+ // E.g. in case an addon installs a new path, which was not well known for an OOo 1.x installation
+ // we can't find a value for it inside the "old" configuration. So a NoSuchElementException
+ // will be normal .-)
+ catch(const css::uno::Exception&)
+ {}
+
+ PathSettings::PathHash::iterator pPath = m_lPaths.find(sPath);
+ if (eOp == PathSettings::E_UNDEFINED)
+ {
+ if (pPath != m_lPaths.end())
+ eOp = PathSettings::E_CHANGED;
+ else
+ eOp = PathSettings::E_ADDED;
+ }
+
+ switch(eOp)
+ {
+ case PathSettings::E_ADDED :
+ {
+ if (bNotifyListener)
+ {
+ pPathOld = nullptr;
+ pPathNew = &aPath;
+ impl_notifyPropListener(sPath, pPathOld, pPathNew);
+ }
+ m_lPaths[sPath] = aPath;
+ }
+ break;
+
+ case PathSettings::E_CHANGED :
+ {
+ if (bNotifyListener)
+ {
+ pPathOld = &(pPath->second);
+ pPathNew = &aPath;
+ impl_notifyPropListener(sPath, pPathOld, pPathNew);
+ }
+ m_lPaths[sPath] = aPath;
+ }
+ break;
+
+ case PathSettings::E_REMOVED :
+ {
+ if (pPath != m_lPaths.end())
+ {
+ if (bNotifyListener)
+ {
+ pPathOld = &(pPath->second);
+ pPathNew = nullptr;
+ impl_notifyPropListener(sPath, pPathOld, pPathNew);
+ }
+ m_lPaths.erase(pPath);
+ }
+ }
+ break;
+
+ default: // to let compiler be happy
+ break;
+ }
+
+ return eOp;
+}
+
+css::uno::Sequence< sal_Int32 > PathSettings::impl_mapPathName2IDList(std::u16string_view sPath)
+{
+ OUString sInternalProp = OUString::Concat(sPath)+POSTFIX_INTERNAL_PATHS;
+ OUString sUserProp = OUString::Concat(sPath)+POSTFIX_USER_PATHS;
+ OUString sWriteProp = OUString::Concat(sPath)+POSTFIX_WRITE_PATH;
+
+ // Attention: The default set of IDs is fix and must follow these schema.
+ // Otherwise the outside code ant work for new added properties.
+ // Why?
+ // The outside code must fire N events for every changed property.
+ // And the knowing about packaging of variables of the structure PathInfo
+ // follow these group IDs! But if such ID is not in the range of [0..IDGROUP_COUNT]
+ // the outside can't determine the right group ... and can not fire the right events .-)
+
+ css::uno::Sequence<sal_Int32> lIDs{ IDGROUP_OLDSTYLE, IDGROUP_INTERNAL_PATHS,
+ IDGROUP_USER_PATHS, IDGROUP_WRITE_PATH };
+ assert(lIDs.getLength() == IDGROUP_COUNT);
+ auto plIDs = lIDs.getArray();
+
+ sal_Int32 c = m_lPropDesc.getLength();
+ sal_Int32 i = 0;
+ for (i=0; i<c; ++i)
+ {
+ const css::beans::Property& rProp = m_lPropDesc[i];
+
+ if (rProp.Name == sPath)
+ plIDs[IDGROUP_OLDSTYLE] = rProp.Handle;
+ else
+ if (rProp.Name == sInternalProp)
+ plIDs[IDGROUP_INTERNAL_PATHS] = rProp.Handle;
+ else
+ if (rProp.Name == sUserProp)
+ plIDs[IDGROUP_USER_PATHS] = rProp.Handle;
+ else
+ if (rProp.Name == sWriteProp)
+ plIDs[IDGROUP_WRITE_PATH] = rProp.Handle;
+ }
+
+ return lIDs;
+}
+
+void PathSettings::impl_notifyPropListener( std::u16string_view sPath,
+ const PathSettings::PathInfo* pPathOld,
+ const PathSettings::PathInfo* pPathNew)
+{
+ css::uno::Sequence< sal_Int32 > lHandles(1);
+ auto plHandles = lHandles.getArray();
+ css::uno::Sequence< css::uno::Any > lOldVals(1);
+ auto plOldVals = lOldVals.getArray();
+ css::uno::Sequence< css::uno::Any > lNewVals(1);
+ auto plNewVals = lNewVals.getArray();
+
+ css::uno::Sequence< sal_Int32 > lIDs = impl_mapPathName2IDList(sPath);
+ sal_Int32 c = lIDs.getLength();
+ sal_Int32 i = 0;
+ sal_Int32 nMaxID = m_lPropDesc.getLength()-1;
+ for (i=0; i<c; ++i)
+ {
+ sal_Int32 nID = lIDs[i];
+
+ if (
+ (nID < 0 ) ||
+ (nID > nMaxID)
+ )
+ continue;
+
+ plHandles[0] = nID;
+ switch(impl_getPropGroup(nID))
+ {
+ case IDGROUP_OLDSTYLE :
+ {
+ if (pPathOld)
+ {
+ OUString sVal = impl_convertPath2OldStyle(*pPathOld);
+ plOldVals[0] <<= sVal;
+ }
+ if (pPathNew)
+ {
+ OUString sVal = impl_convertPath2OldStyle(*pPathNew);
+ plNewVals[0] <<= sVal;
+ }
+ }
+ break;
+
+ case IDGROUP_INTERNAL_PATHS :
+ {
+ if (pPathOld)
+ plOldVals[0] <<= comphelper::containerToSequence(pPathOld->lInternalPaths);
+ if (pPathNew)
+ plNewVals[0] <<= comphelper::containerToSequence(pPathNew->lInternalPaths);
+ }
+ break;
+
+ case IDGROUP_USER_PATHS :
+ {
+ if (pPathOld)
+ plOldVals[0] <<= comphelper::containerToSequence(pPathOld->lUserPaths);
+ if (pPathNew)
+ plNewVals[0] <<= comphelper::containerToSequence(pPathNew->lUserPaths);
+ }
+ break;
+
+ case IDGROUP_WRITE_PATH :
+ {
+ if (pPathOld)
+ plOldVals[0] <<= pPathOld->sWritePath;
+ if (pPathNew)
+ plNewVals[0] <<= pPathNew->sWritePath;
+ }
+ break;
+ }
+
+ fire(plHandles,
+ plNewVals,
+ plOldVals,
+ 1,
+ false);
+ }
+}
+
+void PathSettings::impl_subst(std::vector<OUString>& lVals ,
+ const css::uno::Reference< css::util::XStringSubstitution >& xSubst ,
+ bool bReSubst)
+{
+ for (auto & old : lVals)
+ {
+ OUString sNew;
+ if (bReSubst)
+ sNew = xSubst->reSubstituteVariables(old);
+ else
+ sNew = xSubst->substituteVariables(old, false);
+
+ old = sNew;
+ }
+}
+
+void PathSettings::impl_subst(PathSettings::PathInfo& aPath ,
+ bool bReSubst)
+{
+ css::uno::Reference< css::util::XStringSubstitution > xSubst = fa_getSubstitution();
+
+ impl_subst(aPath.lInternalPaths, xSubst, bReSubst);
+ impl_subst(aPath.lUserPaths , xSubst, bReSubst);
+ if (bReSubst)
+ aPath.sWritePath = xSubst->reSubstituteVariables(aPath.sWritePath);
+ else
+ aPath.sWritePath = xSubst->substituteVariables(aPath.sWritePath, false);
+}
+
+OUString PathSettings::impl_convertPath2OldStyle(const PathSettings::PathInfo& rPath) const
+{
+ OUStringBuffer sPathVal(256);
+
+ for (auto const& internalPath : rPath.lInternalPaths)
+ {
+ if (sPathVal.getLength())
+ sPathVal.append(";");
+ sPathVal.append(internalPath);
+ }
+ for (auto const& userPath : rPath.lUserPaths)
+ {
+ if (sPathVal.getLength())
+ sPathVal.append(";");
+ sPathVal.append(userPath);
+ }
+ if (!rPath.sWritePath.isEmpty())
+ {
+ if (sPathVal.getLength())
+ sPathVal.append(";");
+ sPathVal.append(rPath.sWritePath);
+ }
+
+ return sPathVal.makeStringAndClear();
+}
+
+std::vector<OUString> PathSettings::impl_convertOldStyle2Path(std::u16string_view sOldStylePath) const
+{
+ std::vector<OUString> lList;
+ sal_Int32 nToken = 0;
+ do
+ {
+ OUString sToken( o3tl::getToken(sOldStylePath, 0, ';', nToken) );
+ if (!sToken.isEmpty())
+ lList.push_back(sToken);
+ }
+ while(nToken >= 0);
+
+ return lList;
+}
+
+void PathSettings::impl_purgeKnownPaths(PathSettings::PathInfo& rPath,
+ std::vector<OUString>& lList)
+{
+ // Erase items in the internal path list from lList.
+ // Also erase items in the internal path list from the user path list.
+ for (auto const& internalPath : rPath.lInternalPaths)
+ {
+ std::vector<OUString>::iterator pItem = std::find(lList.begin(), lList.end(), internalPath);
+ if (pItem != lList.end())
+ lList.erase(pItem);
+ pItem = std::find(rPath.lUserPaths.begin(), rPath.lUserPaths.end(), internalPath);
+ if (pItem != rPath.lUserPaths.end())
+ rPath.lUserPaths.erase(pItem);
+ }
+
+ // Erase items not in lList from the user path list.
+ rPath.lUserPaths.erase(std::remove_if(rPath.lUserPaths.begin(), rPath.lUserPaths.end(),
+ [&lList](const OUString& rItem) {
+ return std::find(lList.begin(), lList.end(), rItem) == lList.end();
+ }),
+ rPath.lUserPaths.end());
+
+ // Erase items in the user path list from lList.
+ for (auto const& userPath : rPath.lUserPaths)
+ {
+ std::vector<OUString>::iterator pItem = std::find(lList.begin(), lList.end(), userPath);
+ if (pItem != lList.end())
+ lList.erase(pItem);
+ }
+
+ // Erase the write path from lList
+ std::vector<OUString>::iterator pItem = std::find(lList.begin(), lList.end(), rPath.sWritePath);
+ if (pItem != lList.end())
+ lList.erase(pItem);
+}
+
+void PathSettings::impl_rebuildPropertyDescriptor()
+{
+ // SAFE ->
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ sal_Int32 c = static_cast<sal_Int32>(m_lPaths.size());
+ sal_Int32 i = 0;
+ m_lPropDesc.realloc(c*IDGROUP_COUNT);
+ auto plPropDesc = m_lPropDesc.getArray();
+
+ for (auto const& path : m_lPaths)
+ {
+ const PathSettings::PathInfo& rPath = path.second;
+ css::beans::Property* pProp = nullptr;
+
+ pProp = &(plPropDesc[i]);
+ pProp->Name = rPath.sPathName;
+ pProp->Handle = i;
+ pProp->Type = cppu::UnoType<OUString>::get();
+ pProp->Attributes = css::beans::PropertyAttribute::BOUND;
+ if (rPath.bIsReadonly)
+ pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
+ ++i;
+
+ pProp = &(plPropDesc[i]);
+ pProp->Name = rPath.sPathName+POSTFIX_INTERNAL_PATHS;
+ pProp->Handle = i;
+ pProp->Type = cppu::UnoType<css::uno::Sequence< OUString >>::get();
+ pProp->Attributes = css::beans::PropertyAttribute::BOUND |
+ css::beans::PropertyAttribute::READONLY;
+ ++i;
+
+ pProp = &(plPropDesc[i]);
+ pProp->Name = rPath.sPathName+POSTFIX_USER_PATHS;
+ pProp->Handle = i;
+ pProp->Type = cppu::UnoType<css::uno::Sequence< OUString >>::get();
+ pProp->Attributes = css::beans::PropertyAttribute::BOUND;
+ if (rPath.bIsReadonly)
+ pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
+ ++i;
+
+ pProp = &(plPropDesc[i]);
+ pProp->Name = rPath.sPathName+POSTFIX_WRITE_PATH;
+ pProp->Handle = i;
+ pProp->Type = cppu::UnoType<OUString>::get();
+ pProp->Attributes = css::beans::PropertyAttribute::BOUND;
+ if (rPath.bIsReadonly)
+ pProp->Attributes |= css::beans::PropertyAttribute::READONLY;
+ ++i;
+ }
+
+ m_pPropHelp.reset(new ::cppu::OPropertyArrayHelper(m_lPropDesc, false)); // false => not sorted ... must be done inside helper
+
+ // <- SAFE
+}
+
+css::uno::Any PathSettings::impl_getPathValue(sal_Int32 nID) const
+{
+ const PathSettings::PathInfo* pPath = impl_getPathAccessConst(nID);
+ if (! pPath)
+ throw css::lang::IllegalArgumentException();
+
+ css::uno::Any aVal;
+ switch(impl_getPropGroup(nID))
+ {
+ case IDGROUP_OLDSTYLE :
+ {
+ OUString sVal = impl_convertPath2OldStyle(*pPath);
+ aVal <<= sVal;
+ }
+ break;
+
+ case IDGROUP_INTERNAL_PATHS :
+ {
+ aVal <<= comphelper::containerToSequence(pPath->lInternalPaths);
+ }
+ break;
+
+ case IDGROUP_USER_PATHS :
+ {
+ aVal <<= comphelper::containerToSequence(pPath->lUserPaths);
+ }
+ break;
+
+ case IDGROUP_WRITE_PATH :
+ {
+ aVal <<= pPath->sWritePath;
+ }
+ break;
+ }
+
+ return aVal;
+}
+
+void PathSettings::impl_setPathValue( sal_Int32 nID ,
+ const css::uno::Any& aVal)
+{
+ PathSettings::PathInfo* pOrgPath = impl_getPathAccess(nID);
+ if (! pOrgPath)
+ throw css::container::NoSuchElementException();
+
+ // We work on a copied path ... so we can be sure that errors during this operation
+ // does not make our internal cache invalid .-)
+ PathSettings::PathInfo aChangePath(*pOrgPath);
+
+ switch(impl_getPropGroup(nID))
+ {
+ case IDGROUP_OLDSTYLE :
+ {
+ OUString sVal;
+ aVal >>= sVal;
+ std::vector<OUString> lList = impl_convertOldStyle2Path(sVal);
+ impl_subst(lList, fa_getSubstitution(), false);
+ impl_purgeKnownPaths(aChangePath, lList);
+ if (! impl_isValidPath(lList))
+ throw css::lang::IllegalArgumentException();
+
+ if (aChangePath.bIsSinglePath)
+ {
+ SAL_WARN_IF(lList.size()>1, "fwk", "PathSettings::impl_setPathValue(): You try to set more than path value for a defined SINGLE_PATH!");
+ if ( !lList.empty() )
+ aChangePath.sWritePath = *(lList.begin());
+ else
+ aChangePath.sWritePath.clear();
+ }
+ else
+ {
+ for (auto const& elem : lList)
+ {
+ aChangePath.lUserPaths.push_back(elem);
+ }
+ }
+ }
+ break;
+
+ case IDGROUP_INTERNAL_PATHS :
+ {
+ if (aChangePath.bIsSinglePath)
+ {
+ throw css::uno::Exception(
+ "The path '" + aChangePath.sPathName
+ + "' is defined as SINGLE_PATH. It's sub set of internal paths can't be set.",
+ static_cast< ::cppu::OWeakObject* >(this));
+ }
+
+ css::uno::Sequence<OUString> lTmpList;
+ aVal >>= lTmpList;
+ std::vector<OUString> lList = comphelper::sequenceToContainer<std::vector<OUString>>(lTmpList);
+ if (! impl_isValidPath(lList))
+ throw css::lang::IllegalArgumentException();
+ aChangePath.lInternalPaths = lList;
+ }
+ break;
+
+ case IDGROUP_USER_PATHS :
+ {
+ if (aChangePath.bIsSinglePath)
+ {
+ throw css::uno::Exception(
+ "The path '" + aChangePath.sPathName
+ + "' is defined as SINGLE_PATH. It's sub set of internal paths can't be set.",
+ static_cast< ::cppu::OWeakObject* >(this));
+ }
+
+ css::uno::Sequence<OUString> lTmpList;
+ aVal >>= lTmpList;
+ std::vector<OUString> lList = comphelper::sequenceToContainer<std::vector<OUString>>(lTmpList);
+ if (! impl_isValidPath(lList))
+ throw css::lang::IllegalArgumentException();
+ aChangePath.lUserPaths = lList;
+ }
+ break;
+
+ case IDGROUP_WRITE_PATH :
+ {
+ OUString sVal;
+ aVal >>= sVal;
+ if (! impl_isValidPath(sVal))
+ throw css::lang::IllegalArgumentException();
+ aChangePath.sWritePath = sVal;
+ }
+ break;
+ }
+
+ // TODO check if path has at least one path value set
+ // At least it depends from the feature using this path, if an empty path list is allowed.
+
+ // first we should try to store the changed (copied!) path ...
+ // In case an error occurs on saving time an exception is thrown ...
+ // If no exception occurs we can update our internal cache (means
+ // we can overwrite pOrgPath !
+ impl_storePath(aChangePath);
+ *pOrgPath = std::move(aChangePath);
+}
+
+bool PathSettings::impl_isValidPath(const std::vector<OUString>& lPath) const
+{
+ for (auto const& path : lPath)
+ {
+ if (! impl_isValidPath(path))
+ return false;
+ }
+
+ return true;
+}
+
+bool PathSettings::impl_isValidPath(std::u16string_view sPath) const
+{
+ // allow empty path to reset a path.
+// idea by LLA to support empty paths
+// if (sPath.getLength() == 0)
+// {
+// return sal_True;
+// }
+
+ return (! INetURLObject(sPath).HasError());
+}
+
+OUString impl_extractBaseFromPropName(const OUString& sPropName)
+{
+ sal_Int32 i = sPropName.indexOf(POSTFIX_INTERNAL_PATHS);
+ if (i > -1)
+ return sPropName.copy(0, i);
+ i = sPropName.indexOf(POSTFIX_USER_PATHS);
+ if (i > -1)
+ return sPropName.copy(0, i);
+ i = sPropName.indexOf(POSTFIX_WRITE_PATH);
+ if (i > -1)
+ return sPropName.copy(0, i);
+
+ return sPropName;
+}
+
+PathSettings::PathInfo* PathSettings::impl_getPathAccess(sal_Int32 nHandle)
+{
+ // SAFE ->
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ if (nHandle > (m_lPropDesc.getLength()-1))
+ return nullptr;
+
+ const css::beans::Property& rProp = m_lPropDesc[nHandle];
+ OUString sProp = impl_extractBaseFromPropName(rProp.Name);
+ PathSettings::PathHash::iterator rPath = m_lPaths.find(sProp);
+
+ if (rPath != m_lPaths.end())
+ return &(rPath->second);
+
+ return nullptr;
+ // <- SAFE
+}
+
+const PathSettings::PathInfo* PathSettings::impl_getPathAccessConst(sal_Int32 nHandle) const
+{
+ // SAFE ->
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+
+ if (nHandle > (m_lPropDesc.getLength()-1))
+ return nullptr;
+
+ const css::beans::Property& rProp = m_lPropDesc[nHandle];
+ OUString sProp = impl_extractBaseFromPropName(rProp.Name);
+ PathSettings::PathHash::const_iterator rPath = m_lPaths.find(sProp);
+
+ if (rPath != m_lPaths.end())
+ return &(rPath->second);
+
+ return nullptr;
+ // <- SAFE
+}
+
+sal_Bool SAL_CALL PathSettings::convertFastPropertyValue( css::uno::Any& aConvertedValue,
+ css::uno::Any& aOldValue ,
+ sal_Int32 nHandle ,
+ const css::uno::Any& aValue )
+{
+ // throws NoSuchElementException !
+ css::uno::Any aCurrentVal = impl_getPathValue(nHandle);
+
+ return PropHelper::willPropertyBeChanged(
+ aCurrentVal,
+ aValue,
+ aOldValue,
+ aConvertedValue);
+}
+
+void SAL_CALL PathSettings::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle,
+ const css::uno::Any& aValue )
+{
+ // throws NoSuchElement- and IllegalArgumentException !
+ impl_setPathValue(nHandle, aValue);
+}
+
+void SAL_CALL PathSettings::getFastPropertyValue(css::uno::Any& aValue ,
+ sal_Int32 nHandle) const
+{
+ aValue = impl_getPathValue(nHandle);
+}
+
+::cppu::IPropertyArrayHelper& SAL_CALL PathSettings::getInfoHelper()
+{
+ return *m_pPropHelp;
+}
+
+css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL PathSettings::getPropertySetInfo()
+{
+ return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
+}
+
+css::uno::Reference< css::util::XStringSubstitution > PathSettings::fa_getSubstitution()
+{
+ css::uno::Reference< css::util::XStringSubstitution > xSubst;
+ { // SAFE ->
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ xSubst = m_xSubstitution;
+ }
+
+ if (! xSubst.is())
+ {
+ // create the needed substitution service.
+ // We must replace all used variables inside read path values.
+ // In case we can't do so... the whole office can't work really.
+ // That's why it seems to be OK to throw a RuntimeException then.
+ xSubst = css::util::PathSubstitution::create(m_xContext);
+
+ { // SAFE ->
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ m_xSubstitution = xSubst;
+ }
+ }
+
+ return xSubst;
+}
+
+css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgOld()
+{
+ css::uno::Reference< css::container::XNameAccess > xCfg;
+ { // SAFE ->
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ xCfg = m_xCfgOld;
+ } // <- SAFE
+
+ if (! xCfg.is())
+ {
+ xCfg.set( ::comphelper::ConfigurationHelper::openConfig(
+ m_xContext,
+ "org.openoffice.Office.Common/Path/Current",
+ ::comphelper::EConfigurationModes::Standard), // not readonly! Sometimes we need write access there !!!
+ css::uno::UNO_QUERY_THROW);
+
+ { // SAFE ->
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ m_xCfgOld = xCfg;
+ }
+ }
+
+ return xCfg;
+}
+
+css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgNew()
+{
+ css::uno::Reference< css::container::XNameAccess > xCfg;
+ { // SAFE ->
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ xCfg = m_xCfgNew;
+ } // <- SAFE
+
+ if (! xCfg.is())
+ {
+ xCfg.set( ::comphelper::ConfigurationHelper::openConfig(
+ m_xContext,
+ "org.openoffice.Office.Paths/Paths",
+ ::comphelper::EConfigurationModes::Standard),
+ css::uno::UNO_QUERY_THROW);
+
+ { // SAFE ->
+ osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
+ m_xCfgNew = xCfg;
+ m_xCfgNewListener = new WeakChangesListener(this);
+ }
+
+ css::uno::Reference< css::util::XChangesNotifier > xBroadcaster(xCfg, css::uno::UNO_QUERY_THROW);
+ xBroadcaster->addChangesListener(m_xCfgNewListener);
+ }
+
+ return xCfg;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_PathSettings_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ rtl::Reference<PathSettings> xPathSettings = new PathSettings(context);
+ // fill cache
+ xPathSettings->impl_readAll();
+
+ return cppu::acquire(xPathSettings.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/services/sessionlistener.cxx b/framework/source/services/sessionlistener.cxx
new file mode 100644
index 000000000..7ba5c82de
--- /dev/null
+++ b/framework/source/services/sessionlistener.cxx
@@ -0,0 +1,413 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/types.h>
+#include <sal/log.hxx>
+
+#include <framework/desktop.hxx>
+
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/frame/theAutoRecovery.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/FeatureStateEvent.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XSessionManagerListener2.hpp>
+#include <com/sun/star/frame/XSessionManagerClient.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <utility>
+
+using namespace css;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::util;
+using namespace com::sun::star::beans;
+using namespace framework;
+
+namespace {
+
+/// @HTML
+/** @short implements flat/deep detection of file/stream formats and provides
+ further read/write access to the global office type configuration.
+
+ @descr Using of this class makes it possible to get information about the
+ format type of a given URL or stream. The returned internal type name
+ can be used to get more information about this format. Further this
+ class provides full access to the configuration data and following
+ implementations will support some special query modes.
+
+ @docdate 10.03.2003 by as96863
+
+ @todo <ul>
+ <li>implementation of query mode</li>
+ <li>simple restore mechanism of last consistent cache state,
+ if flush failed</li>
+ </ul>
+ */
+typedef cppu::WeakImplHelper<
+ css::lang::XInitialization,
+ css::frame::XSessionManagerListener2,
+ css::frame::XStatusListener,
+ css::lang::XServiceInfo> SessionListener_BASE;
+
+class SessionListener : public SessionListener_BASE
+{
+private:
+ osl::Mutex m_aMutex;
+
+ /** reference to the uno service manager, which created this service.
+ It can be used to create own needed helper services. */
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ css::uno::Reference< css::frame::XSessionManagerClient > m_rSessionManager;
+
+ // restore handling
+ bool m_bRestored;
+
+ bool m_bSessionStoreRequested;
+
+ bool m_bAllowUserInteractionOnQuit;
+ bool m_bTerminated;
+
+ // in case of synchronous call the caller should do saveDone() call himself!
+ void StoreSession( bool bAsync );
+
+ // let session quietly close the documents, remove lock files, store configuration and etc.
+ void QuitSessionQuietly();
+
+public:
+ explicit SessionListener(css::uno::Reference< css::uno::XComponentContext > xContext);
+
+ virtual ~SessionListener() override;
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.frame.SessionListener";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.frame.SessionListener"};
+ }
+
+ virtual void SAL_CALL disposing(const css::lang::EventObject&) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any >& args) override;
+
+ // XSessionManagerListener
+ virtual void SAL_CALL doSave( sal_Bool bShutdown, sal_Bool bCancelable ) override;
+ virtual void SAL_CALL approveInteraction( sal_Bool bInteractionGranted ) override;
+ virtual void SAL_CALL shutdownCanceled() override;
+ virtual sal_Bool SAL_CALL doRestore() override;
+
+ // XSessionManagerListener2
+ virtual void SAL_CALL doQuit() override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& event) override;
+};
+
+SessionListener::SessionListener(css::uno::Reference< css::uno::XComponentContext > rxContext )
+ : m_xContext(std::move( rxContext ))
+ , m_bRestored( false )
+ , m_bSessionStoreRequested( false )
+ , m_bAllowUserInteractionOnQuit( false )
+ , m_bTerminated( false )
+{
+ SAL_INFO("fwk.session", "SessionListener::SessionListener");
+}
+
+SessionListener::~SessionListener()
+{
+ SAL_INFO("fwk.session", "SessionListener::~SessionListener");
+ if (m_rSessionManager.is())
+ {
+ css::uno::Reference< XSessionManagerListener> me(this);
+ m_rSessionManager->removeSessionManagerListener(me);
+ }
+}
+
+void SessionListener::StoreSession( bool bAsync )
+{
+ SAL_INFO("fwk.session", "SessionListener::StoreSession");
+ osl::MutexGuard g(m_aMutex);
+ try
+ {
+ // xd create SERVICENAME_AUTORECOVERY -> frame::XDispatch
+ // xd->dispatch("vnd.sun.star.autorecovery:/doSessionSave, async=bAsync
+ // on stop event m_rSessionManager->saveDone(this); in case of asynchronous call
+ // in case of synchronous call the caller should do saveDone() call himself!
+
+ css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext );
+ css::uno::Reference< XURLTransformer > xURLTransformer = URLTransformer::create( m_xContext );
+ URL aURL;
+ aURL.Complete = "vnd.sun.star.autorecovery:/doSessionSave";
+ xURLTransformer->parseStrict(aURL);
+
+ // in case of asynchronous call the notification will trigger saveDone()
+ if ( bAsync )
+ xDispatch->addStatusListener(this, aURL);
+
+ Sequence< PropertyValue > args{ PropertyValue("DispatchAsynchron",-1,Any(bAsync),
+ PropertyState_DIRECT_VALUE) };
+ xDispatch->dispatch(aURL, args);
+ } catch (const css::uno::Exception&) {
+ TOOLS_WARN_EXCEPTION("fwk.session", "");
+ // save failed, but tell manager to go on if we haven't yet dispatched the request
+ // in case of synchronous saving the notification is done by the caller
+ if ( bAsync && m_rSessionManager.is() )
+ m_rSessionManager->saveDone(this);
+ }
+}
+
+void SessionListener::QuitSessionQuietly()
+{
+ SAL_INFO("fwk.session", "SessionListener::QuitSessionQuietly");
+ osl::MutexGuard g(m_aMutex);
+ try
+ {
+ // xd create SERVICENAME_AUTORECOVERY -> frame::XDispatch
+ // xd->dispatch("vnd.sun.star.autorecovery:/doSessionQuietQuit, async=false
+ // it is done synchronously to avoid conflict with normal quit process
+
+ css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext );
+ css::uno::Reference< XURLTransformer > xURLTransformer = URLTransformer::create( m_xContext );
+ URL aURL;
+ aURL.Complete = "vnd.sun.star.autorecovery:/doSessionQuietQuit";
+ xURLTransformer->parseStrict(aURL);
+
+ Sequence< PropertyValue > args{ PropertyValue("DispatchAsynchron",-1,Any(false),
+ PropertyState_DIRECT_VALUE) };
+ xDispatch->dispatch(aURL, args);
+ } catch (const css::uno::Exception&) {
+ TOOLS_WARN_EXCEPTION("fwk.session", "");
+ }
+}
+
+void SAL_CALL SessionListener::disposing(const css::lang::EventObject& Source)
+{
+ SAL_INFO("fwk.session", "SessionListener::disposing");
+ if (Source.Source == m_rSessionManager) {
+ m_rSessionManager.clear();
+ }
+}
+
+void SAL_CALL SessionListener::initialize(const Sequence< Any >& args)
+{
+ SAL_INFO("fwk.session", "SessionListener::initialize");
+
+ OUString aSMgr("com.sun.star.frame.SessionManagerClient");
+ if ( (args.getLength() == 1) && (args[0] >>= m_bAllowUserInteractionOnQuit) )
+ ;// do nothing
+ else if (args.hasElements())
+ {
+ NamedValue v;
+ for (const Any& rArg : args)
+ {
+ if (rArg >>= v)
+ {
+ if ( v.Name == "SessionManagerName" )
+ v.Value >>= aSMgr;
+ else if ( v.Name == "SessionManager" )
+ v.Value >>= m_rSessionManager;
+ else if ( v.Name == "AllowUserInteractionOnQuit" )
+ v.Value >>= m_bAllowUserInteractionOnQuit;
+ }
+ }
+ }
+
+ SAL_INFO("fwk.session.debug", " m_bAllowUserInteractionOnQuit = " << (m_bAllowUserInteractionOnQuit ? "true" : "false"));
+ if (!m_rSessionManager.is())
+ m_rSessionManager = css::uno::Reference< frame::XSessionManagerClient >
+ (m_xContext->getServiceManager()->createInstanceWithContext(aSMgr, m_xContext), UNO_QUERY);
+
+ if (m_rSessionManager.is())
+ {
+ m_rSessionManager->addSessionManagerListener(this);
+ }
+}
+
+void SAL_CALL SessionListener::statusChanged(const frame::FeatureStateEvent& event)
+{
+ SAL_INFO("fwk.session", "SessionListener::statusChanged");
+
+ SAL_INFO("fwk.session.debug", " ev.Feature = " << event.FeatureURL.Complete <<
+ ", ev.Descript = " << event.FeatureDescriptor);
+ if ( event.FeatureURL.Complete == "vnd.sun.star.autorecovery:/doSessionRestore" )
+ {
+ if (event.FeatureDescriptor == "update")
+ m_bRestored = true; // a document was restored
+
+ }
+ else if ( event.FeatureURL.Complete == "vnd.sun.star.autorecovery:/doAutoSave" )
+ { // the "doSessionSave" was never set, look to framework/source/services/autorecovery.cxx
+ // it always testing but never setting (enum AutoRecovery::E_SESSION_SAVE)
+ if (event.FeatureDescriptor == "update")
+ {
+ if (m_rSessionManager.is())
+ m_rSessionManager->saveDone(this); // done with save
+ }
+ }
+}
+
+sal_Bool SAL_CALL SessionListener::doRestore()
+{
+ SAL_INFO("fwk.session", "SessionListener::doRestore");
+ osl::MutexGuard g(m_aMutex);
+ m_bRestored = false;
+ try {
+ css::uno::Reference< frame::XDispatch > xDispatch = css::frame::theAutoRecovery::get( m_xContext );
+
+ URL aURL;
+ aURL.Complete = "vnd.sun.star.autorecovery:/doSessionRestore";
+ css::uno::Reference< XURLTransformer > xURLTransformer(URLTransformer::create(m_xContext));
+ xURLTransformer->parseStrict(aURL);
+ Sequence< PropertyValue > args;
+ xDispatch->addStatusListener(this, aURL);
+ xDispatch->dispatch(aURL, args);
+ m_bRestored = true;
+
+ } catch (const css::uno::Exception&) {
+ TOOLS_WARN_EXCEPTION("fwk.session", "");
+ }
+
+ return m_bRestored;
+}
+
+void SAL_CALL SessionListener::doSave( sal_Bool bShutdown, sal_Bool /*bCancelable*/ )
+{
+ SAL_INFO("fwk.session", "SessionListener::doSave");
+
+ SAL_INFO("fwk.session.debug", " m_bAllowUserInteractionOnQuit = " << (m_bAllowUserInteractionOnQuit ? "true" : "false") <<
+ ", bShutdown = " << (bShutdown ? "true" : "false"));
+ if (bShutdown)
+ {
+ m_bSessionStoreRequested = true; // there is no need to protect it with mutex
+ if ( m_bAllowUserInteractionOnQuit && m_rSessionManager.is() )
+ m_rSessionManager->queryInteraction( static_cast< css::frame::XSessionManagerListener* >( this ) );
+ else
+ StoreSession( true );
+ }
+ // we don't have anything to do so tell the session manager we're done
+ else if( m_rSessionManager.is() )
+ m_rSessionManager->saveDone( this );
+}
+
+void SAL_CALL SessionListener::approveInteraction( sal_Bool bInteractionGranted )
+{
+ SAL_INFO("fwk.session", "SessionListener::approveInteraction");
+ // do AutoSave as the first step
+ osl::MutexGuard g(m_aMutex);
+
+ if ( bInteractionGranted )
+ {
+ // close the office documents in normal way
+ try
+ {
+ // first of all let the session be stored to be sure that we lose no information
+ StoreSession( false );
+
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext );
+ // honestly: how many implementations of XDesktop will we ever have?
+ // so casting this directly to the implementation
+ Desktop* pDesktop(dynamic_cast<Desktop*>(xDesktop.get()));
+ if(pDesktop)
+ {
+ SAL_INFO("fwk.session", " XDesktop is a framework::Desktop -- good.");
+ m_bTerminated = pDesktop->terminateQuickstarterToo();
+ }
+ else
+ {
+ SAL_WARN("fwk.session", " XDesktop is not a framework::Desktop -- this should never happen.");
+ m_bTerminated = xDesktop->terminate();
+ }
+
+ if ( m_rSessionManager.is() )
+ {
+ // false means that the application closing has been cancelled
+ if ( !m_bTerminated )
+ m_rSessionManager->cancelShutdown();
+ else
+ m_rSessionManager->interactionDone( this );
+ }
+ }
+ catch( const css::uno::Exception& )
+ {
+ StoreSession( true );
+ m_rSessionManager->interactionDone( this );
+ }
+
+ if ( m_rSessionManager.is() && m_bTerminated )
+ m_rSessionManager->saveDone(this);
+ }
+ else
+ {
+ StoreSession( true );
+ }
+}
+
+void SessionListener::shutdownCanceled()
+{
+ SAL_INFO("fwk.session", "SessionListener::shutdownCanceled");
+ // set the state back
+ m_bSessionStoreRequested = false; // there is no need to protect it with mutex
+
+ if ( m_rSessionManager.is() )
+ m_rSessionManager->saveDone(this);
+}
+
+void SessionListener::doQuit()
+{
+ SAL_INFO("fwk.session", "SessionListener::doQuit");
+ if ( m_bSessionStoreRequested && !m_bTerminated )
+ {
+ // let the session be closed quietly in this case
+ QuitSessionQuietly();
+ }
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_frame_SessionListener_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ SAL_INFO("fwk.session", "com_sun_star_comp_frame_SessionListener_get_implementation");
+
+ return cppu::acquire(new SessionListener(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/services/substitutepathvars.cxx b/framework/source/services/substitutepathvars.cxx
new file mode 100644
index 000000000..b57523306
--- /dev/null
+++ b/framework/source/services/substitutepathvars.cxx
@@ -0,0 +1,695 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_folders.h>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <unotools/bootstrap.hxx>
+#include <unotools/configmgr.hxx>
+#include <osl/file.hxx>
+#include <osl/security.hxx>
+#include <osl/thread.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <tools/urlobj.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/bootstrap.hxx>
+
+#include <officecfg/Office/Paths.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/util/XStringSubstitution.hpp>
+
+#include <unordered_map>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::container;
+
+namespace {
+
+enum PreDefVariable
+{
+ PREDEFVAR_INST,
+ PREDEFVAR_PROG,
+ PREDEFVAR_USER,
+ PREDEFVAR_WORK,
+ PREDEFVAR_HOME,
+ PREDEFVAR_TEMP,
+ PREDEFVAR_PATH,
+ PREDEFVAR_USERNAME,
+ PREDEFVAR_LANGID,
+ PREDEFVAR_VLANG,
+ PREDEFVAR_INSTPATH,
+ PREDEFVAR_PROGPATH,
+ PREDEFVAR_USERPATH,
+ PREDEFVAR_INSTURL,
+ PREDEFVAR_PROGURL,
+ PREDEFVAR_USERURL,
+ PREDEFVAR_WORKDIRURL,
+ // New variable of hierarchy service (#i32656#)
+ PREDEFVAR_BASEINSTURL,
+ PREDEFVAR_USERDATAURL,
+ PREDEFVAR_BRANDBASEURL,
+ PREDEFVAR_COUNT
+};
+
+struct FixedVariable
+{
+ const char* pVarName;
+ bool bAbsPath;
+};
+
+// Table with all fixed/predefined variables supported.
+const FixedVariable aFixedVarTable[PREDEFVAR_COUNT] =
+{
+ { "$(inst)", true }, // PREDEFVAR_INST
+ { "$(prog)", true }, // PREDEFVAR_PROG
+ { "$(user)", true }, // PREDEFVAR_USER
+ { "$(work)", true }, // PREDEFVAR_WORK, special variable
+ // (transient)
+ { "$(home)", true }, // PREDEFVAR_HOME
+ { "$(temp)", true }, // PREDEFVAR_TEMP
+ { "$(path)", true }, // PREDEFVAR_PATH
+ { "$(username)", false }, // PREDEFVAR_USERNAME
+ { "$(langid)", false }, // PREDEFVAR_LANGID
+ { "$(vlang)", false }, // PREDEFVAR_VLANG
+ { "$(instpath)", true }, // PREDEFVAR_INSTPATH
+ { "$(progpath)", true }, // PREDEFVAR_PROGPATH
+ { "$(userpath)", true }, // PREDEFVAR_USERPATH
+ { "$(insturl)", true }, // PREDEFVAR_INSTURL
+ { "$(progurl)", true }, // PREDEFVAR_PROGURL
+ { "$(userurl)", true }, // PREDEFVAR_USERURL
+ { "$(workdirurl)", true }, // PREDEFVAR_WORKDIRURL, special variable
+ // (transient) and don't use for
+ // resubstitution
+ { "$(baseinsturl)", true }, // PREDEFVAR_BASEINSTURL
+ { "$(userdataurl)", true }, // PREDEFVAR_USERDATAURL
+ { "$(brandbaseurl)", true } // PREDEFVAR_BRANDBASEURL
+};
+
+struct PredefinedPathVariables
+{
+ // Predefined variables supported by substitute variables
+ LanguageType m_eLanguageType; // Language type of Office
+ OUString m_FixedVar[ PREDEFVAR_COUNT ]; // Variable value access by PreDefVariable
+ OUString m_FixedVarNames[ PREDEFVAR_COUNT ]; // Variable name access by PreDefVariable
+};
+
+struct ReSubstFixedVarOrder
+{
+ sal_Int32 nVarValueLength;
+ PreDefVariable eVariable;
+
+ bool operator< ( const ReSubstFixedVarOrder& aFixedVarOrder ) const
+ {
+ // Reverse operator< to have high to low ordering
+ return ( nVarValueLength > aFixedVarOrder.nVarValueLength );
+ }
+};
+
+typedef comphelper::WeakComponentImplHelper<
+ css::util::XStringSubstitution,
+ css::lang::XServiceInfo > SubstitutePathVariables_BASE;
+
+class SubstitutePathVariables : public SubstitutePathVariables_BASE
+{
+public:
+ explicit SubstitutePathVariables();
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.PathSubstitution";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.util.PathSubstitution"};
+ }
+
+ // XStringSubstitution
+ virtual OUString SAL_CALL substituteVariables( const OUString& aText, sal_Bool bSubstRequired ) override;
+ virtual OUString SAL_CALL reSubstituteVariables( const OUString& aText ) override;
+ virtual OUString SAL_CALL getSubstituteVariableValue( const OUString& variable ) override;
+
+protected:
+ void SetPredefinedPathVariables();
+
+ // Special case (transient) values can change during runtime!
+ // Don't store them in the pre defined struct
+ OUString GetWorkPath() const;
+ OUString GetWorkVariableValue() const;
+ OUString GetPathVariableValue() const;
+
+ OUString GetHomeVariableValue() const;
+
+ // XStringSubstitution implementation methods
+ /// @throws css::container::NoSuchElementException
+ /// @throws css::uno::RuntimeException
+ OUString impl_substituteVariable( const OUString& aText, bool bSustRequired );
+ /// @throws css::uno::RuntimeException
+ OUString impl_reSubstituteVariables( const OUString& aText );
+ /// @throws css::container::NoSuchElementException
+ /// @throws css::uno::RuntimeException
+ OUString const & impl_getSubstituteVariableValue( const OUString& variable );
+
+private:
+ typedef std::unordered_map<OUString, PreDefVariable>
+ VarNameToIndexMap;
+
+ VarNameToIndexMap m_aPreDefVarMap; // Mapping from pre-def variable names to enum for array access
+ PredefinedPathVariables m_aPreDefVars; // All predefined variables
+ std::vector<ReSubstFixedVarOrder> m_aReSubstFixedVarOrder; // To speed up resubstitution fixed variables (order for lookup)
+};
+
+SubstitutePathVariables::SubstitutePathVariables()
+{
+ SetPredefinedPathVariables();
+
+ // Init the predefined/fixed variable to index hash map
+ for ( int i = 0; i < PREDEFVAR_COUNT; i++ )
+ {
+ // Store variable name into struct of predefined/fixed variables
+ m_aPreDefVars.m_FixedVarNames[i] = OUString::createFromAscii( aFixedVarTable[i].pVarName );
+
+ // Create hash map entry
+ m_aPreDefVarMap.emplace( m_aPreDefVars.m_FixedVarNames[i], PreDefVariable(i) );
+ }
+
+ // Sort predefined/fixed variable to path length
+ for ( int i = 0; i < PREDEFVAR_COUNT; i++ )
+ {
+ if (( i != PREDEFVAR_WORKDIRURL ) && ( i != PREDEFVAR_PATH ))
+ {
+ // Special path variables, don't include into automatic resubstitution search!
+ // $(workdirurl) is not allowed to resubstitute! This variable is the value of path settings entry
+ // and it could be possible that it will be resubstituted by itself!!
+ // Example: WORK_PATH=c:\test, $(workdirurl)=WORK_PATH => WORK_PATH=$(workdirurl) and this cannot be substituted!
+ ReSubstFixedVarOrder aFixedVar;
+ aFixedVar.eVariable = PreDefVariable(i);
+ aFixedVar.nVarValueLength = m_aPreDefVars.m_FixedVar[static_cast<sal_Int32>(aFixedVar.eVariable)].getLength();
+ m_aReSubstFixedVarOrder.push_back( aFixedVar );
+ }
+ }
+ sort(m_aReSubstFixedVarOrder.begin(),m_aReSubstFixedVarOrder.end());
+}
+
+// XStringSubstitution
+OUString SAL_CALL SubstitutePathVariables::substituteVariables( const OUString& aText, sal_Bool bSubstRequired )
+{
+ std::unique_lock g(m_aMutex);
+ return impl_substituteVariable( aText, bSubstRequired );
+}
+
+OUString SAL_CALL SubstitutePathVariables::reSubstituteVariables( const OUString& aText )
+{
+ std::unique_lock g(m_aMutex);
+ return impl_reSubstituteVariables( aText );
+}
+
+OUString SAL_CALL SubstitutePathVariables::getSubstituteVariableValue( const OUString& aVariable )
+{
+ std::unique_lock g(m_aMutex);
+ return impl_getSubstituteVariableValue( aVariable );
+}
+
+OUString SubstitutePathVariables::GetWorkPath() const
+{
+ OUString aWorkPath;
+ css::uno::Reference< css::container::XHierarchicalNameAccess > xPaths(officecfg::Office::Paths::Paths::get(), css::uno::UNO_QUERY_THROW);
+ if (!(xPaths->getByHierarchicalName("['Work']/WritePath") >>= aWorkPath))
+ // fallback in case config layer does not return a usable work dir value.
+ aWorkPath = GetWorkVariableValue();
+
+ return aWorkPath;
+}
+
+OUString SubstitutePathVariables::GetWorkVariableValue() const
+{
+ OUString aWorkPath;
+ std::optional<OUString> x(officecfg::Office::Paths::Variables::Work::get());
+ if (!x)
+ {
+ // fallback to $HOME in case platform dependent config layer does not return
+ // a usable work dir value.
+ osl::Security aSecurity;
+ aSecurity.getHomeDir( aWorkPath );
+ }
+ else
+ aWorkPath = *x;
+ return aWorkPath;
+}
+
+OUString SubstitutePathVariables::GetHomeVariableValue() const
+{
+ osl::Security aSecurity;
+ OUString aHomePath;
+
+ aSecurity.getHomeDir( aHomePath );
+ return aHomePath;
+}
+
+OUString SubstitutePathVariables::GetPathVariableValue() const
+{
+ OUString aRetStr;
+ const char* pEnv = getenv( "PATH" );
+
+ if ( pEnv )
+ {
+ const int PATH_EXTEND_FACTOR = 200;
+ OUString aTmp;
+ OUString aPathList( pEnv, strlen( pEnv ), osl_getThreadTextEncoding() );
+ OUStringBuffer aPathStrBuffer( aPathList.getLength() * PATH_EXTEND_FACTOR / 100 );
+
+ bool bAppendSep = false;
+ sal_Int32 nToken = 0;
+ do
+ {
+ OUString sToken = aPathList.getToken(0, SAL_PATHSEPARATOR, nToken);
+ if (!sToken.isEmpty() &&
+ osl::FileBase::getFileURLFromSystemPath( sToken, aTmp ) ==
+ osl::FileBase::RC::E_None )
+ {
+ if ( bAppendSep )
+ aPathStrBuffer.append( ";" ); // Office uses ';' as path separator
+ aPathStrBuffer.append( aTmp );
+ bAppendSep = true;
+ }
+ }
+ while(nToken>=0);
+
+ aRetStr = aPathStrBuffer.makeStringAndClear();
+ }
+
+ return aRetStr;
+}
+
+OUString SubstitutePathVariables::impl_substituteVariable( const OUString& rText, bool bSubstRequired )
+{
+ // This is maximal recursive depth supported!
+ const sal_Int32 nMaxRecursiveDepth = 8;
+
+ OUString aWorkText = rText;
+ OUString aResult;
+
+ // Use vector with strings to detect endless recursions!
+ std::vector< OUString > aEndlessRecursiveDetector;
+
+ // Search for first occurrence of "$(...".
+ sal_Int32 nDepth = 0;
+ bool bSubstitutionCompleted = false;
+ sal_Int32 nPosition = aWorkText.indexOf( "$(" );
+ sal_Int32 nLength = 0; // = count of letters from "$(" to ")" in string
+ bool bVarNotSubstituted = false;
+
+ // Have we found any variable like "$(...)"?
+ if ( nPosition != -1 )
+ {
+ // Yes; Get length of found variable.
+ // If no ")" was found - nLength is set to 0 by default! see before.
+ sal_Int32 nEndPosition = aWorkText.indexOf( ')', nPosition );
+ if ( nEndPosition != -1 )
+ nLength = nEndPosition - nPosition + 1;
+ }
+
+ // Is there something to replace ?
+ bool bWorkRetrieved = false;
+ bool bWorkDirURLRetrieved = false;
+ while (nDepth < nMaxRecursiveDepth)
+ {
+ while ( ( nPosition != -1 ) && ( nLength > 3 ) ) // "$(" ")"
+ {
+ // YES; Get the next variable for replace.
+ sal_Int32 nReplaceLength = 0;
+ OUString aReplacement;
+ OUString aSubString = aWorkText.copy( nPosition, nLength );
+
+ // Path variables are not case sensitive!
+ OUString aSubVarString = aSubString.toAsciiLowerCase();
+ VarNameToIndexMap::const_iterator pNTOIIter = m_aPreDefVarMap.find( aSubVarString );
+ if ( pNTOIIter != m_aPreDefVarMap.end() )
+ {
+ // Fixed/Predefined variable found
+ PreDefVariable nIndex = pNTOIIter->second;
+
+ // Determine variable value and length from array/table
+ if ( nIndex == PREDEFVAR_WORK && !bWorkRetrieved )
+ {
+ // Transient value, retrieve it again
+ m_aPreDefVars.m_FixedVar[ nIndex ] = GetWorkVariableValue();
+ bWorkRetrieved = true;
+ }
+ else if ( nIndex == PREDEFVAR_WORKDIRURL && !bWorkDirURLRetrieved )
+ {
+ // Transient value, retrieve it again
+ m_aPreDefVars.m_FixedVar[ nIndex ] = GetWorkPath();
+ bWorkDirURLRetrieved = true;
+ }
+
+ // Check preconditions to substitute path variables.
+ // 1. A path variable can only be substituted if it follows a ';'!
+ // 2. It's located exactly at the start of the string being substituted!
+ if (( aFixedVarTable[ int( nIndex ) ].bAbsPath && (( nPosition == 0 ) || (( nPosition > 0 ) && ( aWorkText[nPosition-1] == ';')))) ||
+ ( !aFixedVarTable[ int( nIndex ) ].bAbsPath ))
+ {
+ aReplacement = m_aPreDefVars.m_FixedVar[ nIndex ];
+ nReplaceLength = nLength;
+ }
+ }
+
+ // Have we found something to replace?
+ if ( nReplaceLength > 0 )
+ {
+ // Yes ... then do it.
+ aWorkText = aWorkText.replaceAt( nPosition, nReplaceLength, aReplacement );
+ }
+ else
+ {
+ // Variable not known
+ bVarNotSubstituted = true;
+ nPosition += nLength;
+ }
+
+ // Step after replaced text! If no text was replaced (unknown variable!),
+ // length of aReplacement is 0 ... and we don't step then.
+ nPosition += aReplacement.getLength();
+
+ // We must control index in string before call something at OUString!
+ // The OUString-implementation don't do it for us :-( but the result is not defined otherwise.
+ if ( nPosition + 1 > aWorkText.getLength() )
+ {
+ // Position is out of range. Break loop!
+ nPosition = -1;
+ nLength = 0;
+ }
+ else
+ {
+ // Else; Position is valid. Search for next variable to replace.
+ nPosition = aWorkText.indexOf( "$(", nPosition );
+ // Have we found any variable like "$(...)"?
+ if ( nPosition != -1 )
+ {
+ // Yes; Get length of found variable. If no ")" was found - nLength must set to 0!
+ nLength = 0;
+ sal_Int32 nEndPosition = aWorkText.indexOf( ')', nPosition );
+ if ( nEndPosition != -1 )
+ nLength = nEndPosition - nPosition + 1;
+ }
+ }
+ }
+
+ nPosition = aWorkText.indexOf( "$(" );
+ if ( nPosition == -1 )
+ {
+ bSubstitutionCompleted = true;
+ break; // All variables are substituted
+ }
+ else
+ {
+ // Check for recursion
+ const sal_uInt32 nCount = aEndlessRecursiveDetector.size();
+ for ( sal_uInt32 i=0; i < nCount; i++ )
+ {
+ if ( aEndlessRecursiveDetector[i] == aWorkText )
+ {
+ if ( bVarNotSubstituted )
+ break; // Not all variables could be substituted!
+ else
+ {
+ nDepth = nMaxRecursiveDepth;
+ break; // Recursion detected!
+ }
+ }
+ }
+
+ aEndlessRecursiveDetector.push_back( aWorkText );
+
+ // Initialize values for next
+ sal_Int32 nEndPosition = aWorkText.indexOf( ')', nPosition );
+ if ( nEndPosition != -1 )
+ nLength = nEndPosition - nPosition + 1;
+ bVarNotSubstituted = false;
+ ++nDepth;
+ }
+ }
+
+ // Fill return value with result
+ if ( bSubstitutionCompleted )
+ {
+ // Substitution successful!
+ aResult = aWorkText;
+ }
+ else
+ {
+ // Substitution not successful!
+ if ( nDepth == nMaxRecursiveDepth )
+ {
+ // recursion depth reached!
+ if ( bSubstRequired )
+ {
+ throw NoSuchElementException( "Endless recursion detected. Cannot substitute variables!", static_cast<cppu::OWeakObject *>(this) );
+ }
+ aResult = rText;
+ }
+ else
+ {
+ // variable in text but unknown!
+ if ( bSubstRequired )
+ {
+ throw NoSuchElementException( "Unknown variable found!", static_cast<cppu::OWeakObject *>(this) );
+ }
+ aResult = aWorkText;
+ }
+ }
+
+ return aResult;
+}
+
+OUString SubstitutePathVariables::impl_reSubstituteVariables( const OUString& rURL )
+{
+ OUString aURL;
+
+ INetURLObject aUrl( rURL );
+ if ( !aUrl.HasError() )
+ aURL = aUrl.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ else
+ {
+ // Convert a system path to a UCB compliant URL before resubstitution
+ OUString aTemp;
+ if ( osl::FileBase::getFileURLFromSystemPath( rURL, aTemp ) == osl::FileBase::E_None )
+ {
+ aURL = INetURLObject( aTemp ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ if( aURL.isEmpty() )
+ return rURL;
+ }
+ else
+ {
+ // rURL is not a valid URL nor a osl system path. Give up and return error!
+ return rURL;
+ }
+ }
+
+ // Get transient predefined path variable $(work) value before starting resubstitution
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORK ] = GetWorkVariableValue();
+
+ for (;;)
+ {
+ bool bVariableFound = false;
+
+ for (auto const & i: m_aReSubstFixedVarOrder)
+ {
+ OUString aValue = m_aPreDefVars.m_FixedVar[i.eVariable];
+ sal_Int32 nPos = aURL.indexOf( aValue );
+ if ( nPos >= 0 )
+ {
+ bool bMatch = true;
+ if ( !aFixedVarTable[i.eVariable].bAbsPath )
+ {
+ // Special path variables as they can occur in the middle of a path. Only match if they
+ // describe a whole directory and not only a substring of a directory!
+ // (Ideally, all substitutions should stick to syntactical
+ // boundaries within the given URL, like not covering only
+ // part of a URL path segment; however, at least when saving
+ // an Impress document, one URL that is passed in is of the
+ // form <file:///.../share/palette%3Bfile:///.../user/
+ // config/standard.sob>, re-substituted to
+ // <$(inst)/share/palette%3B$(user)/config/standard.sob>.)
+ const sal_Unicode* pStr = aURL.getStr();
+
+ if ( nPos > 0 )
+ bMatch = ( aURL[ nPos-1 ] == '/' );
+
+ if ( bMatch )
+ {
+ if ( nPos + aValue.getLength() < aURL.getLength() )
+ bMatch = ( pStr[ nPos + aValue.getLength() ] == '/' );
+ }
+ }
+
+ if ( bMatch )
+ {
+ aURL = aURL.replaceAt(
+ nPos, aValue.getLength(),
+ m_aPreDefVars.m_FixedVarNames[i.eVariable]);
+ bVariableFound = true; // Resubstitution not finished yet!
+ break;
+ }
+ }
+ }
+
+ if ( !bVariableFound )
+ {
+ return aURL;
+ }
+ }
+}
+
+// This method support both request schemes "$("<varname>")" or "<varname>".
+OUString const & SubstitutePathVariables::impl_getSubstituteVariableValue( const OUString& rVariable )
+{
+ OUString aVariable;
+
+ sal_Int32 nPos = rVariable.indexOf( "$(" );
+ if ( nPos == -1 )
+ {
+ // Prepare variable name before hash map access
+ aVariable = "$(" + rVariable + ")";
+ }
+
+ VarNameToIndexMap::const_iterator pNTOIIter = m_aPreDefVarMap.find( ( nPos == -1 ) ? aVariable : rVariable );
+
+ // Fixed/Predefined variable
+ if ( pNTOIIter == m_aPreDefVarMap.end() )
+ {
+ throw NoSuchElementException("Unknown variable!", static_cast<cppu::OWeakObject *>(this));
+ }
+ PreDefVariable nIndex = pNTOIIter->second;
+ return m_aPreDefVars.m_FixedVar[static_cast<sal_Int32>(nIndex)];
+}
+
+void SubstitutePathVariables::SetPredefinedPathVariables()
+{
+
+ m_aPreDefVars.m_FixedVar[PREDEFVAR_BRANDBASEURL] = "$BRAND_BASE_DIR";
+ rtl::Bootstrap::expandMacros(
+ m_aPreDefVars.m_FixedVar[PREDEFVAR_BRANDBASEURL]);
+
+ // Get inspath and userpath from bootstrap mechanism in every case as file URL
+ ::utl::Bootstrap::PathStatus aState;
+ OUString sVal;
+
+ aState = utl::Bootstrap::locateUserData( sVal );
+ //There can be the valid case that there is no user installation.
+ //TODO: Is that still the case? (With OOo 3.4, "unopkg sync" was run as part
+ // of the setup. Then no user installation was required.)
+ //Therefore we do not assert here.
+ // It's not possible to detect when an empty value would actually be used.
+ // (note: getenv is a hack to detect if we're running in a unit test)
+ // Also, it's okay to have an empty user installation path in case of LOK
+ if (aState == ::utl::Bootstrap::PATH_EXISTS || getenv("SRC_ROOT") ||
+ (comphelper::LibreOfficeKit::isActive() && aState == ::utl::Bootstrap::PATH_VALID))
+ {
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ] = sVal;
+ }
+
+ // Set $(inst), $(instpath), $(insturl)
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ] = m_aPreDefVars.m_FixedVar[PREDEFVAR_BRANDBASEURL];
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTURL ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ];
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_INST ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ];
+ // New variable of hierarchy service (#i32656#)
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_BASEINSTURL ]= m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ];
+
+ // Set $(user), $(userpath), $(userurl)
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERURL ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ];
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_USER ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ];
+ // New variable of hierarchy service (#i32656#)
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERDATAURL ]= m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ];
+
+ // Detect the program directory
+ // Set $(prog), $(progpath), $(progurl)
+ INetURLObject aProgObj(
+ m_aPreDefVars.m_FixedVar[PREDEFVAR_BRANDBASEURL] );
+ if ( !aProgObj.HasError() && aProgObj.insertName( u"" LIBO_BIN_FOLDER ) )
+ {
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROGPATH ] = aProgObj.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROGURL ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROGPATH ];
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROG ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_PROGPATH ];
+ }
+
+ // Set $(username)
+ OUString aSystemUser;
+ ::osl::Security aSecurity;
+ aSecurity.getUserName( aSystemUser, false );
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERNAME ] = aSystemUser;
+
+ // Detect the language type of the current office
+ m_aPreDefVars.m_eLanguageType = LANGUAGE_ENGLISH_US;
+ OUString aLocaleStr( utl::ConfigManager::getUILocale() );
+ m_aPreDefVars.m_eLanguageType = LanguageTag::convertToLanguageTypeWithFallback( aLocaleStr );
+ // We used to have an else branch here with a SAL_WARN, but that
+ // always fired in some unit tests when this code was built with
+ // debug=t, so it seems fairly pointless, especially as
+ // m_aPreDefVars.m_eLanguageType has been initialized to a
+ // default value above anyway.
+
+ // Set $(vlang)
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_VLANG ] = aLocaleStr;
+
+ // Set $(langid)
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_LANGID ] = OUString::number( static_cast<sal_uInt16>(m_aPreDefVars.m_eLanguageType) );
+
+ // Set the other pre defined path variables
+ // Set $(work)
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORK ] = GetWorkVariableValue();
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_HOME ] = GetHomeVariableValue();
+
+ // Set $(workdirurl) this is the value of the path PATH_WORK which doesn't make sense
+ // anymore because the path settings service has this value! It can deliver this value more
+ // quickly than the substitution service!
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORKDIRURL ] = GetWorkPath();
+
+ // Set $(path) variable
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_PATH ] = GetPathVariableValue();
+
+ // Set $(temp)
+ OUString aTmp;
+ osl::FileBase::getTempDirURL( aTmp );
+ m_aPreDefVars.m_FixedVar[ PREDEFVAR_TEMP ] = aTmp;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_PathSubstitution_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SubstitutePathVariables());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/services/taskcreatorsrv.cxx b/framework/source/services/taskcreatorsrv.cxx
new file mode 100644
index 000000000..12e7c2c34
--- /dev/null
+++ b/framework/source/services/taskcreatorsrv.cxx
@@ -0,0 +1,357 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/persistentwindowstate.hxx>
+#include <helper/tagwindowasmodified.hxx>
+#include <helper/titlebarupdate.hxx>
+#include <loadenv/targethelper.hxx>
+#include <taskcreatordefs.hxx>
+
+#include <com/sun/star/frame/Frame.hpp>
+#include <com/sun/star/frame/XFrame2.hpp>
+#include <com/sun/star/frame/XDesktop.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/awt/Toolkit.hpp>
+#include <com/sun/star/awt/WindowDescriptor.hpp>
+#include <com/sun/star/awt/WindowAttribute.hpp>
+#include <com/sun/star/awt/VclWindowPeerAttribute.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <svtools/colorcfg.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+
+using namespace framework;
+
+namespace {
+
+typedef comphelper::WeakComponentImplHelper<
+ css::lang::XServiceInfo,
+ css::lang::XSingleServiceFactory> TaskCreatorService_BASE;
+
+class TaskCreatorService : public TaskCreatorService_BASE
+{
+private:
+
+ /** @short the global uno service manager.
+ @descr Must be used to create own needed services.
+ */
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+public:
+
+ explicit TaskCreatorService(css::uno::Reference< css::uno::XComponentContext > xContext);
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.TaskCreator";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.frame.TaskCreator"};
+ }
+
+ // XSingleServiceFactory
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance() override;
+
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments(const css::uno::Sequence< css::uno::Any >& lArguments) override;
+
+private:
+
+ css::uno::Reference< css::awt::XWindow > implts_createContainerWindow( const css::uno::Reference< css::awt::XWindow >& xParentWindow ,
+ const css::awt::Rectangle& aPosSize ,
+ bool bTopWindow );
+
+ void implts_applyDocStyleToWindow(const css::uno::Reference< css::awt::XWindow >& xWindow) const;
+
+ css::uno::Reference< css::frame::XFrame2 > implts_createFrame( const css::uno::Reference< css::frame::XFrame >& xParentFrame ,
+ const css::uno::Reference< css::awt::XWindow >& xContainerWindow ,
+ const OUString& sName );
+
+ void implts_establishWindowStateListener( const css::uno::Reference< css::frame::XFrame2 >& xFrame );
+ void implts_establishTitleBarUpdate( const css::uno::Reference< css::frame::XFrame2 >& xFrame );
+
+ void implts_establishDocModifyListener( const css::uno::Reference< css::frame::XFrame2 >& xFrame );
+
+ OUString impl_filterNames( const OUString& sName );
+};
+
+TaskCreatorService::TaskCreatorService(css::uno::Reference< css::uno::XComponentContext > xContext)
+ : m_xContext (std::move(xContext ))
+{
+}
+
+css::uno::Reference< css::uno::XInterface > SAL_CALL TaskCreatorService::createInstance()
+{
+ return createInstanceWithArguments(css::uno::Sequence< css::uno::Any >());
+}
+
+css::uno::Reference< css::uno::XInterface > SAL_CALL TaskCreatorService::createInstanceWithArguments(const css::uno::Sequence< css::uno::Any >& lArguments)
+{
+ ::comphelper::SequenceAsHashMap lArgs(lArguments);
+
+ css::uno::Reference< css::frame::XFrame > xParentFrame = lArgs.getUnpackedValueOrDefault(ARGUMENT_PARENTFRAME , css::uno::Reference< css::frame::XFrame >());
+ OUString sFrameName = lArgs.getUnpackedValueOrDefault(ARGUMENT_FRAMENAME , OUString() );
+ bool bVisible = lArgs.getUnpackedValueOrDefault(ARGUMENT_MAKEVISIBLE , false );
+ bool bCreateTopWindow = lArgs.getUnpackedValueOrDefault(ARGUMENT_CREATETOPWINDOW , true );
+ // only possize=[0,0,0,0] triggers default handling of vcl !
+ css::awt::Rectangle aPosSize = lArgs.getUnpackedValueOrDefault(ARGUMENT_POSSIZE , css::awt::Rectangle(0, 0, 0, 0) );
+ css::uno::Reference< css::awt::XWindow > xContainerWindow = lArgs.getUnpackedValueOrDefault(ARGUMENT_CONTAINERWINDOW , css::uno::Reference< css::awt::XWindow >() );
+ bool bSupportPersistentWindowState = lArgs.getUnpackedValueOrDefault(ARGUMENT_SUPPORTPERSISTENTWINDOWSTATE , false );
+ bool bEnableTitleBarUpdate = lArgs.getUnpackedValueOrDefault(ARGUMENT_ENABLE_TITLEBARUPDATE , true );
+ // If the frame is explicitly requested to be hidden.
+ bool bHidden = lArgs.getUnpackedValueOrDefault("HiddenForConversion", false);
+
+ // We use FrameName property to set it as API name of the new created frame later.
+ // But those frame names must be different from the set of special target names as e.g. _blank, _self etcpp !
+ OUString sRightName = impl_filterNames(sFrameName);
+
+ // if no external frame window was given ... create a new one.
+ if ( ! xContainerWindow.is())
+ {
+ css::uno::Reference< css::awt::XWindow > xParentWindow;
+ if (xParentFrame.is())
+ xParentWindow = xParentFrame->getContainerWindow();
+
+ // Parent has no own window ...
+ // So we have to create a top level window always !
+ if ( ! xParentWindow.is())
+ bCreateTopWindow = true;
+
+ xContainerWindow = implts_createContainerWindow(xParentWindow, aPosSize, bCreateTopWindow);
+ }
+
+ // #i53630#
+ // Mark all document windows as "special ones", so VCL can bind
+ // special features to it. Because VCL doesn't know anything about documents ...
+ // Note: Doing so it's no longer supported, that e.g. our wizards can use findFrame(_blank)
+ // to create it's previous frames. They must do it manually by using WindowDescriptor+Toolkit!
+ css::uno::Reference< css::frame::XDesktop > xDesktop(xParentFrame, css::uno::UNO_QUERY);
+ bool bTopLevelDocumentWindow = (
+ sRightName.isEmpty() &&
+ (
+ (! xParentFrame.is() ) ||
+ ( xDesktop.is() )
+ )
+ );
+ if (bTopLevelDocumentWindow)
+ implts_applyDocStyleToWindow(xContainerWindow);
+ //------------------->
+
+ // create the new frame
+ VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow(xContainerWindow);
+ if (pContainerWindow && bHidden)
+ {
+ WindowExtendedStyle eStyle = pContainerWindow->GetExtendedStyle();
+ eStyle |= WindowExtendedStyle::DocHidden;
+ pContainerWindow->SetExtendedStyle(eStyle);
+ }
+ css::uno::Reference< css::frame::XFrame2 > xFrame = implts_createFrame(xParentFrame, xContainerWindow, sRightName);
+
+ // special freature:
+ // A special listener will restore pos/size states in case
+ // a component was loaded into the frame first time.
+ if (bSupportPersistentWindowState)
+ implts_establishWindowStateListener(xFrame);
+
+ // special feature: On Mac we need tagging the window in case
+ // the underlying model was modified.
+ // VCL will ignore our calls in case different platform then Mac
+ // is used ...
+ if (bTopLevelDocumentWindow)
+ implts_establishDocModifyListener (xFrame);
+
+ // special freature:
+ // A special listener will update title bar (text and icon)
+ // if component of frame will be changed.
+ if (bEnableTitleBarUpdate)
+ implts_establishTitleBarUpdate(xFrame);
+
+ // Make it visible directly here...
+ // if it's required from outside.
+ if (bVisible)
+ xContainerWindow->setVisible(bVisible);
+
+ return css::uno::Reference< css::uno::XInterface >(xFrame, css::uno::UNO_QUERY_THROW);
+}
+
+void TaskCreatorService::implts_applyDocStyleToWindow(const css::uno::Reference< css::awt::XWindow >& xWindow) const
+{
+ // SYNCHRONIZED ->
+ SolarMutexGuard aSolarGuard;
+ VclPtr<vcl::Window> pVCLWindow = VCLUnoHelper::GetWindow(xWindow);
+ if (pVCLWindow)
+ pVCLWindow->SetExtendedStyle(WindowExtendedStyle::Document);
+ // <- SYNCHRONIZED
+}
+
+css::uno::Reference< css::awt::XWindow > TaskCreatorService::implts_createContainerWindow( const css::uno::Reference< css::awt::XWindow >& xParentWindow ,
+ const css::awt::Rectangle& aPosSize ,
+ bool bTopWindow )
+{
+ // get toolkit to create task container window
+ css::uno::Reference< css::awt::XToolkit2 > xToolkit = css::awt::Toolkit::create( m_xContext );
+
+ // Check if child frames can be created really. We need at least a valid window at the parent frame ...
+ css::uno::Reference< css::awt::XWindowPeer > xParentWindowPeer;
+ if ( ! bTopWindow)
+ {
+ if ( ! xParentWindow.is())
+ bTopWindow = false;
+ else
+ xParentWindowPeer.set(xParentWindow, css::uno::UNO_QUERY_THROW);
+ }
+
+ // describe window properties.
+ css::awt::WindowDescriptor aDescriptor;
+ if (bTopWindow)
+ {
+ aDescriptor.Type = css::awt::WindowClass_TOP;
+ aDescriptor.WindowServiceName = "window";
+ aDescriptor.ParentIndex = -1;
+ aDescriptor.Parent.clear();
+ aDescriptor.Bounds = aPosSize;
+ aDescriptor.WindowAttributes = css::awt::WindowAttribute::BORDER |
+ css::awt::WindowAttribute::MOVEABLE |
+ css::awt::WindowAttribute::SIZEABLE |
+ css::awt::WindowAttribute::CLOSEABLE |
+ css::awt::VclWindowPeerAttribute::CLIPCHILDREN;
+ }
+ else
+ {
+ aDescriptor.Type = css::awt::WindowClass_TOP;
+ aDescriptor.WindowServiceName = "dockingwindow";
+ aDescriptor.ParentIndex = 1;
+ aDescriptor.Parent = xParentWindowPeer;
+ aDescriptor.Bounds = aPosSize;
+ aDescriptor.WindowAttributes = css::awt::VclWindowPeerAttribute::CLIPCHILDREN;
+ }
+
+ // create a new blank container window and get access to parent container to append new created task.
+ css::uno::Reference< css::awt::XWindowPeer > xPeer = xToolkit->createWindow( aDescriptor );
+ css::uno::Reference< css::awt::XWindow > xWindow ( xPeer, css::uno::UNO_QUERY_THROW );
+
+ sal_Int32 nBackground = 0xffffffff;
+
+ if (bTopWindow)
+ {
+ try
+ {
+ nBackground = sal_Int32(::svtools::ColorConfig().GetColorValue(::svtools::APPBACKGROUND).nColor);
+ }
+ catch (const css::uno::Exception &)
+ {
+ // Ignore
+ }
+ }
+ xPeer->setBackground(nBackground);
+
+ return xWindow;
+}
+
+css::uno::Reference< css::frame::XFrame2 > TaskCreatorService::implts_createFrame( const css::uno::Reference< css::frame::XFrame >& xParentFrame ,
+ const css::uno::Reference< css::awt::XWindow >& xContainerWindow,
+ const OUString& sName )
+{
+ // create new frame.
+ css::uno::Reference< css::frame::XFrame2 > xNewFrame = css::frame::Frame::create( m_xContext );
+
+ // Set window on frame.
+ // Do it before calling any other interface methods ...
+ // The new created frame must be initialized before you can do anything else there.
+ xNewFrame->initialize( xContainerWindow );
+
+ // Put frame to the frame tree.
+ // Note: The property creator/parent will be set on the new putted frame automatically ... by the parent container.
+ if (xParentFrame.is())
+ {
+ css::uno::Reference< css::frame::XFramesSupplier > xSupplier (xParentFrame, css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::frame::XFrames > xContainer = xSupplier->getFrames();
+ xContainer->append( css::uno::Reference<css::frame::XFrame>(xNewFrame, css::uno::UNO_QUERY_THROW) );
+ }
+
+ // Set it's API name (if there is one from outside)
+ if (!sName.isEmpty())
+ xNewFrame->setName( sName );
+
+ return xNewFrame;
+}
+
+void TaskCreatorService::implts_establishWindowStateListener( const css::uno::Reference< css::frame::XFrame2 >& xFrame )
+{
+ // Special feature: It's allowed for frames using a top level window only!
+ // We must create a special listener service and couple it with the new created task frame.
+ // He will restore or save the window state of it ...
+ // See used classes for further information too.
+ rtl::Reference<PersistentWindowState> pPersistentStateHandler = new PersistentWindowState( m_xContext );
+
+ css::uno::Sequence< css::uno::Any > lInitData{ css::uno::Any(xFrame) };
+ pPersistentStateHandler->initialize(lInitData);
+}
+
+void TaskCreatorService::implts_establishDocModifyListener( const css::uno::Reference< css::frame::XFrame2 >& xFrame )
+{
+ // Special feature: It's allowed for frames using a top level window only!
+ // We must create a special listener service and couple it with the new created task frame.
+ // It will tag the window as modified if the underlying model was modified ...
+ rtl::Reference<TagWindowAsModified> pTag = new TagWindowAsModified();
+
+ css::uno::Sequence< css::uno::Any > lInitData{ css::uno::Any(xFrame) };
+ pTag->initialize(lInitData);
+}
+
+void TaskCreatorService::implts_establishTitleBarUpdate( const css::uno::Reference< css::frame::XFrame2 >& xFrame )
+{
+ rtl::Reference<TitleBarUpdate> pHelper = new TitleBarUpdate (m_xContext);
+
+ css::uno::Sequence< css::uno::Any > lInitData{ css::uno::Any(xFrame) };
+ pHelper->initialize(lInitData);
+}
+
+OUString TaskCreatorService::impl_filterNames( const OUString& sName )
+{
+ OUString sFiltered;
+ if (TargetHelper::isValidNameForFrame(sName))
+ sFiltered = sName;
+ return sFiltered;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_TaskCreator_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new TaskCreatorService(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/services/uriabbreviation.cxx b/framework/source/services/uriabbreviation.cxx
new file mode 100644
index 000000000..6faa0c8e3
--- /dev/null
+++ b/framework/source/services/uriabbreviation.cxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <services/uriabbreviation.hxx>
+
+#include <sal/config.h>
+
+#include <tools/urlobj.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+// framework namespace
+namespace framework
+{
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL UriAbbreviation::getImplementationName()
+{
+ return "com.sun.star.comp.framework.UriAbbreviation";
+}
+
+sal_Bool SAL_CALL UriAbbreviation::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL UriAbbreviation::getSupportedServiceNames()
+{
+ return { "com.sun.star.util.UriAbbreviation" };
+}
+
+UriAbbreviation::UriAbbreviation(css::uno::Reference< css::uno::XComponentContext > const & )
+{
+}
+
+// css::util::XStringAbbreviation:
+OUString SAL_CALL UriAbbreviation::abbreviateString(const css::uno::Reference< css::util::XStringWidth > & xStringWidth, ::sal_Int32 nWidth, const OUString & aString)
+{
+ OUString aResult( aString );
+ if ( xStringWidth.is() )
+ {
+ // Use INetURLObject to abbreviate URLs
+ INetURLObject aURL( aString );
+ aResult = aURL.getAbbreviated( xStringWidth, nWidth, INetURLObject::DecodeMechanism::Unambiguous );
+ }
+
+ return aResult;
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_UriAbbreviation_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::UriAbbreviation(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/services/urltransformer.cxx b/framework/source/services/urltransformer.cxx
new file mode 100644
index 000000000..b4fd3de16
--- /dev/null
+++ b/framework/source/services/urltransformer.cxx
@@ -0,0 +1,310 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/urlobj.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+namespace {
+
+class URLTransformer : public ::cppu::WeakImplHelper< css::util::XURLTransformer, css::lang::XServiceInfo>
+{
+public:
+ URLTransformer() {}
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.URLTransformer";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.util.URLTransformer"};
+ }
+
+ virtual sal_Bool SAL_CALL parseStrict( css::util::URL& aURL ) override;
+
+ virtual sal_Bool SAL_CALL parseSmart( css::util::URL& aURL, const OUString& sSmartProtocol ) override;
+
+ virtual sal_Bool SAL_CALL assemble( css::util::URL& aURL ) override;
+
+ virtual OUString SAL_CALL getPresentation( const css::util::URL& aURL, sal_Bool bWithPassword ) override;
+};
+
+void lcl_ParserHelper(INetURLObject& _rParser, css::util::URL& _rURL,bool _bUseIntern)
+{
+ // Get all information about this URL.
+ _rURL.Protocol = INetURLObject::GetScheme( _rParser.GetProtocol() );
+ _rURL.User = _rParser.GetUser ( INetURLObject::DecodeMechanism::WithCharset );
+ _rURL.Password = _rParser.GetPass ( INetURLObject::DecodeMechanism::WithCharset );
+ _rURL.Server = _rParser.GetHost ( INetURLObject::DecodeMechanism::WithCharset );
+ _rURL.Port = static_cast<sal_Int16>(_rParser.GetPort());
+
+ sal_Int32 nCount = _rParser.getSegmentCount( false );
+ if ( nCount > 0 )
+ {
+ // Don't add last segment as it is the name!
+ --nCount;
+
+ OUStringBuffer aPath(128);
+ for ( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ aPath.append( '/');
+ aPath.append( _rParser.getName( nIndex, false, INetURLObject::DecodeMechanism::NONE ));
+ }
+
+ if ( nCount > 0 )
+ aPath.append( '/' ); // final slash!
+
+ _rURL.Path = aPath.makeStringAndClear();
+ _rURL.Name = _rParser.getName( INetURLObject::LAST_SEGMENT, false, INetURLObject::DecodeMechanism::NONE );
+ }
+ else
+ {
+ _rURL.Path = _rParser.GetURLPath( INetURLObject::DecodeMechanism::NONE );
+ _rURL.Name = _rParser.GetLastName();
+ }
+
+ _rURL.Arguments = _rParser.GetParam();
+ _rURL.Mark = _rParser.GetMark( INetURLObject::DecodeMechanism::WithCharset );
+
+ // INetURLObject supports only an intelligent method of parsing URL's. So write
+ // back Complete to have a valid encoded URL in all cases!
+ _rURL.Complete = _rParser.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ if ( _bUseIntern )
+ _rURL.Complete = _rURL.Complete.intern();
+
+ _rParser.SetMark( u"" );
+ _rParser.SetParam( u"" );
+
+ _rURL.Main = _rParser.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+}
+
+// XURLTransformer
+sal_Bool SAL_CALL URLTransformer::parseStrict( css::util::URL& aURL )
+{
+ // Safe impossible cases.
+ if ( aURL.Complete.isEmpty() )
+ {
+ return false;
+ }
+ // Try to extract the protocol
+ sal_Int32 nURLIndex = aURL.Complete.indexOf( ':' );
+ if ( nURLIndex <= 1 )
+ return false;
+
+ std::u16string_view aProtocol = aURL.Complete.subView( 0, nURLIndex+1 );
+
+ // If INetURLObject knows this protocol let it parse
+ if ( INetURLObject::CompareProtocolScheme( aProtocol ) != INetProtocol::NotValid )
+ {
+ // Initialize parser with given URL.
+ INetURLObject aParser( aURL.Complete );
+
+ // Get all information about this URL.
+ INetProtocol eINetProt = aParser.GetProtocol();
+ if ( eINetProt == INetProtocol::NotValid )
+ {
+ return false;
+ }
+ else if ( !aParser.HasError() )
+ {
+ lcl_ParserHelper(aParser,aURL,false);
+ // Return "URL is parsed".
+ return true;
+ }
+ }
+ else
+ {
+ // Minimal support for unknown protocols. This is mandatory to support the "Protocol Handlers" implemented
+ // in framework!
+ aURL.Protocol = aProtocol;
+ aURL.Main = aURL.Complete;
+ aURL.Path = aURL.Complete.copy( nURLIndex+1 );
+
+ // Return "URL is parsed".
+ return true;
+ }
+
+ return false;
+}
+
+// XURLTransformer
+
+sal_Bool SAL_CALL URLTransformer::parseSmart( css::util::URL& aURL,
+ const OUString& sSmartProtocol )
+{
+ // Safe impossible cases.
+ if ( aURL.Complete.isEmpty() )
+ {
+ return false;
+ }
+
+ // Initialize parser with given URL.
+ INetURLObject aParser;
+
+ aParser.SetSmartProtocol( INetURLObject::CompareProtocolScheme( sSmartProtocol ));
+ bool bOk = aParser.SetSmartURL( aURL.Complete );
+ if ( bOk )
+ {
+ lcl_ParserHelper(aParser,aURL,true);
+ // Return "URL is parsed".
+ return true;
+ }
+ else
+ {
+ // Minimal support for unknown protocols. This is mandatory to support the "Protocol Handlers" implemented
+ // in framework!
+ if ( INetURLObject::CompareProtocolScheme( sSmartProtocol ) == INetProtocol::NotValid )
+ {
+ // Try to extract the protocol
+ sal_Int32 nIndex = aURL.Complete.indexOf( ':' );
+ if ( nIndex > 1 )
+ {
+ OUString aProtocol = aURL.Complete.copy( 0, nIndex+1 );
+
+ // If INetURLObject knows this protocol something is wrong as detected before =>
+ // give up and return false!
+ if ( INetURLObject::CompareProtocolScheme( aProtocol ) != INetProtocol::NotValid )
+ return false;
+ else
+ aURL.Protocol = aProtocol;
+ }
+ else
+ return false;
+
+ aURL.Main = aURL.Complete;
+ aURL.Path = aURL.Complete.copy( nIndex+1 );
+ return true;
+ }
+ else
+ return false;
+ }
+}
+
+// XURLTransformer
+sal_Bool SAL_CALL URLTransformer::assemble( css::util::URL& aURL )
+{
+ // Initialize parser.
+ INetURLObject aParser;
+
+ if ( INetURLObject::CompareProtocolScheme( aURL.Protocol ) != INetProtocol::NotValid )
+ {
+ OUStringBuffer aCompletePath( aURL.Path );
+
+ // Concat the name if it is provided, just support a final slash
+ if ( !aURL.Name.isEmpty() )
+ {
+ sal_Int32 nIndex = aURL.Path.lastIndexOf( '/' );
+ if ( nIndex == ( aURL.Path.getLength() -1 ))
+ aCompletePath.append( aURL.Name );
+ else
+ {
+ aCompletePath.append( '/' );
+ aCompletePath.append( aURL.Name );
+ }
+ }
+
+ bool bResult = aParser.ConcatData(
+ INetURLObject::CompareProtocolScheme( aURL.Protocol ) ,
+ aURL.User ,
+ aURL.Password ,
+ aURL.Server ,
+ aURL.Port ,
+ aCompletePath.makeStringAndClear() );
+
+ if ( !bResult )
+ return false;
+
+ // First parse URL WITHOUT ...
+ aURL.Main = aParser.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ // ...and then WITH parameter and mark.
+ aParser.SetParam( aURL.Arguments);
+ aParser.SetMark ( aURL.Mark, INetURLObject::EncodeMechanism::All );
+ aURL.Complete = aParser.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ // Return "URL is assembled".
+ return true;
+ }
+ else if ( !aURL.Protocol.isEmpty() )
+ {
+ // Minimal support for unknown protocols
+ aURL.Complete = aURL.Protocol + aURL.Path;
+ aURL.Main = aURL.Complete;
+ return true;
+ }
+
+ return false;
+}
+
+// XURLTransformer
+
+OUString SAL_CALL URLTransformer::getPresentation( const css::util::URL& aURL,
+ sal_Bool bWithPassword )
+{
+ // Safe impossible cases.
+ if ( aURL.Complete.isEmpty() )
+ {
+ return OUString();
+ }
+
+ // Check given URL
+ css::util::URL aTestURL = aURL;
+ bool bParseResult = parseSmart( aTestURL, aTestURL.Protocol );
+ if ( bParseResult )
+ {
+ if ( !bWithPassword && !aTestURL.Password.isEmpty() )
+ {
+ // Exchange password text with other placeholder string
+ aTestURL.Password = "<******>";
+ assemble( aTestURL );
+ }
+
+ // Convert internal URLs to "praesentation"-URLs!
+ OUString sPraesentationURL;
+ INetURLObject::translateToExternal( aTestURL.Complete, sPraesentationURL, INetURLObject::DecodeMechanism::Unambiguous );
+
+ return sPraesentationURL;
+ }
+ else
+ return OUString();
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_URLTransformer_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new URLTransformer());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/CommandImageResolver.cxx b/framework/source/uiconfiguration/CommandImageResolver.cxx
new file mode 100644
index 000000000..a431ae320
--- /dev/null
+++ b/framework/source/uiconfiguration/CommandImageResolver.cxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "CommandImageResolver.hxx"
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/urlobj.hxx>
+
+using css::uno::Sequence;
+
+namespace vcl
+{
+
+namespace
+{
+
+const o3tl::enumarray<ImageType, const char*> ImageType_Prefixes =
+{
+ "cmd/sc_",
+ "cmd/lc_",
+ "cmd/32/"
+};
+
+OUString lclConvertToCanonicalName(const OUString& rFileName)
+{
+ bool bRemoveSlash(true);
+ sal_Int32 nLength = rFileName.getLength();
+ const sal_Unicode* pString = rFileName.getStr();
+
+ OUStringBuffer aBuffer(nLength*2);
+
+ for (sal_Int32 i = 0; i < nLength; i++)
+ {
+ const sal_Unicode cCurrentChar = pString[i];
+ switch (cCurrentChar)
+ {
+ // map forbidden characters to escape
+ case '/':
+ if (!bRemoveSlash)
+ aBuffer.append("%2f");
+ break;
+ case '\\': aBuffer.append("%5c"); bRemoveSlash = false; break;
+ case ':': aBuffer.append("%3a"); bRemoveSlash = false; break;
+ case '*': aBuffer.append("%2a"); bRemoveSlash = false; break;
+ case '?': aBuffer.append("%3f"); bRemoveSlash = false; break;
+ case '<': aBuffer.append("%3c"); bRemoveSlash = false; break;
+ case '>': aBuffer.append("%3e"); bRemoveSlash = false; break;
+ case '|': aBuffer.append("%7c"); bRemoveSlash = false; break;
+ default:
+ aBuffer.append(cCurrentChar); bRemoveSlash = false; break;
+ }
+ }
+ return aBuffer.makeStringAndClear();
+}
+
+} // end anonymous namespace
+
+CommandImageResolver::CommandImageResolver()
+{
+}
+
+CommandImageResolver::~CommandImageResolver()
+{
+}
+
+void CommandImageResolver::registerCommands(const Sequence<OUString>& aCommandSequence)
+{
+ sal_Int32 nSequenceSize = aCommandSequence.getLength();
+
+ m_aImageCommandNameVector.resize(nSequenceSize);
+ m_aImageNameVector.resize(nSequenceSize);
+
+ for (sal_Int32 i = 0; i < nSequenceSize; ++i)
+ {
+ OUString aCommandName(aCommandSequence[i]);
+ OUString aImageName;
+
+ m_aImageCommandNameVector[i] = aCommandName;
+
+ if (aCommandName.indexOf(".uno:") != 0)
+ {
+ INetURLObject aUrlObject(aCommandName, INetURLObject::EncodeMechanism::All);
+ aImageName = aUrlObject.GetURLPath();
+ aImageName = lclConvertToCanonicalName(aImageName);
+ }
+ else
+ {
+ // just remove the schema
+ if (aCommandName.getLength() > 5)
+ aImageName = aCommandName.copy(5);
+
+ // Search for query part.
+ if (aImageName.indexOf('?') != -1)
+ aImageName = lclConvertToCanonicalName(aImageName);
+ }
+
+ // Image names are not case-dependent. Always use lower case characters to
+ // reflect this.
+ aImageName = aImageName.toAsciiLowerCase() + ".png";
+
+ m_aImageNameVector[i] = aImageName;
+ m_aCommandToImageNameMap[aCommandName] = aImageName;
+ }
+}
+
+bool CommandImageResolver::hasImage(const OUString& rCommandURL)
+{
+ CommandToImageNameMap::const_iterator pIterator = m_aCommandToImageNameMap.find(rCommandURL);
+ return pIterator != m_aCommandToImageNameMap.end();
+}
+
+ImageList* CommandImageResolver::getImageList(ImageType nImageType)
+{
+ const OUString sIconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme();
+
+ if (sIconTheme != m_sIconTheme)
+ {
+ m_sIconTheme = sIconTheme;
+ for (auto& rp : m_pImageList)
+ rp.reset();
+ }
+
+ if (!m_pImageList[nImageType])
+ {
+ OUString sIconPath = OUString::createFromAscii(ImageType_Prefixes[nImageType]);
+ m_pImageList[nImageType].reset( new ImageList(m_aImageNameVector, sIconPath) );
+ }
+
+ return m_pImageList[nImageType].get();
+}
+
+Image CommandImageResolver::getImageFromCommandURL(ImageType nImageType, const OUString& rCommandURL)
+{
+ CommandToImageNameMap::const_iterator pIterator = m_aCommandToImageNameMap.find(rCommandURL);
+ if (pIterator != m_aCommandToImageNameMap.end())
+ {
+ ImageList* pImageList = getImageList(nImageType);
+ return pImageList->GetImage(pIterator->second);
+ }
+ return Image();
+}
+
+} // end namespace vcl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/CommandImageResolver.hxx b/framework/source/uiconfiguration/CommandImageResolver.hxx
new file mode 100644
index 000000000..0622caf33
--- /dev/null
+++ b/framework/source/uiconfiguration/CommandImageResolver.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vcl/image.hxx>
+#include <o3tl/enumarray.hxx>
+
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include "ImageList.hxx"
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+namespace vcl
+{
+class CommandImageResolver final
+{
+private:
+ typedef std::unordered_map<OUString, OUString> CommandToImageNameMap;
+
+ CommandToImageNameMap m_aCommandToImageNameMap;
+ std::vector<OUString> m_aImageCommandNameVector;
+ std::vector<OUString> m_aImageNameVector;
+
+ o3tl::enumarray<ImageType, std::unique_ptr<ImageList>> m_pImageList;
+ OUString m_sIconTheme;
+
+ ImageList* getImageList(ImageType nImageType);
+
+public:
+ CommandImageResolver();
+ ~CommandImageResolver();
+
+ void registerCommands(const css::uno::Sequence<OUString>& aCommandSequence);
+ Image getImageFromCommandURL(ImageType nImageType, const OUString& rCommandURL);
+
+ std::vector<OUString>& getCommandNames() { return m_aImageCommandNameVector; }
+
+ bool hasImage(const OUString& rCommandURL);
+};
+
+} // end namespace vcl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/ImageList.cxx b/framework/source/uiconfiguration/ImageList.cxx
new file mode 100644
index 000000000..0e64d219c
--- /dev/null
+++ b/framework/source/uiconfiguration/ImageList.cxx
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <vcl/image.hxx>
+#include "ImageList.hxx"
+
+ImageList::ImageList()
+{
+}
+
+ImageList::ImageList(const std::vector< OUString >& rNameVector,
+ const OUString& rPrefix)
+{
+ SAL_INFO( "vcl", "vcl: ImageList::ImageList(const vector< OUString >& ..." );
+
+ maImages.reserve( rNameVector.size() );
+
+ maPrefix = rPrefix;
+ for( size_t i = 0; i < rNameVector.size(); ++i )
+ ImplAddImage( rPrefix, rNameVector[ i ], static_cast< sal_uInt16 >( i ) + 1, Image() );
+}
+
+// FIXME: Rather a performance hazard
+BitmapEx ImageList::GetAsHorizontalStrip() const
+{
+ sal_uInt16 nCount = maImages.size();
+ if( !nCount )
+ return BitmapEx();
+
+ BitmapEx aTempl = maImages[ 0 ]->maImage.GetBitmapEx();
+ Size aImageSize(aTempl.GetSizePixel());
+ Size aSize(aImageSize.Width() * nCount, aImageSize.Height());
+ BitmapEx aResult( aTempl, Point(), aSize );
+
+ tools::Rectangle aSrcRect( Point( 0, 0 ), aImageSize );
+ for (sal_uInt16 nIdx = 0; nIdx < nCount; nIdx++)
+ {
+ tools::Rectangle aDestRect( Point( nIdx * aImageSize.Width(), 0 ), aImageSize );
+ ImageAryData *pData = maImages[ nIdx ].get();
+ BitmapEx aTmp = pData->maImage.GetBitmapEx();
+ aResult.CopyPixel( aDestRect, aSrcRect, &aTmp);
+ }
+
+ return aResult;
+}
+
+void ImageList::InsertFromHorizontalStrip( const BitmapEx &rBitmapEx,
+ const std::vector< OUString > &rNameVector )
+{
+ sal_uInt16 nItems = sal::static_int_cast< sal_uInt16 >( rNameVector.size() );
+
+ if (!nItems)
+ return;
+
+ Size aSize( rBitmapEx.GetSizePixel() );
+ DBG_ASSERT (rBitmapEx.GetSizePixel().Width() % nItems == 0,
+ "ImageList::InsertFromHorizontalStrip - very odd size");
+ aSize.setWidth( aSize.Width() / nItems );
+ maImages.clear();
+ maNameHash.clear();
+ maImages.reserve( nItems );
+ maPrefix.clear();
+
+ for (sal_uInt16 nIdx = 0; nIdx < nItems; nIdx++)
+ {
+ BitmapEx aBitmap( rBitmapEx, Point( nIdx * aSize.Width(), 0 ), aSize );
+ ImplAddImage( maPrefix, rNameVector[ nIdx ], nIdx + 1, Image( aBitmap ) );
+ }
+}
+
+sal_uInt16 ImageList::ImplGetImageId( const OUString& rImageName ) const
+{
+ auto it = maNameHash.find( rImageName );
+ if (it == maNameHash.end())
+ return 0;
+ return it->second->mnId;
+}
+
+void ImageList::AddImage( const OUString& rImageName, const Image& rImage )
+{
+ SAL_WARN_IF( GetImagePos( rImageName ) != IMAGELIST_IMAGE_NOTFOUND, "vcl", "ImageList::AddImage() - ImageName already exists" );
+
+ ImplAddImage( maPrefix, rImageName, GetImageCount() + 1, rImage );
+}
+
+void ImageList::ReplaceImage( const OUString& rImageName, const Image& rImage )
+{
+ const sal_uInt16 nId = ImplGetImageId( rImageName );
+
+ if( nId )
+ {
+ // Just replace the bitmap rather than doing RemoveImage / AddImage
+ // which breaks index-based iteration.
+ ImageAryData *pImg = maNameHash[ rImageName ];
+ pImg->maImage = rImage;
+ }
+}
+
+void ImageList::RemoveImage( sal_uInt16 nId )
+{
+ for( size_t i = 0; i < maImages.size(); ++i )
+ {
+ if( maImages[ i ]->mnId == nId )
+ {
+ ImplRemoveImage( static_cast< sal_uInt16 >( i ) );
+ break;
+ }
+ }
+}
+
+Image ImageList::GetImage( const OUString& rImageName ) const
+{
+ auto it = maNameHash.find( rImageName );
+ if (it == maNameHash.end())
+ return Image();
+ return it->second->maImage;
+}
+
+sal_uInt16 ImageList::GetImageCount() const
+{
+ return static_cast< sal_uInt16 >( maImages.size() );
+}
+
+sal_uInt16 ImageList::GetImagePos( std::u16string_view rImageName ) const
+{
+ if( !rImageName.empty() )
+ {
+ for( size_t i = 0; i < maImages.size(); i++ )
+ {
+ if (maImages[i]->maName == rImageName)
+ return static_cast< sal_uInt16 >( i );
+ }
+ }
+
+ return IMAGELIST_IMAGE_NOTFOUND;
+}
+
+sal_uInt16 ImageList::GetImageId( sal_uInt16 nPos ) const
+{
+ return maImages[ nPos ]->mnId;
+}
+
+const OUString & ImageList::GetImageName( sal_uInt16 nPos ) const
+{
+ return maImages[ nPos ]->maName;
+}
+
+void ImageList::GetImageNames( std::vector< OUString >& rNames ) const
+{
+ SAL_INFO( "vcl", "vcl: ImageList::GetImageNames" );
+
+ rNames = std::vector< OUString >();
+
+ for(auto const & pImage : maImages)
+ {
+ const OUString& rName( pImage->maName );
+ if( !rName.isEmpty())
+ rNames.push_back( rName );
+ }
+}
+
+void ImageList::ImplAddImage( std::u16string_view aPrefix, const OUString &aName,
+ sal_uInt16 nId, const Image &aImage )
+{
+ Image aInsert = aImage;
+ if (!aInsert)
+ aInsert = Image( OUString::Concat("private:graphicrepository/") + aPrefix + aName );
+
+ ImageAryData *pImg = new ImageAryData{ aName, nId, aInsert };
+ maImages.emplace_back( pImg );
+ if( !aName.isEmpty() )
+ maNameHash [ aName ] = pImg;
+}
+
+void ImageList::ImplRemoveImage( sal_uInt16 nPos )
+{
+ ImageAryData *pImg = maImages[ nPos ].get();
+ if( !pImg->maName.isEmpty() )
+ maNameHash.erase( pImg->maName );
+ maImages.erase( maImages.begin() + nPos );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/ImageList.hxx b/framework/source/uiconfiguration/ImageList.hxx
new file mode 100644
index 000000000..0951b8369
--- /dev/null
+++ b/framework/source/uiconfiguration/ImageList.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef FRAMEWORK_SOURCE_UICONFIGURATION_IMAGELIST_HXX
+#define FRAMEWORK_SOURCE_UICONFIGURATION_IMAGELIST_HXX
+
+#include <vcl/image.hxx>
+
+#include <string_view>
+#include <unordered_map>
+#include <vector>
+
+// Images identified by either name, or by id
+struct ImageAryData
+{
+ OUString maName;
+ sal_uInt16 mnId;
+ Image maImage;
+};
+
+
+class ImageList
+{
+public:
+ explicit ImageList();
+ ImageList( const std::vector<OUString>& rNameVector,
+ const OUString& rPrefix);
+
+ void InsertFromHorizontalStrip( const BitmapEx &rBitmapEx,
+ const std::vector< OUString > &rNameVector );
+ BitmapEx GetAsHorizontalStrip() const;
+ sal_uInt16 GetImageCount() const;
+
+ void AddImage( const OUString& rImageName, const Image& rImage );
+
+ void ReplaceImage( const OUString& rImageName, const Image& rImage );
+
+ void RemoveImage( sal_uInt16 nId );
+
+ Image GetImage( const OUString& rImageName ) const;
+
+ sal_uInt16 GetImagePos( std::u16string_view rImageName ) const;
+
+ sal_uInt16 GetImageId( sal_uInt16 nPos ) const;
+
+ const OUString & GetImageName( sal_uInt16 nPos ) const;
+ void GetImageNames( ::std::vector< OUString >& rNames ) const;
+
+private:
+
+ std::vector< std::unique_ptr<ImageAryData> > maImages;
+ std::unordered_map< OUString, ImageAryData * > maNameHash;
+ OUString maPrefix;
+
+ sal_uInt16 ImplGetImageId( const OUString& rImageName ) const;
+ void ImplAddImage( std::u16string_view aPrefix, const OUString &aName, sal_uInt16 nId, const Image &aImage );
+ void ImplRemoveImage( sal_uInt16 nPos );
+};
+
+#endif // INCLUDED_VCL_IMAGE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/globalsettings.cxx b/framework/source/uiconfiguration/globalsettings.cxx
new file mode 100644
index 000000000..0883cc8af
--- /dev/null
+++ b/framework/source/uiconfiguration/globalsettings.cxx
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uiconfiguration/globalsettings.hxx>
+#include <services.h>
+
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+
+#include <rtl/ref.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <mutex>
+#include <utility>
+
+// Defines
+
+using namespace ::com::sun::star;
+
+// Namespace
+
+namespace framework
+{
+
+// Configuration access class for WindowState supplier implementation
+
+namespace {
+
+class GlobalSettings_Access : public ::cppu::WeakImplHelper<
+ css::lang::XComponent,
+ css::lang::XEventListener>
+{
+ public:
+ explicit GlobalSettings_Access( css::uno::Reference< css::uno::XComponentContext > xContext );
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // settings access
+ bool HasToolbarStatesInfo();
+ bool GetToolbarStateInfo( GlobalSettings::StateInfo eStateInfo, css::uno::Any& aValue );
+
+ private:
+ void impl_initConfigAccess();
+
+ std::mutex m_mutex;
+ bool m_bDisposed : 1,
+ m_bConfigRead : 1;
+ OUString m_aNodeRefStates;
+ OUString m_aPropStatesEnabled;
+ OUString m_aPropLocked;
+ OUString m_aPropDocked;
+ css::uno::Reference< css::container::XNameAccess > m_xConfigAccess;
+ css::uno::Reference< css::uno::XComponentContext> m_xContext;
+};
+
+}
+
+GlobalSettings_Access::GlobalSettings_Access( css::uno::Reference< css::uno::XComponentContext > xContext ) :
+ m_bDisposed( false ),
+ m_bConfigRead( false ),
+ m_aNodeRefStates( "States" ),
+ m_aPropStatesEnabled( "StatesEnabled" ),
+ m_aPropLocked( "Locked" ),
+ m_aPropDocked( "Docked" ),
+ m_xContext(std::move( xContext ))
+{
+}
+
+// XComponent
+void SAL_CALL GlobalSettings_Access::dispose()
+{
+ std::unique_lock g(m_mutex);
+ m_xConfigAccess.clear();
+ m_bDisposed = true;
+}
+
+void SAL_CALL GlobalSettings_Access::addEventListener( const css::uno::Reference< css::lang::XEventListener >& )
+{
+}
+
+void SAL_CALL GlobalSettings_Access::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& )
+{
+}
+
+// XEventListener
+void SAL_CALL GlobalSettings_Access::disposing( const css::lang::EventObject& )
+{
+ std::unique_lock g(m_mutex);
+ m_xConfigAccess.clear();
+}
+
+// settings access
+bool GlobalSettings_Access::HasToolbarStatesInfo()
+{
+ std::unique_lock g(m_mutex);
+
+ if ( m_bDisposed )
+ return false;
+
+ if ( !m_bConfigRead )
+ {
+ m_bConfigRead = true;
+ impl_initConfigAccess();
+ }
+
+ if ( m_xConfigAccess.is() )
+ {
+ try
+ {
+ css::uno::Any a;
+ bool bValue;
+ a = m_xConfigAccess->getByName( m_aPropStatesEnabled );
+ if ( a >>= bValue )
+ return bValue;
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+ catch ( const css::uno::Exception& )
+ {
+ }
+ }
+
+ return false;
+}
+
+bool GlobalSettings_Access::GetToolbarStateInfo( GlobalSettings::StateInfo eStateInfo, css::uno::Any& aValue )
+{
+ std::unique_lock g(m_mutex);
+
+ if ( m_bDisposed )
+ return false;
+
+ if ( !m_bConfigRead )
+ {
+ m_bConfigRead = true;
+ impl_initConfigAccess();
+ }
+
+ if ( !m_xConfigAccess.is() )
+ return false;
+
+ try
+ {
+ css::uno::Any a = m_xConfigAccess->getByName( m_aNodeRefStates );
+ css::uno::Reference< css::container::XNameAccess > xNameAccess;
+ if ( a >>= xNameAccess )
+ {
+ if ( eStateInfo == GlobalSettings::STATEINFO_LOCKED )
+ a = xNameAccess->getByName( m_aPropLocked );
+ else if ( eStateInfo == GlobalSettings::STATEINFO_DOCKED )
+ a = xNameAccess->getByName( m_aPropDocked );
+
+ aValue = a;
+ return true;
+ }
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+ catch ( const css::uno::Exception& )
+ {
+ }
+
+ return false;
+}
+
+void GlobalSettings_Access::impl_initConfigAccess()
+{
+ try
+ {
+ if ( m_xContext.is() )
+ {
+ css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider =
+ css::configuration::theDefaultProvider::get( m_xContext );
+
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString("/org.openoffice.Office.UI.GlobalSettings/Toolbars"))}
+ }));
+ m_xConfigAccess.set(xConfigProvider->createInstanceWithArguments(
+ SERVICENAME_CFGREADACCESS, aArgs ),
+ css::uno::UNO_QUERY );
+
+ css::uno::Reference< css::lang::XComponent >(
+ xConfigProvider, css::uno::UNO_QUERY_THROW )->addEventListener(
+ css::uno::Reference< css::lang::XEventListener >(this));
+ }
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ catch ( const css::uno::Exception& )
+ {
+ }
+}
+
+// global class
+
+static GlobalSettings_Access* GetGlobalSettings( const css::uno::Reference< css::uno::XComponentContext >& rxContext )
+{
+ static rtl::Reference<GlobalSettings_Access> pStaticSettings = new GlobalSettings_Access( rxContext );
+ return pStaticSettings.get();
+}
+
+GlobalSettings::GlobalSettings( css::uno::Reference< css::uno::XComponentContext > xContext ) :
+ m_xContext(std::move( xContext ))
+{
+}
+
+GlobalSettings::~GlobalSettings()
+{
+}
+
+// settings access
+bool GlobalSettings::HasToolbarStatesInfo() const
+{
+ GlobalSettings_Access* pSettings( GetGlobalSettings( m_xContext ));
+
+ if ( pSettings )
+ return pSettings->HasToolbarStatesInfo();
+ else
+ return false;
+}
+
+bool GlobalSettings::GetToolbarStateInfo( StateInfo eStateInfo, css::uno::Any& aValue )
+{
+ GlobalSettings_Access* pSettings( GetGlobalSettings( m_xContext ));
+
+ if ( pSettings )
+ return pSettings->GetToolbarStateInfo( eStateInfo, aValue );
+ else
+ return false;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/graphicnameaccess.cxx b/framework/source/uiconfiguration/graphicnameaccess.cxx
new file mode 100644
index 000000000..6812f5604
--- /dev/null
+++ b/framework/source/uiconfiguration/graphicnameaccess.cxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uiconfiguration/graphicnameaccess.hxx>
+
+#include <comphelper/sequence.hxx>
+
+using namespace ::com::sun::star;
+
+namespace framework
+{
+
+GraphicNameAccess::GraphicNameAccess()
+{
+}
+
+GraphicNameAccess::~GraphicNameAccess()
+{
+}
+
+void GraphicNameAccess::addElement( const OUString& rName, const uno::Reference< graphic::XGraphic >& rElement )
+{
+ m_aNameToElementMap.emplace( rName, rElement );
+}
+
+// XNameAccess
+uno::Any SAL_CALL GraphicNameAccess::getByName( const OUString& aName )
+{
+ NameGraphicHashMap::const_iterator pIter = m_aNameToElementMap.find( aName );
+ if ( pIter == m_aNameToElementMap.end() )
+ throw container::NoSuchElementException();
+ return uno::Any( pIter->second );
+}
+
+uno::Sequence< OUString > SAL_CALL GraphicNameAccess::getElementNames()
+{
+ if ( !m_aSeq.hasElements() )
+ {
+ m_aSeq = comphelper::mapKeysToSequence(m_aNameToElementMap);
+ }
+
+ return m_aSeq;
+}
+
+sal_Bool SAL_CALL GraphicNameAccess::hasByName( const OUString& aName )
+{
+ NameGraphicHashMap::const_iterator pIter = m_aNameToElementMap.find( aName );
+ return ( pIter != m_aNameToElementMap.end() );
+}
+
+// XElementAccess
+sal_Bool SAL_CALL GraphicNameAccess::hasElements()
+{
+ return ( !m_aNameToElementMap.empty() );
+}
+
+uno::Type SAL_CALL GraphicNameAccess::getElementType()
+{
+ return cppu::UnoType<graphic::XGraphic>::get();
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/imagemanager.cxx b/framework/source/uiconfiguration/imagemanager.cxx
new file mode 100644
index 000000000..1e104b6df
--- /dev/null
+++ b/framework/source/uiconfiguration/imagemanager.cxx
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uiconfiguration/imagemanager.hxx>
+#include "imagemanagerimpl.hxx"
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <vcl/svapp.hxx>
+
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::XInterface;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::graphic::XGraphic;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::embed;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+
+namespace framework
+{
+
+ImageManager::ImageManager( const uno::Reference< uno::XComponentContext >& rxContext, bool bForModule ) :
+ m_pImpl( new ImageManagerImpl(rxContext, this, bForModule) )
+{
+}
+
+ImageManager::~ImageManager()
+{
+ m_pImpl->clear();
+}
+
+// XComponent
+void SAL_CALL ImageManager::dispose()
+{
+ m_pImpl->dispose();
+}
+
+void SAL_CALL ImageManager::addEventListener( const uno::Reference< XEventListener >& xListener )
+{
+ m_pImpl->addEventListener(xListener);
+}
+
+void SAL_CALL ImageManager::removeEventListener( const uno::Reference< XEventListener >& xListener )
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ m_pImpl->removeEventListener(xListener);
+}
+
+// Non-UNO methods
+void ImageManager::setStorage( const uno::Reference< XStorage >& Storage )
+{
+ SolarMutexGuard g;
+
+ m_pImpl->m_xUserConfigStorage = Storage;
+ m_pImpl->implts_initialize();
+}
+
+// XInitialization
+void SAL_CALL ImageManager::initialize( const Sequence< Any >& aArguments )
+{
+ m_pImpl->initialize(aArguments);
+}
+
+// XImageManager
+void SAL_CALL ImageManager::reset()
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ m_pImpl->reset();
+}
+
+Sequence< OUString > SAL_CALL ImageManager::getAllImageNames( ::sal_Int16 nImageType )
+{
+ return m_pImpl->getAllImageNames( nImageType );
+}
+
+sal_Bool SAL_CALL ImageManager::hasImage( ::sal_Int16 nImageType, const OUString& aCommandURL )
+{
+ return m_pImpl->hasImage(nImageType,aCommandURL);
+}
+
+Sequence< uno::Reference< XGraphic > > SAL_CALL ImageManager::getImages(
+ ::sal_Int16 nImageType,
+ const Sequence< OUString >& aCommandURLSequence )
+{
+ return m_pImpl->getImages(nImageType,aCommandURLSequence);
+}
+
+void SAL_CALL ImageManager::replaceImages(
+ ::sal_Int16 nImageType,
+ const Sequence< OUString >& aCommandURLSequence,
+ const Sequence< uno::Reference< XGraphic > >& aGraphicsSequence )
+{
+ m_pImpl->replaceImages(nImageType,aCommandURLSequence,aGraphicsSequence);
+}
+
+void SAL_CALL ImageManager::removeImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence )
+{
+ m_pImpl->removeImages(nImageType,aCommandURLSequence);
+}
+
+void SAL_CALL ImageManager::insertImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence, const Sequence< uno::Reference< XGraphic > >& aGraphicSequence )
+{
+ m_pImpl->insertImages(nImageType,aCommandURLSequence,aGraphicSequence);
+}
+
+// XUIConfiguration
+void SAL_CALL ImageManager::addConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener )
+{
+ m_pImpl->addConfigurationListener(xListener);
+}
+
+void SAL_CALL ImageManager::removeConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener )
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ m_pImpl->removeConfigurationListener(xListener);
+}
+
+// XUIConfigurationPersistence
+void SAL_CALL ImageManager::reload()
+{
+ m_pImpl->reload();
+}
+
+void SAL_CALL ImageManager::store()
+{
+ m_pImpl->store();
+}
+
+void SAL_CALL ImageManager::storeToStorage( const uno::Reference< XStorage >& Storage )
+{
+ m_pImpl->storeToStorage(Storage);
+}
+
+sal_Bool SAL_CALL ImageManager::isModified()
+{
+ return m_pImpl->isModified();
+}
+
+sal_Bool SAL_CALL ImageManager::isReadOnly()
+{
+ return m_pImpl->isReadOnly();
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_ImageManager_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new framework::ImageManager(context, /*bForModule*/false));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/imagemanagerimpl.cxx b/framework/source/uiconfiguration/imagemanagerimpl.cxx
new file mode 100644
index 000000000..5211e79b9
--- /dev/null
+++ b/framework/source/uiconfiguration/imagemanagerimpl.cxx
@@ -0,0 +1,1205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "imagemanagerimpl.hxx"
+#include <utility>
+#include <xml/imagesconfiguration.hxx>
+#include <uiconfiguration/imagetype.hxx>
+#include <uiconfiguration/graphicnameaccess.hxx>
+
+#include <properties.h>
+
+#include <com/sun/star/frame/theUICommandDescription.hpp>
+#include <com/sun/star/ui/ConfigurationEvent.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IllegalAccessException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/InvalidStorageException.hpp>
+#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/ui/ImageType.hpp>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <o3tl/enumrange.hxx>
+#include <comphelper/sequence.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <vcl/filter/PngImageReader.hxx>
+#include <vcl/pngwrite.hxx>
+#include <memory>
+
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::XInterface;
+using ::com::sun::star::uno::RuntimeException;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::graphic::XGraphic;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::embed;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::ui;
+using namespace ::cppu;
+
+const sal_Int16 MAX_IMAGETYPE_VALUE = css::ui::ImageType::SIZE_32;
+
+constexpr OUStringLiteral IMAGE_FOLDER = u"images";
+constexpr OUStringLiteral BITMAPS_FOLDER = u"Bitmaps";
+
+const o3tl::enumarray<vcl::ImageType, const char*> IMAGELIST_XML_FILE =
+{
+ "sc_imagelist.xml",
+ "lc_imagelist.xml",
+ "xc_imagelist.xml"
+};
+
+const o3tl::enumarray<vcl::ImageType, const char*> BITMAP_FILE_NAMES =
+{
+ "sc_userimages.png",
+ "lc_userimages.png",
+ "xc_userimages.png"
+};
+
+namespace framework
+{
+
+static GlobalImageList* pGlobalImageList = nullptr;
+
+static std::mutex& getGlobalImageListMutex()
+{
+ static std::mutex mutex;
+ return mutex;
+}
+
+static GlobalImageList* getGlobalImageList( const uno::Reference< uno::XComponentContext >& rxContext )
+{
+ std::unique_lock guard( getGlobalImageListMutex() );
+
+ if ( pGlobalImageList == nullptr )
+ pGlobalImageList = new GlobalImageList( rxContext );
+
+ return pGlobalImageList;
+}
+
+CmdImageList::CmdImageList( uno::Reference< uno::XComponentContext > rxContext, OUString aModuleIdentifier ) :
+ m_bInitialized(false),
+ m_aModuleIdentifier(std::move( aModuleIdentifier )),
+ m_xContext(std::move( rxContext ))
+{
+}
+
+CmdImageList::~CmdImageList()
+{
+}
+
+void CmdImageList::initialize()
+{
+ if (m_bInitialized)
+ return;
+
+ const OUString aCommandImageList(UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDIMAGELIST);
+
+ Sequence<OUString> aCommandImageSeq;
+ uno::Reference<XNameAccess> xCommandDesc = frame::theUICommandDescription::get(m_xContext);
+
+ if (!m_aModuleIdentifier.isEmpty())
+ {
+ // If we have a module identifier - use to retrieve the command image name list from it.
+ // Otherwise we will use the global command image list
+ try
+ {
+ xCommandDesc->getByName(m_aModuleIdentifier) >>= xCommandDesc;
+ if (xCommandDesc.is())
+ xCommandDesc->getByName(aCommandImageList) >>= aCommandImageSeq;
+ }
+ catch (const NoSuchElementException&)
+ {
+ // Module unknown we will work with an empty command image list!
+ return;
+ }
+ }
+
+ if (xCommandDesc.is())
+ {
+ try
+ {
+ xCommandDesc->getByName(aCommandImageList) >>= aCommandImageSeq;
+ }
+ catch (const NoSuchElementException&)
+ {
+ }
+ catch (const WrappedTargetException&)
+ {
+ }
+ }
+
+ m_aResolver.registerCommands(aCommandImageSeq);
+
+ m_bInitialized = true;
+}
+
+
+Image CmdImageList::getImageFromCommandURL(vcl::ImageType nImageType, const OUString& rCommandURL)
+{
+ initialize();
+ return m_aResolver.getImageFromCommandURL(nImageType, rCommandURL);
+}
+
+bool CmdImageList::hasImage(vcl::ImageType /*nImageType*/, const OUString& rCommandURL)
+{
+ initialize();
+ return m_aResolver.hasImage(rCommandURL);
+}
+
+std::vector<OUString>& CmdImageList::getImageCommandNames()
+{
+ return m_aResolver.getCommandNames();
+}
+
+GlobalImageList::GlobalImageList( const uno::Reference< uno::XComponentContext >& rxContext ) :
+ CmdImageList( rxContext, OUString() )
+{
+}
+
+GlobalImageList::~GlobalImageList()
+{
+ std::unique_lock guard( getGlobalImageListMutex() );
+ // remove global pointer as we destroy the object now
+ pGlobalImageList = nullptr;
+}
+
+Image GlobalImageList::getImageFromCommandURL( vcl::ImageType nImageType, const OUString& rCommandURL )
+{
+ std::unique_lock guard( getGlobalImageListMutex() );
+ return CmdImageList::getImageFromCommandURL( nImageType, rCommandURL );
+}
+
+bool GlobalImageList::hasImage( vcl::ImageType nImageType, const OUString& rCommandURL )
+{
+ std::unique_lock guard( getGlobalImageListMutex() );
+ return CmdImageList::hasImage( nImageType, rCommandURL );
+}
+
+::std::vector< OUString >& GlobalImageList::getImageCommandNames()
+{
+ std::unique_lock guard( getGlobalImageListMutex() );
+ return CmdImageList::getImageCommandNames();
+}
+
+static bool implts_checkAndScaleGraphic( uno::Reference< XGraphic >& rOutGraphic, const uno::Reference< XGraphic >& rInGraphic, vcl::ImageType nImageType )
+{
+ if ( !rInGraphic.is() )
+ {
+ rOutGraphic = uno::Reference<graphic::XGraphic>();
+ return false;
+ }
+
+ static const o3tl::enumarray<vcl::ImageType, Size> BITMAP_SIZE =
+ {
+ Size(16, 16), Size(24, 24), Size(32, 32)
+ };
+
+ // Check size and scale it
+ Graphic aImage(rInGraphic);
+ if (BITMAP_SIZE[nImageType] != aImage.GetSizePixel())
+ {
+ BitmapEx aBitmap = aImage.GetBitmapEx();
+ aBitmap.Scale(BITMAP_SIZE[nImageType]);
+ aImage = Graphic(aBitmap);
+ rOutGraphic = aImage.GetXGraphic();
+ }
+ else
+ rOutGraphic = rInGraphic;
+
+ return true;
+}
+
+static vcl::ImageType implts_convertImageTypeToIndex( sal_Int16 nImageType )
+{
+ if (nImageType & css::ui::ImageType::SIZE_LARGE)
+ return vcl::ImageType::Size26;
+ else if (nImageType & css::ui::ImageType::SIZE_32)
+ return vcl::ImageType::Size32;
+ else
+ return vcl::ImageType::Size16;
+}
+
+ImageList* ImageManagerImpl::implts_getUserImageList( vcl::ImageType nImageType )
+{
+ SolarMutexGuard g;
+ if ( !m_pUserImageList[nImageType] )
+ implts_loadUserImages( nImageType, m_xUserImageStorage, m_xUserBitmapsStorage );
+
+ return m_pUserImageList[nImageType].get();
+}
+
+void ImageManagerImpl::implts_initialize()
+{
+ // Initialize the top-level structures with the storage data
+ if ( !m_xUserConfigStorage.is() )
+ return;
+
+ tools::Long nModes = m_bReadOnly ? ElementModes::READ : ElementModes::READWRITE;
+
+ try
+ {
+ m_xUserImageStorage = m_xUserConfigStorage->openStorageElement( IMAGE_FOLDER,
+ nModes );
+ if ( m_xUserImageStorage.is() )
+ {
+ m_xUserBitmapsStorage = m_xUserImageStorage->openStorageElement( BITMAPS_FOLDER,
+ nModes );
+ }
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+ catch ( const css::embed::InvalidStorageException& )
+ {
+ }
+ catch ( const css::lang::IllegalArgumentException& )
+ {
+ }
+ catch ( const css::io::IOException& )
+ {
+ }
+ catch ( const css::embed::StorageWrappedTargetException& )
+ {
+ }
+}
+
+void ImageManagerImpl::implts_loadUserImages(
+ vcl::ImageType nImageType,
+ const uno::Reference< XStorage >& xUserImageStorage,
+ const uno::Reference< XStorage >& xUserBitmapsStorage )
+{
+ SolarMutexGuard g;
+
+ if ( xUserImageStorage.is() && xUserBitmapsStorage.is() )
+ {
+ try
+ {
+ uno::Reference< XStream > xStream = xUserImageStorage->openStreamElement( OUString::createFromAscii( IMAGELIST_XML_FILE[nImageType] ),
+ ElementModes::READ );
+ uno::Reference< XInputStream > xInputStream = xStream->getInputStream();
+
+ ImageItemDescriptorList aUserImageListInfo;
+ ImagesConfiguration::LoadImages( m_xContext,
+ xInputStream,
+ aUserImageListInfo );
+ if ( !aUserImageListInfo.empty() )
+ {
+ sal_Int32 nCount = aUserImageListInfo.size();
+ std::vector< OUString > aUserImagesVector;
+ aUserImagesVector.reserve(nCount);
+ for ( sal_Int32 i=0; i < nCount; i++ )
+ {
+ const ImageItemDescriptor& rItem = aUserImageListInfo[i];
+ aUserImagesVector.push_back( rItem.aCommandURL );
+ }
+
+ uno::Reference< XStream > xBitmapStream = xUserBitmapsStorage->openStreamElement(
+ OUString::createFromAscii( BITMAP_FILE_NAMES[nImageType] ),
+ ElementModes::READ );
+
+ if ( xBitmapStream.is() )
+ {
+ BitmapEx aUserBitmap;
+ {
+ std::unique_ptr<SvStream> pSvStream(utl::UcbStreamHelper::CreateStream( xBitmapStream ));
+ vcl::PngImageReader aPngReader( *pSvStream );
+ aUserBitmap = aPngReader.read();
+ }
+
+ // Delete old image list and create a new one from the read bitmap
+ m_pUserImageList[nImageType].reset(new ImageList());
+ m_pUserImageList[nImageType]->InsertFromHorizontalStrip
+ ( aUserBitmap, aUserImagesVector );
+ return;
+ }
+ }
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+ catch ( const css::embed::InvalidStorageException& )
+ {
+ }
+ catch ( const css::lang::IllegalArgumentException& )
+ {
+ }
+ catch ( const css::io::IOException& )
+ {
+ }
+ catch ( const css::embed::StorageWrappedTargetException& )
+ {
+ }
+ }
+
+ // Destroy old image list - create a new empty one
+ m_pUserImageList[nImageType].reset(new ImageList);
+}
+
+bool ImageManagerImpl::implts_storeUserImages(
+ vcl::ImageType nImageType,
+ const uno::Reference< XStorage >& xUserImageStorage,
+ const uno::Reference< XStorage >& xUserBitmapsStorage )
+{
+ SolarMutexGuard g;
+
+ if ( !m_bModified )
+ return false;
+
+ ImageList* pImageList = implts_getUserImageList( nImageType );
+ if ( pImageList->GetImageCount() > 0 )
+ {
+ ImageItemDescriptorList aUserImageListInfo;
+
+ for ( sal_uInt16 i=0; i < pImageList->GetImageCount(); i++ )
+ {
+ ImageItemDescriptor aItem;
+ aItem.aCommandURL = pImageList->GetImageName( i );
+ aUserImageListInfo.push_back( aItem );
+ }
+
+ uno::Reference< XTransactedObject > xTransaction;
+ uno::Reference< XOutputStream > xOutputStream;
+ uno::Reference< XStream > xStream = xUserImageStorage->openStreamElement( OUString::createFromAscii( IMAGELIST_XML_FILE[nImageType] ),
+ ElementModes::WRITE|ElementModes::TRUNCATE );
+ if ( xStream.is() )
+ {
+ uno::Reference< XStream > xBitmapStream =
+ xUserBitmapsStorage->openStreamElement( OUString::createFromAscii( BITMAP_FILE_NAMES[nImageType] ),
+ ElementModes::WRITE|ElementModes::TRUNCATE );
+ if ( xBitmapStream.is() )
+ {
+ {
+ std::unique_ptr<SvStream> pSvStream(utl::UcbStreamHelper::CreateStream( xBitmapStream ));
+ vcl::PNGWriter aPngWriter( pImageList->GetAsHorizontalStrip() );
+ aPngWriter.Write( *pSvStream );
+ }
+
+ // Commit user bitmaps storage
+ xTransaction.set( xUserBitmapsStorage, UNO_QUERY );
+ if ( xTransaction.is() )
+ xTransaction->commit();
+ }
+
+ xOutputStream = xStream->getOutputStream();
+ if ( xOutputStream.is() )
+ ImagesConfiguration::StoreImages( m_xContext, xOutputStream, aUserImageListInfo );
+
+ // Commit user image storage
+ xTransaction.set( xUserImageStorage, UNO_QUERY );
+ if ( xTransaction.is() )
+ xTransaction->commit();
+ }
+
+ return true;
+ }
+ else
+ {
+ // Remove the streams from the storage, if we have no data. We have to catch
+ // the NoSuchElementException as it can be possible that there is no stream at all!
+ try
+ {
+ xUserImageStorage->removeElement( OUString::createFromAscii( IMAGELIST_XML_FILE[nImageType] ));
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+
+ try
+ {
+ xUserBitmapsStorage->removeElement( OUString::createFromAscii( BITMAP_FILE_NAMES[nImageType] ));
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+
+ uno::Reference< XTransactedObject > xTransaction;
+
+ // Commit user image storage
+ xTransaction.set( xUserImageStorage, UNO_QUERY );
+ if ( xTransaction.is() )
+ xTransaction->commit();
+
+ // Commit user bitmaps storage
+ xTransaction.set( xUserBitmapsStorage, UNO_QUERY );
+ if ( xTransaction.is() )
+ xTransaction->commit();
+
+ return true;
+ }
+
+ return false;
+}
+
+const rtl::Reference< GlobalImageList >& ImageManagerImpl::implts_getGlobalImageList()
+{
+ SolarMutexGuard g;
+
+ if ( !m_pGlobalImageList.is() )
+ m_pGlobalImageList = getGlobalImageList( m_xContext );
+ return m_pGlobalImageList;
+}
+
+CmdImageList* ImageManagerImpl::implts_getDefaultImageList()
+{
+ SolarMutexGuard g;
+
+ if ( !m_pDefaultImageList )
+ m_pDefaultImageList.reset(new CmdImageList( m_xContext, m_aModuleIdentifier ));
+
+ return m_pDefaultImageList.get();
+}
+
+ImageManagerImpl::ImageManagerImpl( uno::Reference< uno::XComponentContext > xContext, ::cppu::OWeakObject* pOwner, bool _bUseGlobal ) :
+ m_xContext(std::move( xContext ))
+ , m_pOwner(pOwner)
+ , m_aResourceString( "private:resource/images/moduleimages" )
+ , m_bUseGlobal(_bUseGlobal)
+ , m_bReadOnly( true )
+ , m_bInitialized( false )
+ , m_bModified( false )
+ , m_bDisposed( false )
+{
+ for ( vcl::ImageType n : o3tl::enumrange<vcl::ImageType>() )
+ {
+ m_pUserImageList[n] = nullptr;
+ m_bUserImageListModified[n] = false;
+ }
+}
+
+ImageManagerImpl::~ImageManagerImpl()
+{
+ clear();
+}
+
+void ImageManagerImpl::dispose()
+{
+ uno::Reference< uno::XInterface > xOwner(m_pOwner);
+ css::lang::EventObject aEvent( xOwner );
+ {
+ std::unique_lock aGuard(m_mutex);
+ m_aEventListeners.disposeAndClear( aGuard, aEvent );
+ }
+ {
+ std::unique_lock aGuard(m_mutex);
+ m_aConfigListeners.disposeAndClear( aGuard, aEvent );
+ }
+
+ {
+ SolarMutexGuard g;
+ m_xUserConfigStorage.clear();
+ m_xUserImageStorage.clear();
+ m_xUserRootCommit.clear();
+ m_bModified = false;
+ m_bDisposed = true;
+
+ // delete user and default image list on dispose
+ for (auto& n : m_pUserImageList)
+ {
+ n.reset();
+ }
+ m_pDefaultImageList.reset();
+ }
+
+}
+void ImageManagerImpl::addEventListener( const uno::Reference< XEventListener >& xListener )
+{
+ {
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+ }
+
+ std::unique_lock aGuard(m_mutex);
+ m_aEventListeners.addInterface( aGuard, xListener );
+}
+
+void ImageManagerImpl::removeEventListener( const uno::Reference< XEventListener >& xListener )
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ std::unique_lock aGuard(m_mutex);
+ m_aEventListeners.removeInterface( aGuard, xListener );
+}
+
+// XInitialization
+void ImageManagerImpl::initialize( const Sequence< Any >& aArguments )
+{
+ SolarMutexGuard g;
+
+ if ( m_bInitialized )
+ return;
+
+ for ( const Any& rArg : aArguments )
+ {
+ PropertyValue aPropValue;
+ if ( rArg >>= aPropValue )
+ {
+ if ( aPropValue.Name == "UserConfigStorage" )
+ {
+ aPropValue.Value >>= m_xUserConfigStorage;
+ }
+ else if ( aPropValue.Name == "ModuleIdentifier" )
+ {
+ aPropValue.Value >>= m_aModuleIdentifier;
+ }
+ else if ( aPropValue.Name == "UserRootCommit" )
+ {
+ aPropValue.Value >>= m_xUserRootCommit;
+ }
+ }
+ }
+
+ if ( m_xUserConfigStorage.is() )
+ {
+ uno::Reference< XPropertySet > xPropSet( m_xUserConfigStorage, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ tools::Long nOpenMode = 0;
+ if ( xPropSet->getPropertyValue("OpenMode") >>= nOpenMode )
+ m_bReadOnly = !( nOpenMode & ElementModes::WRITE );
+ }
+ }
+
+ implts_initialize();
+
+ m_bInitialized = true;
+}
+
+// XImageManagerImpl
+void ImageManagerImpl::reset()
+{
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ std::vector< OUString > aUserImageNames;
+
+ for ( vcl::ImageType i : o3tl::enumrange<vcl::ImageType>() )
+ {
+ aUserImageNames.clear();
+ ImageList* pImageList = implts_getUserImageList(i);
+ pImageList->GetImageNames( aUserImageNames );
+
+ Sequence< OUString > aRemoveList( comphelper::containerToSequence(aUserImageNames) );
+
+ // Remove images
+ removeImages( sal_Int16( i ), aRemoveList );
+ m_bUserImageListModified[i] = true;
+ }
+
+ m_bModified = true;
+}
+
+Sequence< OUString > ImageManagerImpl::getAllImageNames( ::sal_Int16 nImageType )
+{
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ ImageNameMap aImageCmdNameMap;
+
+ vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
+
+ sal_uInt32 i( 0 );
+ if ( m_bUseGlobal )
+ {
+ rtl::Reference< GlobalImageList > rGlobalImageList = implts_getGlobalImageList();
+
+ const std::vector< OUString >& rGlobalImageNameVector = rGlobalImageList->getImageCommandNames();
+ const sal_uInt32 nGlobalCount = rGlobalImageNameVector.size();
+ for ( i = 0; i < nGlobalCount; i++ )
+ aImageCmdNameMap.emplace( rGlobalImageNameVector[i], true );
+
+ const std::vector< OUString >& rModuleImageNameVector = implts_getDefaultImageList()->getImageCommandNames();
+ const sal_uInt32 nModuleCount = rModuleImageNameVector.size();
+ for ( i = 0; i < nModuleCount; i++ )
+ aImageCmdNameMap.emplace( rModuleImageNameVector[i], true );
+ }
+
+ ImageList* pImageList = implts_getUserImageList(nIndex);
+ std::vector< OUString > rUserImageNames;
+ pImageList->GetImageNames( rUserImageNames );
+ const sal_uInt32 nUserCount = rUserImageNames.size();
+ for ( i = 0; i < nUserCount; i++ )
+ aImageCmdNameMap.emplace( rUserImageNames[i], true );
+
+ return comphelper::mapKeysToSequence( aImageCmdNameMap );
+}
+
+bool ImageManagerImpl::hasImage( ::sal_Int16 nImageType, const OUString& aCommandURL )
+{
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE ))
+ throw IllegalArgumentException();
+
+ vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
+ if ( m_bUseGlobal && implts_getGlobalImageList()->hasImage( nIndex, aCommandURL ))
+ return true;
+ else
+ {
+ if ( m_bUseGlobal && implts_getDefaultImageList()->hasImage( nIndex, aCommandURL ))
+ return true;
+ else
+ {
+ // User layer
+ ImageList* pImageList = implts_getUserImageList(nIndex);
+ if ( pImageList )
+ return ( pImageList->GetImagePos( aCommandURL ) != IMAGELIST_IMAGE_NOTFOUND );
+ }
+ }
+
+ return false;
+}
+
+namespace
+{
+ css::uno::Reference< css::graphic::XGraphic > GetXGraphic(const Image &rImage)
+ {
+ return Graphic(rImage).GetXGraphic();
+ }
+}
+
+Sequence< uno::Reference< XGraphic > > ImageManagerImpl::getImages(
+ ::sal_Int16 nImageType,
+ const Sequence< OUString >& aCommandURLSequence )
+{
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE ))
+ throw IllegalArgumentException();
+
+ Sequence< uno::Reference< XGraphic > > aGraphSeq( aCommandURLSequence.getLength() );
+
+ vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
+ rtl::Reference< GlobalImageList > rGlobalImageList;
+ CmdImageList* pDefaultImageList = nullptr;
+ if ( m_bUseGlobal )
+ {
+ rGlobalImageList = implts_getGlobalImageList();
+ pDefaultImageList = implts_getDefaultImageList();
+ }
+ ImageList* pUserImageList = implts_getUserImageList(nIndex);
+
+ // We have to search our image list in the following order:
+ // 1. user image list (read/write)
+ // 2. module image list (read)
+ // 3. global image list (read)
+ auto aGraphSeqRange = asNonConstRange(aGraphSeq);
+ sal_Int32 n = 0;
+ for ( const OUString& rURL : aCommandURLSequence )
+ {
+ Image aImage = pUserImageList->GetImage( rURL );
+ if ( !aImage && m_bUseGlobal )
+ {
+ aImage = pDefaultImageList->getImageFromCommandURL( nIndex, rURL );
+ if ( !aImage )
+ aImage = rGlobalImageList->getImageFromCommandURL( nIndex, rURL );
+ }
+
+ aGraphSeqRange[n++] = GetXGraphic(aImage);
+ }
+
+ return aGraphSeq;
+}
+
+void ImageManagerImpl::replaceImages(
+ ::sal_Int16 nImageType,
+ const Sequence< OUString >& aCommandURLSequence,
+ const Sequence< uno::Reference< XGraphic > >& aGraphicsSequence )
+{
+ rtl::Reference<GraphicNameAccess> pInsertedImages;
+ rtl::Reference<GraphicNameAccess> pReplacedImages;
+
+ {
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if (( aCommandURLSequence.getLength() != aGraphicsSequence.getLength() ) ||
+ (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE )))
+ throw IllegalArgumentException();
+
+ if ( m_bReadOnly )
+ throw IllegalAccessException();
+
+ vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
+ ImageList* pImageList = implts_getUserImageList(nIndex);
+
+ uno::Reference< XGraphic > xGraphic;
+ for ( sal_Int32 i = 0; i < aCommandURLSequence.getLength(); i++ )
+ {
+ // Check size and scale. If we don't have any graphics ignore it
+ if ( !implts_checkAndScaleGraphic( xGraphic, aGraphicsSequence[i], nIndex ))
+ continue;
+
+ sal_uInt16 nPos = pImageList->GetImagePos( aCommandURLSequence[i] );
+ if ( nPos == IMAGELIST_IMAGE_NOTFOUND )
+ {
+ pImageList->AddImage(aCommandURLSequence[i], Image(xGraphic));
+ if ( !pInsertedImages )
+ pInsertedImages = new GraphicNameAccess();
+ pInsertedImages->addElement( aCommandURLSequence[i], xGraphic );
+ }
+ else
+ {
+ pImageList->ReplaceImage(aCommandURLSequence[i], Image(xGraphic));
+ if ( !pReplacedImages )
+ pReplacedImages = new GraphicNameAccess();
+ pReplacedImages->addElement( aCommandURLSequence[i], xGraphic );
+ }
+ }
+
+ if (( pInsertedImages != nullptr ) || ( pReplacedImages != nullptr ))
+ {
+ m_bModified = true;
+ m_bUserImageListModified[nIndex] = true;
+ }
+ }
+
+ uno::Reference< uno::XInterface > xOwner(m_pOwner);
+ // Notify listeners
+ if ( pInsertedImages != nullptr )
+ {
+ ConfigurationEvent aInsertEvent;
+ aInsertEvent.aInfo <<= nImageType;
+ aInsertEvent.Accessor <<= xOwner;
+ aInsertEvent.Source = xOwner;
+ aInsertEvent.ResourceURL = m_aResourceString;
+ aInsertEvent.Element <<= uno::Reference< XNameAccess >(pInsertedImages);
+ implts_notifyContainerListener( aInsertEvent, NotifyOp_Insert );
+ }
+ if ( pReplacedImages != nullptr )
+ {
+ ConfigurationEvent aReplaceEvent;
+ aReplaceEvent.aInfo <<= nImageType;
+ aReplaceEvent.Accessor <<= xOwner;
+ aReplaceEvent.Source = xOwner;
+ aReplaceEvent.ResourceURL = m_aResourceString;
+ aReplaceEvent.ReplacedElement = Any();
+ aReplaceEvent.Element <<= uno::Reference< XNameAccess >(pReplacedImages);
+ implts_notifyContainerListener( aReplaceEvent, NotifyOp_Replace );
+ }
+}
+
+void ImageManagerImpl::removeImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence )
+{
+ rtl::Reference<GraphicNameAccess> pRemovedImages;
+ rtl::Reference<GraphicNameAccess> pReplacedImages;
+
+ {
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE ))
+ throw IllegalArgumentException();
+
+ if ( m_bReadOnly )
+ throw IllegalAccessException();
+
+ vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
+ rtl::Reference< GlobalImageList > rGlobalImageList;
+ CmdImageList* pDefaultImageList = nullptr;
+ if ( m_bUseGlobal )
+ {
+ rGlobalImageList = implts_getGlobalImageList();
+ pDefaultImageList = implts_getDefaultImageList();
+ }
+ ImageList* pImageList = implts_getUserImageList(nIndex);
+ uno::Reference<XGraphic> xEmptyGraphic;
+
+ for ( const OUString& rURL : aCommandURLSequence )
+ {
+ sal_uInt16 nPos = pImageList->GetImagePos( rURL );
+ if ( nPos != IMAGELIST_IMAGE_NOTFOUND )
+ {
+ sal_uInt16 nId = pImageList->GetImageId( nPos );
+ pImageList->RemoveImage( nId );
+
+ if ( m_bUseGlobal )
+ {
+ // Check, if we have an image in our module/global image list. If we find one =>
+ // this is a replace instead of a remove operation!
+ Image aNewImage = pDefaultImageList->getImageFromCommandURL( nIndex, rURL );
+ if ( !aNewImage )
+ aNewImage = rGlobalImageList->getImageFromCommandURL( nIndex, rURL );
+ if ( !aNewImage )
+ {
+ if ( !pRemovedImages )
+ pRemovedImages = new GraphicNameAccess();
+ pRemovedImages->addElement( rURL, xEmptyGraphic );
+ }
+ else
+ {
+ if ( !pReplacedImages )
+ pReplacedImages = new GraphicNameAccess();
+ pReplacedImages->addElement(rURL, GetXGraphic(aNewImage));
+ }
+ } // if ( m_bUseGlobal )
+ else
+ {
+ if ( !pRemovedImages )
+ pRemovedImages = new GraphicNameAccess();
+ pRemovedImages->addElement( rURL, xEmptyGraphic );
+ }
+ }
+ }
+
+ if (( pReplacedImages != nullptr ) || ( pRemovedImages != nullptr ))
+ {
+ m_bModified = true;
+ m_bUserImageListModified[nIndex] = true;
+ }
+ }
+
+ // Notify listeners
+ uno::Reference< uno::XInterface > xOwner(m_pOwner);
+ if ( pRemovedImages != nullptr )
+ {
+ ConfigurationEvent aRemoveEvent;
+ aRemoveEvent.aInfo <<= nImageType;
+ aRemoveEvent.Accessor <<= xOwner;
+ aRemoveEvent.Source = xOwner;
+ aRemoveEvent.ResourceURL = m_aResourceString;
+ aRemoveEvent.Element <<= uno::Reference< XNameAccess >(pRemovedImages);
+ implts_notifyContainerListener( aRemoveEvent, NotifyOp_Remove );
+ }
+ if ( pReplacedImages != nullptr )
+ {
+ ConfigurationEvent aReplaceEvent;
+ aReplaceEvent.aInfo <<= nImageType;
+ aReplaceEvent.Accessor <<= xOwner;
+ aReplaceEvent.Source = xOwner;
+ aReplaceEvent.ResourceURL = m_aResourceString;
+ aReplaceEvent.ReplacedElement = Any();
+ aReplaceEvent.Element <<= uno::Reference< XNameAccess >(pReplacedImages);
+ implts_notifyContainerListener( aReplaceEvent, NotifyOp_Replace );
+ }
+}
+
+void ImageManagerImpl::insertImages( ::sal_Int16 nImageType, const Sequence< OUString >& aCommandURLSequence, const Sequence< uno::Reference< XGraphic > >& aGraphicSequence )
+{
+ replaceImages(nImageType,aCommandURLSequence,aGraphicSequence);
+}
+
+// XUIConfigurationPersistence
+void ImageManagerImpl::reload()
+{
+ SolarMutexResettableGuard aGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ CommandMap aOldUserCmdImageSet;
+ std::vector< OUString > aNewUserCmdImageSet;
+
+ if ( !m_bModified )
+ return;
+
+ for ( vcl::ImageType i : o3tl::enumrange<vcl::ImageType>() )
+ {
+ if ( !m_bDisposed && m_bUserImageListModified[i] )
+ {
+ std::vector< OUString > aOldUserCmdImageVector;
+ ImageList* pImageList = implts_getUserImageList(i);
+ pImageList->GetImageNames( aOldUserCmdImageVector );
+
+ // Fill hash map to speed up search afterwards
+ sal_uInt32 j( 0 );
+ const sal_uInt32 nOldCount = aOldUserCmdImageVector.size();
+ for ( j = 0; j < nOldCount; j++ )
+ aOldUserCmdImageSet.emplace( aOldUserCmdImageVector[j], false );
+
+ // Attention: This can make the old image list pointer invalid!
+ implts_loadUserImages( i, m_xUserImageStorage, m_xUserBitmapsStorage );
+ pImageList = implts_getUserImageList(i);
+ pImageList->GetImageNames( aNewUserCmdImageSet );
+
+ rtl::Reference<GraphicNameAccess> pInsertedImages;
+ rtl::Reference<GraphicNameAccess> pReplacedImages;
+ rtl::Reference<GraphicNameAccess> pRemovedImages;
+
+ for (auto const& newUserCmdImage : aNewUserCmdImageSet)
+ {
+ CommandMap::iterator pIter = aOldUserCmdImageSet.find(newUserCmdImage);
+ if ( pIter != aOldUserCmdImageSet.end() )
+ {
+ pIter->second = true; // mark entry as replaced
+ if ( !pReplacedImages )
+ pReplacedImages = new GraphicNameAccess();
+ pReplacedImages->addElement( newUserCmdImage,
+ GetXGraphic(pImageList->GetImage(newUserCmdImage)) );
+ }
+ else
+ {
+ if ( !pInsertedImages )
+ pInsertedImages = new GraphicNameAccess();
+ pInsertedImages->addElement( newUserCmdImage,
+ GetXGraphic(pImageList->GetImage(newUserCmdImage)) );
+ }
+ }
+
+ // Search map for unmarked entries => they have been removed from the user list
+ // through this reload operation.
+ // We have to search the module and global image list!
+ rtl::Reference< GlobalImageList > rGlobalImageList;
+ CmdImageList* pDefaultImageList = nullptr;
+ if ( m_bUseGlobal )
+ {
+ rGlobalImageList = implts_getGlobalImageList();
+ pDefaultImageList = implts_getDefaultImageList();
+ }
+ uno::Reference<XGraphic> xEmptyGraphic;
+ for (auto const& oldUserCmdImage : aOldUserCmdImageSet)
+ {
+ if ( !oldUserCmdImage.second )
+ {
+ if ( m_bUseGlobal )
+ {
+ Image aImage = pDefaultImageList->getImageFromCommandURL( i, oldUserCmdImage.first );
+ if ( !aImage )
+ aImage = rGlobalImageList->getImageFromCommandURL( i, oldUserCmdImage.first );
+
+ if ( !aImage )
+ {
+ // No image in the module/global image list => remove user image
+ if ( !pRemovedImages )
+ pRemovedImages = new GraphicNameAccess();
+ pRemovedImages->addElement( oldUserCmdImage.first, xEmptyGraphic );
+ }
+ else
+ {
+ // Image has been found in the module/global image list => replace user image
+ if ( !pReplacedImages )
+ pReplacedImages = new GraphicNameAccess();
+ pReplacedImages->addElement(oldUserCmdImage.first, GetXGraphic(aImage));
+ }
+ } // if ( m_bUseGlobal )
+ else
+ {
+ // No image in the user image list => remove user image
+ if ( !pRemovedImages )
+ pRemovedImages = new GraphicNameAccess();
+ pRemovedImages->addElement( oldUserCmdImage.first, xEmptyGraphic );
+ }
+ }
+ }
+
+ aGuard.clear();
+
+ // Now notify our listeners. Unlock mutex to prevent deadlocks
+ uno::Reference< uno::XInterface > xOwner(m_pOwner);
+ if ( pInsertedImages != nullptr )
+ {
+ ConfigurationEvent aInsertEvent;
+ aInsertEvent.aInfo <<=static_cast<sal_uInt16>(i);
+ aInsertEvent.Accessor <<= xOwner;
+ aInsertEvent.Source = xOwner;
+ aInsertEvent.ResourceURL = m_aResourceString;
+ aInsertEvent.Element <<= uno::Reference< XNameAccess >( pInsertedImages );
+ implts_notifyContainerListener( aInsertEvent, NotifyOp_Insert );
+ }
+ if ( pReplacedImages != nullptr )
+ {
+ ConfigurationEvent aReplaceEvent;
+ aReplaceEvent.aInfo <<= static_cast<sal_uInt16>(i);
+ aReplaceEvent.Accessor <<= xOwner;
+ aReplaceEvent.Source = xOwner;
+ aReplaceEvent.ResourceURL = m_aResourceString;
+ aReplaceEvent.ReplacedElement = Any();
+ aReplaceEvent.Element <<= uno::Reference< XNameAccess >( pReplacedImages );
+ implts_notifyContainerListener( aReplaceEvent, NotifyOp_Replace );
+ }
+ if ( pRemovedImages != nullptr )
+ {
+ ConfigurationEvent aRemoveEvent;
+ aRemoveEvent.aInfo <<= static_cast<sal_uInt16>(i);
+ aRemoveEvent.Accessor <<= xOwner;
+ aRemoveEvent.Source = xOwner;
+ aRemoveEvent.ResourceURL = m_aResourceString;
+ aRemoveEvent.Element <<= uno::Reference< XNameAccess >( pRemovedImages );
+ implts_notifyContainerListener( aRemoveEvent, NotifyOp_Remove );
+ }
+
+ aGuard.reset();
+ }
+ }
+}
+
+void ImageManagerImpl::store()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !m_bModified )
+ return;
+
+ bool bWritten( false );
+ for ( vcl::ImageType i : o3tl::enumrange<vcl::ImageType>() )
+ {
+ bool bSuccess = implts_storeUserImages(i, m_xUserImageStorage, m_xUserBitmapsStorage );
+ if ( bSuccess )
+ bWritten = true;
+ m_bUserImageListModified[i] = false;
+ }
+
+ if ( bWritten &&
+ m_xUserConfigStorage.is() )
+ {
+ uno::Reference< XTransactedObject > xUserConfigStorageCommit( m_xUserConfigStorage, UNO_QUERY );
+ if ( xUserConfigStorageCommit.is() )
+ xUserConfigStorageCommit->commit();
+ if ( m_xUserRootCommit.is() )
+ m_xUserRootCommit->commit();
+ }
+
+ m_bModified = false;
+}
+
+void ImageManagerImpl::storeToStorage( const uno::Reference< XStorage >& Storage )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !(m_bModified && Storage.is()) )
+ return;
+
+ tools::Long nModes = ElementModes::READWRITE;
+
+ uno::Reference< XStorage > xUserImageStorage = Storage->openStorageElement( IMAGE_FOLDER,
+ nModes );
+ if ( !xUserImageStorage.is() )
+ return;
+
+ uno::Reference< XStorage > xUserBitmapsStorage = xUserImageStorage->openStorageElement( BITMAPS_FOLDER,
+ nModes );
+ for ( vcl::ImageType i : o3tl::enumrange<vcl::ImageType>() )
+ {
+ implts_getUserImageList(i);
+ implts_storeUserImages( i, xUserImageStorage, xUserBitmapsStorage );
+ }
+
+ uno::Reference< XTransactedObject > xTransaction( Storage, UNO_QUERY );
+ if ( xTransaction.is() )
+ xTransaction->commit();
+}
+
+bool ImageManagerImpl::isModified() const
+{
+ SolarMutexGuard g;
+ return m_bModified;
+}
+
+bool ImageManagerImpl::isReadOnly() const
+{
+ SolarMutexGuard g;
+ return m_bReadOnly;
+}
+// XUIConfiguration
+void ImageManagerImpl::addConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener )
+{
+ {
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+ }
+
+ std::unique_lock aGuard(m_mutex);
+ m_aConfigListeners.addInterface( aGuard, xListener );
+}
+
+void ImageManagerImpl::removeConfigurationListener( const uno::Reference< css::ui::XUIConfigurationListener >& xListener )
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ std::unique_lock aGuard(m_mutex);
+ m_aConfigListeners.removeInterface( aGuard, xListener );
+}
+
+void ImageManagerImpl::implts_notifyContainerListener( const ConfigurationEvent& aEvent, NotifyOp eOp )
+{
+ std::unique_lock aGuard(m_mutex);
+ comphelper::OInterfaceIteratorHelper4 pIterator( aGuard, m_aConfigListeners );
+ while ( pIterator.hasMoreElements() )
+ {
+ try
+ {
+ switch ( eOp )
+ {
+ case NotifyOp_Replace:
+ pIterator.next()->elementReplaced( aEvent );
+ break;
+ case NotifyOp_Insert:
+ pIterator.next()->elementInserted( aEvent );
+ break;
+ case NotifyOp_Remove:
+ pIterator.next()->elementRemoved( aEvent );
+ break;
+ }
+ }
+ catch( const css::uno::RuntimeException& )
+ {
+ pIterator.remove(aGuard);
+ }
+ }
+}
+void ImageManagerImpl::clear()
+{
+ SolarMutexGuard g;
+
+ for (auto & n : m_pUserImageList)
+ {
+ n.reset();
+ }
+}
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/imagemanagerimpl.hxx b/framework/source/uiconfiguration/imagemanagerimpl.hxx
new file mode 100644
index 000000000..f36c9fff7
--- /dev/null
+++ b/framework/source/uiconfiguration/imagemanagerimpl.hxx
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/ui/ConfigurationEvent.hpp>
+#include <com/sun/star/ui/XUIConfigurationListener.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+
+#include <cppuhelper/weak.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <rtl/ustring.hxx>
+
+#include <rtl/ref.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+#include "CommandImageResolver.hxx"
+
+namespace framework
+{
+ class CmdImageList
+ {
+ public:
+ CmdImageList(css::uno::Reference< css::uno::XComponentContext > xContext, OUString aModuleIdentifier);
+ virtual ~CmdImageList();
+
+ virtual Image getImageFromCommandURL(vcl::ImageType nImageType, const OUString& rCommandURL);
+ virtual bool hasImage(vcl::ImageType nImageType, const OUString& rCommandURL);
+ virtual std::vector<OUString>& getImageCommandNames();
+
+ protected:
+ void initialize();
+
+ private:
+ bool m_bInitialized;
+ vcl::CommandImageResolver m_aResolver;
+
+ OUString m_aModuleIdentifier;
+ css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ };
+
+ class GlobalImageList : public CmdImageList, public salhelper::SimpleReferenceObject
+ {
+ public:
+ explicit GlobalImageList(const css::uno::Reference< css::uno::XComponentContext >& rxContext);
+ virtual ~GlobalImageList() override;
+
+ virtual Image getImageFromCommandURL( vcl::ImageType nImageType, const OUString& rCommandURL ) override;
+ virtual bool hasImage( vcl::ImageType nImageType, const OUString& rCommandURL ) override;
+ virtual ::std::vector< OUString >& getImageCommandNames() override;
+ };
+
+ class ImageManagerImpl
+ {
+ public:
+ ImageManagerImpl(css::uno::Reference< css::uno::XComponentContext > xContext
+ ,::cppu::OWeakObject *pOwner
+ ,bool _bUseGlobal);
+ ~ImageManagerImpl();
+
+ void dispose();
+ void initialize( const css::uno::Sequence< css::uno::Any >& aArguments );
+ /// @throws css::uno::RuntimeException
+ void addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener );
+ /// @throws css::uno::RuntimeException
+ void removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener );
+
+ // XImageManager
+ /// @throws css::uno::RuntimeException
+ /// @throws css::lang::IllegalAccessException
+ void reset();
+ /// @throws css::uno::RuntimeException
+ css::uno::Sequence< OUString > getAllImageNames( ::sal_Int16 nImageType );
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ bool hasImage( ::sal_Int16 nImageType, const OUString& aCommandURL );
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ css::uno::Sequence< css::uno::Reference< css::graphic::XGraphic > > getImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aCommandURLSequence );
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::lang::IllegalAccessException
+ /// @throws css::uno::RuntimeException
+ void replaceImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aCommandURLSequence, const css::uno::Sequence< css::uno::Reference< css::graphic::XGraphic > >& aGraphicsSequence );
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::lang::IllegalAccessException
+ /// @throws css::uno::RuntimeException
+ void removeImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aResourceURLSequence );
+ /// @throws css::container::ElementExistException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::lang::IllegalAccessException
+ /// @throws css::uno::RuntimeException
+ void insertImages( ::sal_Int16 nImageType, const css::uno::Sequence< OUString >& aCommandURLSequence, const css::uno::Sequence< css::uno::Reference< css::graphic::XGraphic > >& aGraphicSequence );
+
+ // XUIConfiguration
+ /// @throws css::uno::RuntimeException
+ void addConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener );
+ /// @throws css::uno::RuntimeException
+ void removeConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener );
+
+ // XUIConfigurationPersistence
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void reload();
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void store();
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void storeToStorage( const css::uno::Reference< css::embed::XStorage >& Storage );
+ /// @throws css::uno::RuntimeException
+ bool isModified() const;
+ /// @throws css::uno::RuntimeException
+ bool isReadOnly() const;
+
+ void clear();
+
+ typedef std::unordered_map< OUString,
+ sal_Bool > ImageNameMap;
+
+ enum NotifyOp
+ {
+ NotifyOp_Remove,
+ NotifyOp_Insert,
+ NotifyOp_Replace
+ };
+
+ typedef ::std::vector< css::ui::ConfigurationEvent > ConfigEventNotifyContainer;
+
+ void implts_initialize();
+ void implts_notifyContainerListener( const css::ui::ConfigurationEvent& aEvent, NotifyOp eOp );
+ ImageList* implts_getUserImageList( vcl::ImageType nImageType );
+ void implts_loadUserImages( vcl::ImageType nImageType,
+ const css::uno::Reference< css::embed::XStorage >& xUserImageStorage,
+ const css::uno::Reference< css::embed::XStorage >& xUserBitmapsStorage );
+ bool implts_storeUserImages( vcl::ImageType nImageType,
+ const css::uno::Reference< css::embed::XStorage >& xUserImageStorage,
+ const css::uno::Reference< css::embed::XStorage >& xUserBitmapsStorage );
+ const rtl::Reference< GlobalImageList >& implts_getGlobalImageList();
+ CmdImageList* implts_getDefaultImageList();
+
+ css::uno::Reference< css::embed::XStorage > m_xUserConfigStorage;
+ css::uno::Reference< css::embed::XStorage > m_xUserImageStorage;
+ css::uno::Reference< css::embed::XStorage > m_xUserBitmapsStorage;
+ css::uno::Reference< css::embed::XTransactedObject > m_xUserRootCommit;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ ::cppu::OWeakObject* m_pOwner;
+ rtl::Reference< GlobalImageList > m_pGlobalImageList;
+ std::unique_ptr<CmdImageList> m_pDefaultImageList;
+ OUString m_aModuleIdentifier;
+ OUString m_aResourceString;
+ std::mutex m_mutex;
+ comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_aEventListeners;
+ comphelper::OInterfaceContainerHelper4<css::ui::XUIConfigurationListener> m_aConfigListeners;
+ o3tl::enumarray<vcl::ImageType,std::unique_ptr<ImageList>> m_pUserImageList;
+ o3tl::enumarray<vcl::ImageType,bool> m_bUserImageListModified;
+ bool m_bUseGlobal;
+ bool m_bReadOnly;
+ bool m_bInitialized;
+ bool m_bModified;
+ bool m_bDisposed;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/moduleuicfgsupplier.cxx b/framework/source/uiconfiguration/moduleuicfgsupplier.cxx
new file mode 100644
index 000000000..cdbd647c3
--- /dev/null
+++ b/framework/source/uiconfiguration/moduleuicfgsupplier.cxx
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdtypes.h>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/ui/ModuleUIConfigurationManager.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/ui/XModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/XUIConfigurationManager.hpp>
+#include <com/sun/star/ui/XModuleUIConfigurationManager2.hpp>
+#include <com/sun/star/frame/XModuleManager2.hpp>
+
+#include <comphelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <unordered_map>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::io;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::embed;
+using namespace ::com::sun::star::ui;
+using namespace ::com::sun::star::frame;
+using namespace framework;
+
+namespace {
+
+typedef comphelper::WeakComponentImplHelper<
+ css::lang::XServiceInfo,
+ css::ui::XModuleUIConfigurationManagerSupplier >
+ ModuleUIConfigurationManagerSupplier_BASE;
+
+class ModuleUIConfigurationManagerSupplier : public ModuleUIConfigurationManagerSupplier_BASE
+{
+public:
+ explicit ModuleUIConfigurationManagerSupplier( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~ModuleUIConfigurationManagerSupplier() override;
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.ModuleUIConfigurationManagerSupplier";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.ui.ModuleUIConfigurationManagerSupplier"};
+ }
+
+ // XModuleUIConfigurationManagerSupplier
+ virtual css::uno::Reference< css::ui::XUIConfigurationManager > SAL_CALL getUIConfigurationManager( const OUString& ModuleIdentifier ) override;
+
+private:
+ virtual void disposing(std::unique_lock<std::mutex>&) final override;
+
+ typedef std::unordered_map< OUString, css::uno::Reference< css::ui::XModuleUIConfigurationManager2 > > ModuleToModuleCfgMgr;
+
+//TODO_AS void impl_initStorages();
+
+ ModuleToModuleCfgMgr m_aModuleToModuleUICfgMgrMap;
+ css::uno::Reference< css::frame::XModuleManager2 > m_xModuleMgr;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+};
+
+ModuleUIConfigurationManagerSupplier::ModuleUIConfigurationManagerSupplier( const Reference< XComponentContext >& xContext ) :
+ m_xModuleMgr( ModuleManager::create( xContext ) )
+ , m_xContext( xContext )
+{
+ try
+ {
+ // Retrieve known modules and insert them into our unordered_map to speed-up access time.
+ Reference< XNameAccess > xNameAccess( m_xModuleMgr, UNO_QUERY_THROW );
+ const Sequence< OUString > aNameSeq = xNameAccess->getElementNames();
+ for ( const OUString& rName : aNameSeq )
+ m_aModuleToModuleUICfgMgrMap.emplace( rName, Reference< XModuleUIConfigurationManager2 >() );
+ }
+ catch(...)
+ {
+ }
+}
+
+ModuleUIConfigurationManagerSupplier::~ModuleUIConfigurationManagerSupplier()
+{
+ std::unique_lock g(m_aMutex);
+ disposing(g);
+}
+
+void ModuleUIConfigurationManagerSupplier::disposing(std::unique_lock<std::mutex>&)
+{
+ // dispose all our module user interface configuration managers
+ for (auto const& elem : m_aModuleToModuleUICfgMgrMap)
+ {
+ Reference< XComponent > xComponent( elem.second, UNO_QUERY );
+ if ( xComponent.is() )
+ xComponent->dispose();
+ }
+ m_aModuleToModuleUICfgMgrMap.clear();
+ m_xModuleMgr.clear();
+}
+
+// XModuleUIConfigurationManagerSupplier
+Reference< XUIConfigurationManager > SAL_CALL ModuleUIConfigurationManagerSupplier::getUIConfigurationManager( const OUString& sModuleIdentifier )
+{
+ std::unique_lock g(m_aMutex);
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ ModuleToModuleCfgMgr::iterator pIter = m_aModuleToModuleUICfgMgrMap.find( sModuleIdentifier );
+ if ( pIter == m_aModuleToModuleUICfgMgrMap.end() )
+ throw NoSuchElementException();
+//TODO_AS impl_initStorages();
+
+ // Create instance on demand
+ if ( !pIter->second.is() )
+ {
+ OUString sShort;
+ try
+ {
+ Sequence< PropertyValue > lProps;
+ m_xModuleMgr->getByName(sModuleIdentifier) >>= lProps;
+ for (PropertyValue const & rProp : std::as_const(lProps))
+ {
+ if ( rProp.Name == "ooSetupFactoryShortName" )
+ {
+ rProp.Value >>= sShort;
+ break;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ sShort.clear();
+ }
+
+ if (sShort.isEmpty())
+ throw NoSuchElementException();
+
+ pIter->second = css::ui::ModuleUIConfigurationManager::createDefault(m_xContext, sShort, sModuleIdentifier);
+ }
+
+ return pIter->second;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_ModuleUIConfigurationManagerSupplier_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ModuleUIConfigurationManagerSupplier(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/moduleuiconfigurationmanager.cxx b/framework/source/uiconfiguration/moduleuiconfigurationmanager.cxx
new file mode 100644
index 000000000..ded9d7770
--- /dev/null
+++ b/framework/source/uiconfiguration/moduleuiconfigurationmanager.cxx
@@ -0,0 +1,1655 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <accelerators/presethandler.hxx>
+#include <uiconfiguration/imagemanager.hxx>
+#include <uielement/constitemcontainer.hxx>
+#include <uielement/rootitemcontainer.hxx>
+#include <uielement/uielementtypenames.hxx>
+#include <menuconfiguration.hxx>
+#include <toolboxconfiguration.hxx>
+
+#include <statusbarconfiguration.hxx>
+
+#include <com/sun/star/ui/UIElementType.hpp>
+#include <com/sun/star/ui/ConfigurationEvent.hpp>
+#include <com/sun/star/ui/ModuleAcceleratorConfiguration.hpp>
+#include <com/sun/star/ui/XModuleUIConfigurationManager2.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IllegalAccessException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/InvalidStorageException.hpp>
+#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <o3tl/string_view.hxx>
+#include <memory>
+#include <mutex>
+#include <string_view>
+
+using namespace css;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::io;
+using namespace com::sun::star::embed;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+using namespace com::sun::star::beans;
+using namespace framework;
+
+constexpr OUStringLiteral RESOURCETYPE_MENUBAR = u"menubar";
+constexpr OUStringLiteral RESOURCETYPE_TOOLBAR = u"toolbar";
+constexpr OUStringLiteral RESOURCETYPE_STATUSBAR = u"statusbar";
+constexpr OUStringLiteral RESOURCETYPE_POPUPMENU = u"popupmenu";
+
+namespace {
+
+class ModuleUIConfigurationManager : public cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::lang::XComponent,
+ css::ui::XModuleUIConfigurationManager2 >
+{
+public:
+ ModuleUIConfigurationManager(
+ const css::uno::Reference< css::uno::XComponentContext >& xServiceManager,
+ const css::uno::Sequence< css::uno::Any >& aArguments);
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.ModuleUIConfigurationManager";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.ui.ModuleUIConfigurationManager"};
+ }
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+ // XUIConfiguration
+ virtual void SAL_CALL addConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override;
+ virtual void SAL_CALL removeConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override;
+
+ // XUIConfigurationManager
+ virtual void SAL_CALL reset() override;
+ virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getUIElementsInfo( sal_Int16 ElementType ) override;
+ virtual css::uno::Reference< css::container::XIndexContainer > SAL_CALL createSettings( ) override;
+ virtual sal_Bool SAL_CALL hasSettings( const OUString& ResourceURL ) override;
+ virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getSettings( const OUString& ResourceURL, sal_Bool bWriteable ) override;
+ virtual void SAL_CALL replaceSettings( const OUString& ResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override;
+ virtual void SAL_CALL removeSettings( const OUString& ResourceURL ) override;
+ virtual void SAL_CALL insertSettings( const OUString& NewResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override;
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getImageManager() override;
+ virtual css::uno::Reference< css::ui::XAcceleratorConfiguration > SAL_CALL getShortCutManager() override;
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getEventsManager() override;
+
+ // XModuleUIConfigurationManager
+ virtual sal_Bool SAL_CALL isDefaultSettings( const OUString& ResourceURL ) override;
+ virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getDefaultSettings( const OUString& ResourceURL ) override;
+
+ // XUIConfigurationPersistence
+ virtual void SAL_CALL reload() override;
+ virtual void SAL_CALL store() override;
+ virtual void SAL_CALL storeToStorage( const css::uno::Reference< css::embed::XStorage >& Storage ) override;
+ virtual sal_Bool SAL_CALL isModified() override;
+ virtual sal_Bool SAL_CALL isReadOnly() override;
+
+private:
+ // private data types
+ enum Layer
+ {
+ LAYER_DEFAULT,
+ LAYER_USERDEFINED,
+ LAYER_COUNT
+ };
+
+ enum NotifyOp
+ {
+ NotifyOp_Remove,
+ NotifyOp_Insert,
+ NotifyOp_Replace
+ };
+
+ struct UIElementInfo
+ {
+ UIElementInfo( OUString _aResourceURL, OUString _aUIName ) :
+ aResourceURL(std::move( _aResourceURL)), aUIName(std::move( _aUIName )) {}
+ OUString aResourceURL;
+ OUString aUIName;
+ };
+
+ struct UIElementData
+ {
+ UIElementData() : bModified( false ), bDefault( true ), bDefaultNode( true ) {};
+
+ OUString aResourceURL;
+ OUString aName;
+ bool bModified; // has been changed since last storing
+ bool bDefault; // default settings
+ bool bDefaultNode; // this is a default layer element data
+ css::uno::Reference< css::container::XIndexAccess > xSettings;
+ };
+
+ typedef std::unordered_map< OUString, UIElementData > UIElementDataHashMap;
+
+ struct UIElementType
+ {
+ UIElementType() : bModified( false ),
+ bLoaded( false ),
+ nElementType( css::ui::UIElementType::UNKNOWN ) {}
+
+ bool bModified;
+ bool bLoaded;
+ sal_Int16 nElementType;
+ UIElementDataHashMap aElementsHashMap;
+ css::uno::Reference< css::embed::XStorage > xStorage;
+ };
+
+ typedef std::vector< UIElementType > UIElementTypesVector;
+ typedef std::vector< css::ui::ConfigurationEvent > ConfigEventNotifyContainer;
+ typedef std::unordered_map< OUString, UIElementInfo > UIElementInfoHashMap;
+
+ void impl_Initialize();
+ void implts_notifyContainerListener( const css::ui::ConfigurationEvent& aEvent, NotifyOp eOp );
+ void impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType );
+ void impl_preloadUIElementTypeList( Layer eLayer, sal_Int16 nElementType );
+ UIElementData* impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad = true );
+ void impl_requestUIElementData( sal_Int16 nElementType, Layer eLayer, UIElementData& aUIElementData );
+ void impl_storeElementTypeData( const css::uno::Reference< css::embed::XStorage >& xStorage, UIElementType& rElementType, bool bResetModifyState = true );
+ void impl_resetElementTypeData( UIElementType& rUserElementType, UIElementType const & rDefaultElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer, ConfigEventNotifyContainer& rReplaceNotifyContainer );
+ void impl_reloadElementTypeData( UIElementType& rUserElementType, UIElementType const & rDefaultElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer, ConfigEventNotifyContainer& rReplaceNotifyContainer );
+
+ UIElementTypesVector m_aUIElements[LAYER_COUNT];
+ std::unique_ptr<PresetHandler> m_pStorageHandler[css::ui::UIElementType::COUNT];
+ css::uno::Reference< css::embed::XStorage > m_xDefaultConfigStorage;
+ css::uno::Reference< css::embed::XStorage > m_xUserConfigStorage;
+ bool m_bReadOnly;
+ bool m_bModified;
+ bool m_bDisposed;
+ OUString m_aXMLPostfix;
+ OUString m_aPropUIName;
+ OUString m_aModuleIdentifier;
+ css::uno::Reference< css::embed::XTransactedObject > m_xUserRootCommit;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ std::mutex m_mutex;
+ comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_aEventListeners;
+ comphelper::OInterfaceContainerHelper4<css::ui::XUIConfigurationListener> m_aConfigListeners;
+ rtl::Reference< ImageManager > m_xModuleImageManager;
+ css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xModuleAcceleratorManager;
+};
+
+// important: The order and position of the elements must match the constant
+// definition of "css::ui::UIElementType"
+std::u16string_view UIELEMENTTYPENAMES[] =
+{
+ u"", // Dummy value for unknown!
+ u"" UIELEMENTTYPE_MENUBAR_NAME,
+ u"" UIELEMENTTYPE_POPUPMENU_NAME,
+ u"" UIELEMENTTYPE_TOOLBAR_NAME,
+ u"" UIELEMENTTYPE_STATUSBAR_NAME,
+ u"" UIELEMENTTYPE_FLOATINGWINDOW_NAME,
+ u"" UIELEMENTTYPE_PROGRESSBAR_NAME,
+ u"" UIELEMENTTYPE_TOOLPANEL_NAME
+};
+
+const char RESOURCEURL_PREFIX[] = "private:resource/";
+const sal_Int32 RESOURCEURL_PREFIX_SIZE = strlen(RESOURCEURL_PREFIX);
+
+sal_Int16 RetrieveTypeFromResourceURL( const OUString& aResourceURL )
+{
+
+ if (( aResourceURL.startsWith( RESOURCEURL_PREFIX ) ) &&
+ ( aResourceURL.getLength() > RESOURCEURL_PREFIX_SIZE ))
+ {
+ std::u16string_view aTmpStr = aResourceURL.subView( RESOURCEURL_PREFIX_SIZE );
+ size_t nIndex = aTmpStr.find( '/' );
+ if (( nIndex > 0 ) && ( aTmpStr.size() > nIndex ))
+ {
+ std::u16string_view aTypeStr( aTmpStr.substr( 0, nIndex ));
+ for ( int i = 0; i < ui::UIElementType::COUNT; i++ )
+ {
+ if ( aTypeStr == UIELEMENTTYPENAMES[i] )
+ return sal_Int16( i );
+ }
+ }
+ }
+
+ return ui::UIElementType::UNKNOWN;
+}
+
+OUString RetrieveNameFromResourceURL( const OUString& aResourceURL )
+{
+ if (( aResourceURL.startsWith( RESOURCEURL_PREFIX ) ) &&
+ ( aResourceURL.getLength() > RESOURCEURL_PREFIX_SIZE ))
+ {
+ sal_Int32 nIndex = aResourceURL.lastIndexOf( '/' );
+ if (( nIndex > 0 ) && (( nIndex+1 ) < aResourceURL.getLength()))
+ return aResourceURL.copy( nIndex+1 );
+ }
+
+ return OUString();
+}
+
+void ModuleUIConfigurationManager::impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType )
+{
+ // preload list of element types on demand
+ impl_preloadUIElementTypeList( LAYER_USERDEFINED, nElementType );
+ impl_preloadUIElementTypeList( LAYER_DEFAULT, nElementType );
+
+ UIElementDataHashMap& rUserElements = m_aUIElements[LAYER_USERDEFINED][nElementType].aElementsHashMap;
+
+ OUString aCustomUrlPrefix( "custom_" );
+ for (auto const& userElement : rUserElements)
+ {
+ sal_Int32 nIndex = userElement.second.aResourceURL.indexOf( aCustomUrlPrefix, RESOURCEURL_PREFIX_SIZE );
+ if ( nIndex > RESOURCEURL_PREFIX_SIZE )
+ {
+ // Performance: Retrieve user interface name only for custom user interface elements.
+ // It's only used by them!
+ UIElementData* pDataSettings = impl_findUIElementData( userElement.second.aResourceURL, nElementType );
+ if ( pDataSettings )
+ {
+ // Retrieve user interface name from XPropertySet interface
+ OUString aUIName;
+ Reference< XPropertySet > xPropSet( pDataSettings->xSettings, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ Any a = xPropSet->getPropertyValue( m_aPropUIName );
+ a >>= aUIName;
+ }
+
+ UIElementInfo aInfo( userElement.second.aResourceURL, aUIName );
+ aUIElementInfoCollection.emplace( userElement.second.aResourceURL, aInfo );
+ }
+ }
+ else
+ {
+ // The user interface name for standard user interface elements is stored in the WindowState.xcu file
+ UIElementInfo aInfo( userElement.second.aResourceURL, OUString() );
+ aUIElementInfoCollection.emplace( userElement.second.aResourceURL, aInfo );
+ }
+ }
+
+ UIElementDataHashMap& rDefaultElements = m_aUIElements[LAYER_DEFAULT][nElementType].aElementsHashMap;
+
+ for (auto const& defaultElement : rDefaultElements)
+ {
+ UIElementInfoHashMap::const_iterator pIterInfo = aUIElementInfoCollection.find( defaultElement.second.aResourceURL );
+ if ( pIterInfo == aUIElementInfoCollection.end() )
+ {
+ sal_Int32 nIndex = defaultElement.second.aResourceURL.indexOf( aCustomUrlPrefix, RESOURCEURL_PREFIX_SIZE );
+ if ( nIndex > RESOURCEURL_PREFIX_SIZE )
+ {
+ // Performance: Retrieve user interface name only for custom user interface elements.
+ // It's only used by them!
+ UIElementData* pDataSettings = impl_findUIElementData( defaultElement.second.aResourceURL, nElementType );
+ if ( pDataSettings )
+ {
+ // Retrieve user interface name from XPropertySet interface
+ OUString aUIName;
+ Reference< XPropertySet > xPropSet( pDataSettings->xSettings, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ Any a = xPropSet->getPropertyValue( m_aPropUIName );
+ a >>= aUIName;
+ }
+ UIElementInfo aInfo( defaultElement.second.aResourceURL, aUIName );
+ aUIElementInfoCollection.emplace( defaultElement.second.aResourceURL, aInfo );
+ }
+ }
+ else
+ {
+ // The user interface name for standard user interface elements is stored in the WindowState.xcu file
+ UIElementInfo aInfo( defaultElement.second.aResourceURL, OUString() );
+ aUIElementInfoCollection.emplace( defaultElement.second.aResourceURL, aInfo );
+ }
+ }
+ }
+}
+
+void ModuleUIConfigurationManager::impl_preloadUIElementTypeList( Layer eLayer, sal_Int16 nElementType )
+{
+ UIElementType& rElementTypeData = m_aUIElements[eLayer][nElementType];
+
+ if ( rElementTypeData.bLoaded )
+ return;
+
+ Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage;
+ if ( !xElementTypeStorage.is() )
+ return;
+
+ OUString aResURLPrefix =
+ OUString::Concat(RESOURCEURL_PREFIX) +
+ UIELEMENTTYPENAMES[ nElementType ] +
+ "/";
+
+ UIElementDataHashMap& rHashMap = rElementTypeData.aElementsHashMap;
+ const Sequence< OUString > aUIElementNames = xElementTypeStorage->getElementNames();
+ for ( OUString const & rElementName : aUIElementNames )
+ {
+ UIElementData aUIElementData;
+
+ // Resource name must be without ".xml"
+ sal_Int32 nIndex = rElementName.lastIndexOf( '.' );
+ if (( nIndex > 0 ) && ( nIndex < rElementName.getLength() ))
+ {
+ std::u16string_view aExtension( rElementName.subView( nIndex+1 ));
+ std::u16string_view aUIElementName( rElementName.subView( 0, nIndex ));
+
+ if (!aUIElementName.empty() &&
+ ( o3tl::equalsIgnoreAsciiCase(aExtension, u"xml")))
+ {
+ aUIElementData.aResourceURL = aResURLPrefix + aUIElementName;
+ aUIElementData.aName = rElementName;
+
+ if ( eLayer == LAYER_USERDEFINED )
+ {
+ aUIElementData.bModified = false;
+ aUIElementData.bDefault = false;
+ aUIElementData.bDefaultNode = false;
+ }
+
+ // Create std::unordered_map entries for all user interface elements inside the storage. We don't load the
+ // settings to speed up the process.
+ rHashMap.emplace( aUIElementData.aResourceURL, aUIElementData );
+ }
+ }
+ rElementTypeData.bLoaded = true;
+ }
+
+}
+
+void ModuleUIConfigurationManager::impl_requestUIElementData( sal_Int16 nElementType, Layer eLayer, UIElementData& aUIElementData )
+{
+ UIElementType& rElementTypeData = m_aUIElements[eLayer][nElementType];
+
+ Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage;
+ if ( xElementTypeStorage.is() && !aUIElementData.aName.isEmpty() )
+ {
+ try
+ {
+ Reference< XStream > xStream = xElementTypeStorage->openStreamElement( aUIElementData.aName, ElementModes::READ );
+ Reference< XInputStream > xInputStream = xStream->getInputStream();
+
+ if ( xInputStream.is() )
+ {
+ switch ( nElementType )
+ {
+ case css::ui::UIElementType::UNKNOWN:
+ break;
+
+ case css::ui::UIElementType::MENUBAR:
+ case css::ui::UIElementType::POPUPMENU:
+ {
+ try
+ {
+ MenuConfiguration aMenuCfg( m_xContext );
+ Reference< XIndexAccess > xContainer( aMenuCfg.CreateMenuBarConfigurationFromXML( xInputStream ));
+ auto pRootItemContainer = comphelper::getFromUnoTunnel<RootItemContainer>( xContainer );
+ if ( pRootItemContainer )
+ aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true );
+ else
+ aUIElementData.xSettings = new ConstItemContainer( xContainer, true );
+ return;
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ }
+ break;
+
+ case css::ui::UIElementType::TOOLBAR:
+ {
+ try
+ {
+ Reference< XIndexContainer > xIndexContainer( new RootItemContainer() );
+ ToolBoxConfiguration::LoadToolBox( m_xContext, xInputStream, xIndexContainer );
+ auto pRootItemContainer = comphelper::getFromUnoTunnel<RootItemContainer>( xIndexContainer );
+ aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true );
+ return;
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+
+ break;
+ }
+
+ case css::ui::UIElementType::STATUSBAR:
+ {
+ try
+ {
+ Reference< XIndexContainer > xIndexContainer( new RootItemContainer() );
+ StatusBarConfiguration::LoadStatusBar( m_xContext, xInputStream, xIndexContainer );
+ auto pRootItemContainer = comphelper::getFromUnoTunnel<RootItemContainer>( xIndexContainer );
+ aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true );
+ return;
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+
+ break;
+ }
+
+ case css::ui::UIElementType::FLOATINGWINDOW:
+ {
+ break;
+ }
+ }
+ }
+ }
+ catch ( const css::embed::InvalidStorageException& )
+ {
+ }
+ catch ( const css::lang::IllegalArgumentException& )
+ {
+ }
+ catch ( const css::io::IOException& )
+ {
+ }
+ catch ( const css::embed::StorageWrappedTargetException& )
+ {
+ }
+ }
+
+ // At least we provide an empty settings container!
+ aUIElementData.xSettings = new ConstItemContainer();
+}
+
+ModuleUIConfigurationManager::UIElementData* ModuleUIConfigurationManager::impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad )
+{
+ // preload list of element types on demand
+ impl_preloadUIElementTypeList( LAYER_USERDEFINED, nElementType );
+ impl_preloadUIElementTypeList( LAYER_DEFAULT, nElementType );
+
+ // first try to look into our user-defined vector/unordered_map combination
+ UIElementDataHashMap& rUserHashMap = m_aUIElements[LAYER_USERDEFINED][nElementType].aElementsHashMap;
+ UIElementDataHashMap::iterator pIter = rUserHashMap.find( aResourceURL );
+ if ( pIter != rUserHashMap.end() )
+ {
+ // Default data settings data must be retrieved from the default layer!
+ if ( !pIter->second.bDefault )
+ {
+ if ( !pIter->second.xSettings.is() && bLoad )
+ impl_requestUIElementData( nElementType, LAYER_USERDEFINED, pIter->second );
+ return &(pIter->second);
+ }
+ }
+
+ // Not successful, we have to look into our default vector/unordered_map combination
+ UIElementDataHashMap& rDefaultHashMap = m_aUIElements[LAYER_DEFAULT][nElementType].aElementsHashMap;
+ pIter = rDefaultHashMap.find( aResourceURL );
+ if ( pIter != rDefaultHashMap.end() )
+ {
+ if ( !pIter->second.xSettings.is() && bLoad )
+ impl_requestUIElementData( nElementType, LAYER_DEFAULT, pIter->second );
+ return &(pIter->second);
+ }
+
+ // Nothing has been found!
+ return nullptr;
+}
+
+void ModuleUIConfigurationManager::impl_storeElementTypeData( const Reference< XStorage >& xStorage, UIElementType& rElementType, bool bResetModifyState )
+{
+ UIElementDataHashMap& rHashMap = rElementType.aElementsHashMap;
+
+ for (auto & elem : rHashMap)
+ {
+ UIElementData& rElement = elem.second;
+ if ( rElement.bModified )
+ {
+ if ( rElement.bDefault )
+ {
+ xStorage->removeElement( rElement.aName );
+ rElement.bModified = false; // mark as not modified
+ }
+ else
+ {
+ Reference< XStream > xStream = xStorage->openStreamElement( rElement.aName, ElementModes::WRITE|ElementModes::TRUNCATE );
+ Reference< XOutputStream > xOutputStream( xStream->getOutputStream() );
+
+ if ( xOutputStream.is() )
+ {
+ switch( rElementType.nElementType )
+ {
+ case css::ui::UIElementType::MENUBAR:
+ case css::ui::UIElementType::POPUPMENU:
+ {
+ try
+ {
+ MenuConfiguration aMenuCfg( m_xContext );
+ aMenuCfg.StoreMenuBarConfigurationToXML(
+ rElement.xSettings, xOutputStream, rElementType.nElementType == css::ui::UIElementType::MENUBAR );
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ }
+ break;
+
+ case css::ui::UIElementType::TOOLBAR:
+ {
+ try
+ {
+ ToolBoxConfiguration::StoreToolBox( m_xContext, xOutputStream, rElement.xSettings );
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ }
+ break;
+
+ case css::ui::UIElementType::STATUSBAR:
+ {
+ try
+ {
+ StatusBarConfiguration::StoreStatusBar( m_xContext, xOutputStream, rElement.xSettings );
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // mark as not modified if we store to our own storage
+ if ( bResetModifyState )
+ rElement.bModified = false;
+ }
+ }
+ }
+
+ // commit element type storage
+ Reference< XTransactedObject > xTransactedObject( xStorage, UNO_QUERY );
+ if ( xTransactedObject.is() )
+ xTransactedObject->commit();
+
+ // mark UIElementType as not modified if we store to our own storage
+ if ( bResetModifyState )
+ rElementType.bModified = false;
+}
+
+// This is only allowed to be called on the LAYER_USER_DEFINED!
+void ModuleUIConfigurationManager::impl_resetElementTypeData(
+ UIElementType& rUserElementType,
+ UIElementType const & rDefaultElementType,
+ ConfigEventNotifyContainer& rRemoveNotifyContainer,
+ ConfigEventNotifyContainer& rReplaceNotifyContainer )
+{
+ UIElementDataHashMap& rHashMap = rUserElementType.aElementsHashMap;
+
+ Reference< XUIConfigurationManager > xThis(this);
+ Reference< XInterface > xIfac( xThis, UNO_QUERY );
+ sal_Int16 nType = rUserElementType.nElementType;
+
+ // Make copies of the event structures to be thread-safe. We have to unlock our mutex before calling
+ // our listeners!
+ for (auto & elem : rHashMap)
+ {
+ UIElementData& rElement = elem.second;
+ if ( !rElement.bDefault )
+ {
+ if ( rDefaultElementType.xStorage->hasByName( rElement.aName ))
+ {
+ // Replace settings with data from default layer
+ Reference< XIndexAccess > xOldSettings( rElement.xSettings );
+ impl_requestUIElementData( nType, LAYER_DEFAULT, rElement );
+
+ ui::ConfigurationEvent aReplaceEvent;
+ aReplaceEvent.ResourceURL = rElement.aResourceURL;
+ aReplaceEvent.Accessor <<= xThis;
+ aReplaceEvent.Source = xIfac;
+ aReplaceEvent.ReplacedElement <<= xOldSettings;
+ aReplaceEvent.Element <<= rElement.xSettings;
+
+ rReplaceNotifyContainer.push_back( aReplaceEvent );
+
+ // Mark element as default and not modified. That means "not active"
+ // in the user layer anymore.
+ rElement.bModified = false;
+ rElement.bDefault = true;
+ }
+ else
+ {
+ // Remove user-defined settings from user layer
+ ui::ConfigurationEvent aEvent;
+ aEvent.ResourceURL = rElement.aResourceURL;
+ aEvent.Accessor <<= xThis;
+ aEvent.Source = xIfac;
+ aEvent.Element <<= rElement.xSettings;
+
+ rRemoveNotifyContainer.push_back( aEvent );
+
+ // Mark element as default and not modified. That means "not active"
+ // in the user layer anymore.
+ rElement.bModified = false;
+ rElement.bDefault = true;
+ }
+ }
+ }
+
+ // Remove all settings from our user interface elements
+ rHashMap.clear();
+}
+
+void ModuleUIConfigurationManager::impl_reloadElementTypeData(
+ UIElementType& rUserElementType,
+ UIElementType const & rDefaultElementType,
+ ConfigEventNotifyContainer& rRemoveNotifyContainer,
+ ConfigEventNotifyContainer& rReplaceNotifyContainer )
+{
+ UIElementDataHashMap& rHashMap = rUserElementType.aElementsHashMap;
+
+ Reference< XUIConfigurationManager > xThis(this);
+ Reference< XInterface > xIfac( xThis, UNO_QUERY );
+ sal_Int16 nType = rUserElementType.nElementType;
+
+ for (auto & elem : rHashMap)
+ {
+ UIElementData& rElement = elem.second;
+ if ( rElement.bModified )
+ {
+ if ( rUserElementType.xStorage->hasByName( rElement.aName ))
+ {
+ // Replace settings with data from user layer
+ Reference< XIndexAccess > xOldSettings( rElement.xSettings );
+
+ impl_requestUIElementData( nType, LAYER_USERDEFINED, rElement );
+
+ ui::ConfigurationEvent aReplaceEvent;
+
+ aReplaceEvent.ResourceURL = rElement.aResourceURL;
+ aReplaceEvent.Accessor <<= xThis;
+ aReplaceEvent.Source = xIfac;
+ aReplaceEvent.ReplacedElement <<= xOldSettings;
+ aReplaceEvent.Element <<= rElement.xSettings;
+ rReplaceNotifyContainer.push_back( aReplaceEvent );
+
+ rElement.bModified = false;
+ }
+ else if ( rDefaultElementType.xStorage->hasByName( rElement.aName ))
+ {
+ // Replace settings with data from default layer
+ Reference< XIndexAccess > xOldSettings( rElement.xSettings );
+
+ impl_requestUIElementData( nType, LAYER_DEFAULT, rElement );
+
+ ui::ConfigurationEvent aReplaceEvent;
+
+ aReplaceEvent.ResourceURL = rElement.aResourceURL;
+ aReplaceEvent.Accessor <<= xThis;
+ aReplaceEvent.Source = xIfac;
+ aReplaceEvent.ReplacedElement <<= xOldSettings;
+ aReplaceEvent.Element <<= rElement.xSettings;
+ rReplaceNotifyContainer.push_back( aReplaceEvent );
+
+ // Mark element as default and not modified. That means "not active"
+ // in the user layer anymore.
+ rElement.bModified = false;
+ rElement.bDefault = true;
+ }
+ else
+ {
+ // Element settings are not in any storage => remove
+ ui::ConfigurationEvent aRemoveEvent;
+
+ aRemoveEvent.ResourceURL = rElement.aResourceURL;
+ aRemoveEvent.Accessor <<= xThis;
+ aRemoveEvent.Source = xIfac;
+ aRemoveEvent.Element <<= rElement.xSettings;
+
+ rRemoveNotifyContainer.push_back( aRemoveEvent );
+
+ // Mark element as default and not modified. That means "not active"
+ // in the user layer anymore.
+ rElement.bModified = false;
+ rElement.bDefault = true;
+ }
+ }
+ }
+
+ rUserElementType.bModified = false;
+}
+
+void ModuleUIConfigurationManager::impl_Initialize()
+{
+ // Initialize the top-level structures with the storage data
+ if ( m_xUserConfigStorage.is() )
+ {
+ // Try to access our module sub folder
+ for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT;
+ i++ )
+ {
+ Reference< XStorage > xElementTypeStorage;
+ try
+ {
+ if ( m_pStorageHandler[i] )
+ xElementTypeStorage = m_pStorageHandler[i]->getWorkingStorageUser();
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+ catch ( const css::embed::InvalidStorageException& )
+ {
+ }
+ catch ( const css::lang::IllegalArgumentException& )
+ {
+ }
+ catch ( const css::io::IOException& )
+ {
+ }
+ catch ( const css::embed::StorageWrappedTargetException& )
+ {
+ }
+
+ m_aUIElements[LAYER_USERDEFINED][i].nElementType = i;
+ m_aUIElements[LAYER_USERDEFINED][i].bModified = false;
+ m_aUIElements[LAYER_USERDEFINED][i].xStorage = xElementTypeStorage;
+ }
+ }
+
+ if ( !m_xDefaultConfigStorage.is() )
+ return;
+
+ Reference< XNameAccess > xNameAccess( m_xDefaultConfigStorage, UNO_QUERY_THROW );
+
+ // Try to access our module sub folder
+ for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT;
+ i++ )
+ {
+ Reference< XStorage > xElementTypeStorage;
+ try
+ {
+ const OUString sName( UIELEMENTTYPENAMES[i] );
+ if( xNameAccess->hasByName( sName ) )
+ xNameAccess->getByName( sName ) >>= xElementTypeStorage;
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+
+ m_aUIElements[LAYER_DEFAULT][i].nElementType = i;
+ m_aUIElements[LAYER_DEFAULT][i].bModified = false;
+ m_aUIElements[LAYER_DEFAULT][i].xStorage = xElementTypeStorage;
+ }
+}
+
+ModuleUIConfigurationManager::ModuleUIConfigurationManager(
+ const Reference< XComponentContext >& xContext,
+ const css::uno::Sequence< css::uno::Any >& aArguments)
+ : m_bReadOnly( true )
+ , m_bModified( false )
+ , m_bDisposed( false )
+ , m_aXMLPostfix( ".xml" )
+ , m_aPropUIName( "UIName" )
+ , m_xContext( xContext )
+{
+ // Make sure we have a default initialized entry for every layer and user interface element type!
+ // The following code depends on this!
+ m_aUIElements[LAYER_DEFAULT].resize( css::ui::UIElementType::COUNT );
+ m_aUIElements[LAYER_USERDEFINED].resize( css::ui::UIElementType::COUNT );
+
+ SolarMutexGuard g;
+
+ OUString aModuleShortName;
+ if( aArguments.getLength() == 2 && (aArguments[0] >>= aModuleShortName) && (aArguments[1] >>= m_aModuleIdentifier))
+ {
+ }
+ else
+ {
+ ::comphelper::SequenceAsHashMap lArgs(aArguments);
+ aModuleShortName = lArgs.getUnpackedValueOrDefault("ModuleShortName", OUString());
+ m_aModuleIdentifier = lArgs.getUnpackedValueOrDefault("ModuleIdentifier", OUString());
+ }
+
+ for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
+ {
+ OUString aResourceType;
+ if ( i == css::ui::UIElementType::MENUBAR )
+ aResourceType = RESOURCETYPE_MENUBAR;
+ else if ( i == css::ui::UIElementType::TOOLBAR )
+ aResourceType = RESOURCETYPE_TOOLBAR;
+ else if ( i == css::ui::UIElementType::STATUSBAR )
+ aResourceType = RESOURCETYPE_STATUSBAR;
+ else if ( i == css::ui::UIElementType::POPUPMENU )
+ aResourceType = RESOURCETYPE_POPUPMENU;
+
+ if ( !aResourceType.isEmpty() )
+ {
+ m_pStorageHandler[i].reset( new PresetHandler( m_xContext ) );
+ m_pStorageHandler[i]->connectToResource( PresetHandler::E_MODULES,
+ aResourceType, // this path won't be used later... see next lines!
+ aModuleShortName,
+ css::uno::Reference< css::embed::XStorage >()); // no document root used here!
+ }
+ }
+
+ // initialize root storages for all resource types
+ m_xUserRootCommit.set( m_pStorageHandler[css::ui::UIElementType::MENUBAR]->getOrCreateRootStorageUser(), css::uno::UNO_QUERY); // can be empty
+ m_xDefaultConfigStorage = m_pStorageHandler[css::ui::UIElementType::MENUBAR]->getParentStorageShare();
+ m_xUserConfigStorage = m_pStorageHandler[css::ui::UIElementType::MENUBAR]->getParentStorageUser();
+
+ if ( m_xUserConfigStorage.is() )
+ {
+ Reference< XPropertySet > xPropSet( m_xUserConfigStorage, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ tools::Long nOpenMode = 0;
+ Any a = xPropSet->getPropertyValue("OpenMode");
+ if ( a >>= nOpenMode )
+ m_bReadOnly = !( nOpenMode & ElementModes::WRITE );
+ }
+ }
+
+ impl_Initialize();
+}
+
+// XComponent
+void SAL_CALL ModuleUIConfigurationManager::dispose()
+{
+ Reference< XComponent > xThis(this);
+
+ css::lang::EventObject aEvent( xThis );
+ {
+ std::unique_lock aGuard(m_mutex);
+ m_aEventListeners.disposeAndClear( aGuard, aEvent );
+ }
+ {
+ std::unique_lock aGuard(m_mutex);
+ m_aConfigListeners.disposeAndClear( aGuard, aEvent );
+ }
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ SolarMutexClearableGuard aGuard;
+ Reference< XComponent > xModuleImageManager( m_xModuleImageManager );
+ m_xModuleImageManager.clear();
+ m_xModuleAcceleratorManager.clear();
+ m_aUIElements[LAYER_USERDEFINED].clear();
+ m_aUIElements[LAYER_DEFAULT].clear();
+ m_xDefaultConfigStorage.clear();
+ m_xUserConfigStorage.clear();
+ m_xUserRootCommit.clear();
+ m_bModified = false;
+ m_bDisposed = true;
+ aGuard.clear();
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+
+ try
+ {
+ if ( xModuleImageManager.is() )
+ xModuleImageManager->dispose();
+ }
+ catch ( const Exception& )
+ {
+ }
+}
+
+void SAL_CALL ModuleUIConfigurationManager::addEventListener( const Reference< XEventListener >& xListener )
+{
+ {
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+ }
+
+ std::unique_lock aGuard(m_mutex);
+ m_aEventListeners.addInterface( aGuard, xListener );
+}
+
+void SAL_CALL ModuleUIConfigurationManager::removeEventListener( const Reference< XEventListener >& xListener )
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ std::unique_lock aGuard(m_mutex);
+ m_aEventListeners.removeInterface( aGuard, xListener );
+}
+
+// XUIConfiguration
+void SAL_CALL ModuleUIConfigurationManager::addConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener )
+{
+ {
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+ }
+
+ std::unique_lock aGuard(m_mutex);
+ m_aConfigListeners.addInterface( aGuard, xListener );
+}
+
+void SAL_CALL ModuleUIConfigurationManager::removeConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener )
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ std::unique_lock aGuard(m_mutex);
+ m_aConfigListeners.removeInterface( aGuard, xListener );
+}
+
+// XUIConfigurationManager
+void SAL_CALL ModuleUIConfigurationManager::reset()
+{
+ SolarMutexClearableGuard aGuard;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( isReadOnly() )
+ return;
+
+ // Remove all elements from our user-defined storage!
+ try
+ {
+ for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
+ {
+ UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][i];
+
+ if ( rElementType.xStorage.is() )
+ {
+ bool bCommitSubStorage( false );
+ const Sequence< OUString > aUIElementStreamNames = rElementType.xStorage->getElementNames();
+ for ( OUString const & rName : aUIElementStreamNames )
+ {
+ rElementType.xStorage->removeElement( rName );
+ bCommitSubStorage = true;
+ }
+
+ if ( bCommitSubStorage )
+ {
+ Reference< XTransactedObject > xTransactedObject( rElementType.xStorage, UNO_QUERY );
+ if ( xTransactedObject.is() )
+ xTransactedObject->commit();
+ m_pStorageHandler[i]->commitUserChanges();
+ }
+ }
+ }
+
+ // remove settings from user defined layer and notify listener about removed settings data!
+ ConfigEventNotifyContainer aRemoveEventNotifyContainer;
+ ConfigEventNotifyContainer aReplaceEventNotifyContainer;
+ for ( sal_Int16 j = 1; j < css::ui::UIElementType::COUNT; j++ )
+ {
+ try
+ {
+ UIElementType& rUserElementType = m_aUIElements[LAYER_USERDEFINED][j];
+ UIElementType& rDefaultElementType = m_aUIElements[LAYER_DEFAULT][j];
+
+ impl_resetElementTypeData( rUserElementType, rDefaultElementType, aRemoveEventNotifyContainer, aReplaceEventNotifyContainer );
+ rUserElementType.bModified = false;
+ }
+ catch (const Exception&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "ModuleUIConfigurationManager::reset exception",
+ css::uno::Reference<css::uno::XInterface>(*this), anyEx);
+ }
+ }
+
+ m_bModified = false;
+
+ // Unlock mutex before notify our listeners
+ aGuard.clear();
+
+ // Notify our listeners
+ for ( auto const & k: aRemoveEventNotifyContainer )
+ implts_notifyContainerListener( k, NotifyOp_Remove );
+ for ( auto const & k: aReplaceEventNotifyContainer )
+ implts_notifyContainerListener( k, NotifyOp_Replace );
+ }
+ catch ( const css::lang::IllegalArgumentException& )
+ {
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+ catch ( const css::embed::InvalidStorageException& )
+ {
+ }
+ catch ( const css::embed::StorageWrappedTargetException& )
+ {
+ }
+}
+
+Sequence< Sequence< PropertyValue > > SAL_CALL ModuleUIConfigurationManager::getUIElementsInfo( sal_Int16 ElementType )
+{
+ if (( ElementType < 0 ) || ( ElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException();
+
+ SolarMutexGuard g;
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ std::vector< Sequence< PropertyValue > > aElementInfoSeq;
+ UIElementInfoHashMap aUIElementInfoCollection;
+
+ if ( ElementType == css::ui::UIElementType::UNKNOWN )
+ {
+ for ( sal_Int16 i = 0; i < css::ui::UIElementType::COUNT; i++ )
+ impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, i );
+ }
+ else
+ impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, ElementType );
+
+ aElementInfoSeq.resize( aUIElementInfoCollection.size() );
+
+ sal_Int32 n = 0;
+ for (auto const& elem : aUIElementInfoCollection)
+ {
+ Sequence< PropertyValue > aUIElementInfo{
+ comphelper::makePropertyValue("ResourceURL", elem.second.aResourceURL),
+ comphelper::makePropertyValue(m_aPropUIName, elem.second.aUIName)
+ };
+ aElementInfoSeq[n++] = aUIElementInfo;
+ }
+
+ return comphelper::containerToSequence(aElementInfoSeq);
+}
+
+Reference< XIndexContainer > SAL_CALL ModuleUIConfigurationManager::createSettings()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ // Creates an empty item container which can be filled from outside
+ return Reference< XIndexContainer >( new RootItemContainer() );
+}
+
+sal_Bool SAL_CALL ModuleUIConfigurationManager::hasSettings( const OUString& ResourceURL )
+{
+ sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
+
+ if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
+ ( nElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException();
+
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType, false );
+ if ( pDataSettings )
+ return true;
+
+ return false;
+}
+
+Reference< XIndexAccess > SAL_CALL ModuleUIConfigurationManager::getSettings( const OUString& ResourceURL, sal_Bool bWriteable )
+{
+ sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
+
+ if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
+ ( nElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException();
+
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
+ if ( pDataSettings )
+ {
+ // Create a copy of our data if someone wants to change the data.
+ if ( bWriteable )
+ return Reference< XIndexAccess >( new RootItemContainer( pDataSettings->xSettings ) );
+ else
+ return pDataSettings->xSettings;
+ }
+
+ throw NoSuchElementException();
+}
+
+void SAL_CALL ModuleUIConfigurationManager::replaceSettings( const OUString& ResourceURL, const Reference< css::container::XIndexAccess >& aNewData )
+{
+ sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
+
+ if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
+ ( nElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException();
+ else if ( m_bReadOnly )
+ throw IllegalAccessException();
+ else
+ {
+ SolarMutexClearableGuard aGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
+ if ( !pDataSettings )
+ throw NoSuchElementException();
+ if ( !pDataSettings->bDefaultNode )
+ {
+ // we have a settings entry in our user-defined layer - replace
+ Reference< XIndexAccess > xOldSettings = pDataSettings->xSettings;
+
+ // Create a copy of the data if the container is not const
+ Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY );
+ if ( xReplace.is() )
+ pDataSettings->xSettings = new ConstItemContainer( aNewData );
+ else
+ pDataSettings->xSettings = aNewData;
+ pDataSettings->bDefault = false;
+ pDataSettings->bModified = true;
+ m_bModified = true;
+
+ // Modify type container
+ UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType];
+ rElementType.bModified = true;
+
+ Reference< XUIConfigurationManager > xThis(this);
+ Reference< XInterface > xIfac( xThis, UNO_QUERY );
+
+ // Create event to notify listener about replaced element settings
+ ui::ConfigurationEvent aEvent;
+ aEvent.ResourceURL = ResourceURL;
+ aEvent.Accessor <<= xThis;
+ aEvent.Source = xIfac;
+ aEvent.ReplacedElement <<= xOldSettings;
+ aEvent.Element <<= pDataSettings->xSettings;
+
+ aGuard.clear();
+
+ implts_notifyContainerListener( aEvent, NotifyOp_Replace );
+ }
+ else
+ {
+ // we have no settings in our user-defined layer - insert
+ UIElementData aUIElementData;
+
+ aUIElementData.bDefault = false;
+ aUIElementData.bDefaultNode = false;
+ aUIElementData.bModified = true;
+
+ // Create a copy of the data if the container is not const
+ Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY );
+ if ( xReplace.is() )
+ aUIElementData.xSettings = new ConstItemContainer( aNewData );
+ else
+ aUIElementData.xSettings = aNewData;
+ aUIElementData.aName = RetrieveNameFromResourceURL( ResourceURL ) + m_aXMLPostfix;
+ aUIElementData.aResourceURL = ResourceURL;
+ m_bModified = true;
+
+ // Modify type container
+ UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType];
+ rElementType.bModified = true;
+
+ UIElementDataHashMap& rElements = rElementType.aElementsHashMap;
+
+ // Check our user element settings hash map as it can already contain settings that have been set to default!
+ // If no node can be found, we have to insert it.
+ UIElementDataHashMap::iterator pIter = rElements.find( ResourceURL );
+ if ( pIter != rElements.end() )
+ pIter->second = aUIElementData;
+ else
+ rElements.emplace( ResourceURL, aUIElementData );
+
+ Reference< XUIConfigurationManager > xThis(this);
+ Reference< XInterface > xIfac( xThis, UNO_QUERY );
+
+ // Create event to notify listener about replaced element settings
+ ui::ConfigurationEvent aEvent;
+
+ aEvent.ResourceURL = ResourceURL;
+ aEvent.Accessor <<= xThis;
+ aEvent.Source = xIfac;
+ aEvent.ReplacedElement <<= pDataSettings->xSettings;
+ aEvent.Element <<= aUIElementData.xSettings;
+
+ aGuard.clear();
+
+ implts_notifyContainerListener( aEvent, NotifyOp_Replace );
+ }
+ }
+}
+
+void SAL_CALL ModuleUIConfigurationManager::removeSettings( const OUString& ResourceURL )
+{
+ sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
+
+ if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
+ ( nElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException( "The ResourceURL is not valid or "
+ "describes an unknown type. "
+ "ResourceURL: " + ResourceURL, nullptr, 0 );
+ else if ( m_bReadOnly )
+ throw IllegalAccessException( "The configuration manager is read-only. "
+ "ResourceURL: " + ResourceURL, nullptr );
+ else
+ {
+ SolarMutexClearableGuard aGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException( "The configuration manager has been disposed, "
+ "and can't uphold its method specification anymore. "
+ "ResourceURL: " + ResourceURL, nullptr );
+
+ UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
+ if ( !pDataSettings )
+ throw NoSuchElementException( "The settings data cannot be found. "
+ "ResourceURL: " + ResourceURL, nullptr );
+ // If element settings are default, we don't need to change anything!
+ if ( pDataSettings->bDefault )
+ return;
+ else
+ {
+ Reference< XIndexAccess > xRemovedSettings = pDataSettings->xSettings;
+ pDataSettings->bDefault = true;
+
+ // check if this is a default layer node
+ if ( !pDataSettings->bDefaultNode )
+ pDataSettings->bModified = true; // we have to remove this node from the user layer!
+ pDataSettings->xSettings.clear();
+ m_bModified = true; // user layer must be written
+
+ // Modify type container
+ UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType];
+ rElementType.bModified = true;
+
+ Reference< XUIConfigurationManager > xThis(this);
+ Reference< XInterface > xIfac( xThis, UNO_QUERY );
+
+ // Check if we have settings in the default layer which replaces the user-defined one!
+ UIElementData* pDefaultDataSettings = impl_findUIElementData( ResourceURL, nElementType );
+ if ( pDefaultDataSettings )
+ {
+ // Create event to notify listener about replaced element settings
+ ui::ConfigurationEvent aEvent;
+
+ aEvent.ResourceURL = ResourceURL;
+ aEvent.Accessor <<= xThis;
+ aEvent.Source = xIfac;
+ aEvent.Element <<= xRemovedSettings;
+ aEvent.ReplacedElement <<= pDefaultDataSettings->xSettings;
+
+ aGuard.clear();
+
+ implts_notifyContainerListener( aEvent, NotifyOp_Replace );
+ }
+ else
+ {
+ // Create event to notify listener about removed element settings
+ ui::ConfigurationEvent aEvent;
+
+ aEvent.ResourceURL = ResourceURL;
+ aEvent.Accessor <<= xThis;
+ aEvent.Source = xIfac;
+ aEvent.Element <<= xRemovedSettings;
+
+ aGuard.clear();
+
+ implts_notifyContainerListener( aEvent, NotifyOp_Remove );
+ }
+ }
+ }
+}
+
+void SAL_CALL ModuleUIConfigurationManager::insertSettings( const OUString& NewResourceURL, const Reference< XIndexAccess >& aNewData )
+{
+ sal_Int16 nElementType = RetrieveTypeFromResourceURL( NewResourceURL );
+
+ if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
+ ( nElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException();
+ else if ( m_bReadOnly )
+ throw IllegalAccessException();
+ else
+ {
+ SolarMutexClearableGuard aGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ UIElementData* pDataSettings = impl_findUIElementData( NewResourceURL, nElementType );
+ if ( !(!pDataSettings) )
+ throw ElementExistException();
+ UIElementData aUIElementData;
+
+ aUIElementData.bDefault = false;
+ aUIElementData.bDefaultNode = false;
+ aUIElementData.bModified = true;
+
+ // Create a copy of the data if the container is not const
+ Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY );
+ if ( xReplace.is() )
+ aUIElementData.xSettings = new ConstItemContainer( aNewData );
+ else
+ aUIElementData.xSettings = aNewData;
+ aUIElementData.aName = RetrieveNameFromResourceURL( NewResourceURL ) + m_aXMLPostfix;
+ aUIElementData.aResourceURL = NewResourceURL;
+ m_bModified = true;
+
+ UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType];
+ rElementType.bModified = true;
+
+ UIElementDataHashMap& rElements = rElementType.aElementsHashMap;
+ rElements.emplace( NewResourceURL, aUIElementData );
+
+ Reference< XIndexAccess > xInsertSettings( aUIElementData.xSettings );
+ Reference< XUIConfigurationManager > xThis(this);
+
+ // Create event to notify listener about removed element settings
+ ui::ConfigurationEvent aEvent;
+
+ aEvent.ResourceURL = NewResourceURL;
+ aEvent.Accessor <<= xThis;
+ aEvent.Source = xThis;
+ aEvent.Element <<= xInsertSettings;
+
+ aGuard.clear();
+
+ implts_notifyContainerListener( aEvent, NotifyOp_Insert );
+ }
+}
+
+Reference< XInterface > SAL_CALL ModuleUIConfigurationManager::getImageManager()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !m_xModuleImageManager.is() )
+ {
+ m_xModuleImageManager = new ImageManager( m_xContext, /*bForModule*/true );
+
+ uno::Sequence<uno::Any> aPropSeq(comphelper::InitAnyPropertySequence(
+ {
+ {"UserConfigStorage", uno::Any(m_xUserConfigStorage)},
+ {"ModuleIdentifier", uno::Any(m_aModuleIdentifier)},
+ {"UserRootCommit", uno::Any(m_xUserRootCommit)},
+ }));
+ m_xModuleImageManager->initialize( aPropSeq );
+ }
+
+ return Reference< XInterface >( static_cast<cppu::OWeakObject*>(m_xModuleImageManager.get()), UNO_QUERY );
+}
+
+Reference< ui::XAcceleratorConfiguration > SAL_CALL ModuleUIConfigurationManager::getShortCutManager()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !m_xModuleAcceleratorManager.is() ) try
+ {
+ m_xModuleAcceleratorManager = ui::ModuleAcceleratorConfiguration::
+ createWithModuleIdentifier(m_xContext, m_aModuleIdentifier);
+ }
+ catch ( const css::uno::DeploymentException& )
+ {
+ SAL_WARN("fwk.uiconfiguration", "ModuleAcceleratorConfiguration"
+ " not available. This should happen only on mobile platforms.");
+ }
+
+ return m_xModuleAcceleratorManager;
+}
+
+Reference< XInterface > SAL_CALL ModuleUIConfigurationManager::getEventsManager()
+{
+ return Reference< XInterface >();
+}
+
+// XModuleUIConfigurationManager
+sal_Bool SAL_CALL ModuleUIConfigurationManager::isDefaultSettings( const OUString& ResourceURL )
+{
+ sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
+
+ if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
+ ( nElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException();
+
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType, false );
+ if ( pDataSettings && pDataSettings->bDefaultNode )
+ return true;
+
+ return false;
+}
+
+Reference< XIndexAccess > SAL_CALL ModuleUIConfigurationManager::getDefaultSettings( const OUString& ResourceURL )
+{
+ sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
+
+ if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
+ ( nElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException();
+
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ // preload list of element types on demand
+ impl_preloadUIElementTypeList( LAYER_DEFAULT, nElementType );
+
+ // Look into our default vector/unordered_map combination
+ UIElementDataHashMap& rDefaultHashMap = m_aUIElements[LAYER_DEFAULT][nElementType].aElementsHashMap;
+ UIElementDataHashMap::iterator pIter = rDefaultHashMap.find( ResourceURL );
+ if ( pIter != rDefaultHashMap.end() )
+ {
+ if ( !pIter->second.xSettings.is() )
+ impl_requestUIElementData( nElementType, LAYER_DEFAULT, pIter->second );
+ return pIter->second.xSettings;
+ }
+
+ // Nothing has been found!
+ throw NoSuchElementException();
+}
+
+// XUIConfigurationPersistence
+void SAL_CALL ModuleUIConfigurationManager::reload()
+{
+ SolarMutexClearableGuard aGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !m_xUserConfigStorage.is() || !m_bModified || m_bReadOnly )
+ return;
+
+ // Try to access our module sub folder
+ ConfigEventNotifyContainer aRemoveNotifyContainer;
+ ConfigEventNotifyContainer aReplaceNotifyContainer;
+ for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; i++ )
+ {
+ try
+ {
+ UIElementType& rUserElementType = m_aUIElements[LAYER_USERDEFINED][i];
+
+ if ( rUserElementType.bModified )
+ {
+ UIElementType& rDefaultElementType = m_aUIElements[LAYER_DEFAULT][i];
+ impl_reloadElementTypeData( rUserElementType, rDefaultElementType, aRemoveNotifyContainer, aReplaceNotifyContainer );
+ }
+ }
+ catch ( const Exception& )
+ {
+ throw IOException();
+ }
+ }
+
+ m_bModified = false;
+
+ // Unlock mutex before notify our listeners
+ aGuard.clear();
+
+ // Notify our listeners
+ for (const ui::ConfigurationEvent & j : aRemoveNotifyContainer)
+ implts_notifyContainerListener( j, NotifyOp_Remove );
+ for (const ui::ConfigurationEvent & k : aReplaceNotifyContainer)
+ implts_notifyContainerListener( k, NotifyOp_Replace );
+}
+
+void SAL_CALL ModuleUIConfigurationManager::store()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !m_xUserConfigStorage.is() || !m_bModified || m_bReadOnly )
+ return;
+
+ // Try to access our module sub folder
+ for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
+ {
+ try
+ {
+ UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][i];
+
+ if ( rElementType.bModified && rElementType.xStorage.is() )
+ {
+ impl_storeElementTypeData( rElementType.xStorage, rElementType );
+ m_pStorageHandler[i]->commitUserChanges();
+ }
+ }
+ catch ( const Exception& )
+ {
+ throw IOException();
+ }
+ }
+
+ m_bModified = false;
+}
+
+void SAL_CALL ModuleUIConfigurationManager::storeToStorage( const Reference< XStorage >& Storage )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !m_xUserConfigStorage.is() || !m_bModified || m_bReadOnly )
+ return;
+
+ // Try to access our module sub folder
+ for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
+ {
+ try
+ {
+ Reference< XStorage > xElementTypeStorage( Storage->openStorageElement(
+ OUString(UIELEMENTTYPENAMES[i]), ElementModes::READWRITE ));
+ UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][i];
+
+ if ( rElementType.bModified && xElementTypeStorage.is() )
+ impl_storeElementTypeData( xElementTypeStorage, rElementType, false ); // store data to storage, but don't reset modify flag!
+ }
+ catch ( const Exception& )
+ {
+ throw IOException();
+ }
+ }
+
+ Reference< XTransactedObject > xTransactedObject( Storage, UNO_QUERY );
+ if ( xTransactedObject.is() )
+ xTransactedObject->commit();
+}
+
+sal_Bool SAL_CALL ModuleUIConfigurationManager::isModified()
+{
+ SolarMutexGuard g;
+
+ return m_bModified;
+}
+
+sal_Bool SAL_CALL ModuleUIConfigurationManager::isReadOnly()
+{
+ SolarMutexGuard g;
+
+ return m_bReadOnly;
+}
+
+void ModuleUIConfigurationManager::implts_notifyContainerListener( const ui::ConfigurationEvent& aEvent, NotifyOp eOp )
+{
+ std::unique_lock aGuard(m_mutex);
+ using ListenerMethodType = void (SAL_CALL css::ui::XUIConfigurationListener::*)(const ui::ConfigurationEvent&);
+ ListenerMethodType aListenerMethod {};
+ switch ( eOp )
+ {
+ case NotifyOp_Replace:
+ aListenerMethod = &css::ui::XUIConfigurationListener::elementReplaced;
+ break;
+ case NotifyOp_Insert:
+ aListenerMethod = &css::ui::XUIConfigurationListener::elementInserted;
+ break;
+ case NotifyOp_Remove:
+ aListenerMethod = &css::ui::XUIConfigurationListener::elementRemoved;
+ break;
+ }
+ m_aConfigListeners.notifyEach(aGuard, aListenerMethod, aEvent);
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_ModuleUIConfigurationManager_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &arguments)
+{
+ return cppu::acquire(new ModuleUIConfigurationManager(context, arguments));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/uicategorydescription.cxx b/framework/source/uiconfiguration/uicategorydescription.cxx
new file mode 100644
index 000000000..620384fad
--- /dev/null
+++ b/framework/source/uiconfiguration/uicategorydescription.cxx
@@ -0,0 +1,395 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/uicommanddescription.hxx>
+
+#include <helper/mischelper.hxx>
+
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <sal/log.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unotools/syslocale.hxx>
+
+#include <comphelper/propertysequence.hxx>
+
+#include <string_view>
+#include <unordered_map>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::configuration;
+using namespace com::sun::star::container;
+using namespace framework;
+
+namespace {
+
+class ConfigurationAccess_UICategory : public ::cppu::WeakImplHelper<XNameAccess,XContainerListener>
+{
+ osl::Mutex aMutex;
+ public:
+ ConfigurationAccess_UICategory( std::u16string_view aModuleName, const Reference< XNameAccess >& xGenericUICommands, const Reference< XComponentContext >& rxContext );
+ virtual ~ConfigurationAccess_UICategory() override;
+
+ // XNameAccess
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override;
+
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // container.XContainerListener
+ virtual void SAL_CALL elementInserted( const ContainerEvent& aEvent ) override;
+ virtual void SAL_CALL elementRemoved ( const ContainerEvent& aEvent ) override;
+ virtual void SAL_CALL elementReplaced( const ContainerEvent& aEvent ) override;
+
+ // lang.XEventListener
+ virtual void SAL_CALL disposing( const EventObject& aEvent ) override;
+
+ protected:
+ Any getUINameFromID( const OUString& rId );
+ Any getUINameFromCache( const OUString& rId );
+ Sequence< OUString > getAllIds();
+ void fillCache();
+
+ private:
+ typedef std::unordered_map< OUString,
+ OUString > IdToInfoCache;
+
+ void initializeConfigAccess();
+
+ OUString m_aConfigCategoryAccess;
+ OUString m_aPropUIName;
+ Reference< XNameAccess > m_xGenericUICategories;
+ Reference< XMultiServiceFactory > m_xConfigProvider;
+ Reference< XNameAccess > m_xConfigAccess;
+ Reference< XContainerListener > m_xConfigListener;
+ bool m_bConfigAccessInitialized;
+ bool m_bCacheFilled;
+ IdToInfoCache m_aIdCache;
+};
+
+// XInterface, XTypeProvider
+
+ConfigurationAccess_UICategory::ConfigurationAccess_UICategory( std::u16string_view aModuleName, const Reference< XNameAccess >& rGenericUICategories, const Reference< XComponentContext >& rxContext ) :
+ // Create configuration hierarchical access name
+ m_aConfigCategoryAccess(
+ OUString::Concat("/org.openoffice.Office.UI.") + aModuleName + "/Commands/Categories"),
+ m_aPropUIName( "Name" ),
+ m_xGenericUICategories( rGenericUICategories ),
+ m_xConfigProvider(theDefaultProvider::get( rxContext )),
+ m_bConfigAccessInitialized( false ),
+ m_bCacheFilled( false )
+{
+}
+
+ConfigurationAccess_UICategory::~ConfigurationAccess_UICategory()
+{
+ // SAFE
+ osl::MutexGuard g(aMutex);
+ Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY );
+ if ( xContainer.is() )
+ xContainer->removeContainerListener(m_xConfigListener);
+}
+
+// XNameAccess
+Any SAL_CALL ConfigurationAccess_UICategory::getByName( const OUString& rId )
+{
+ osl::MutexGuard g(aMutex);
+ if ( !m_bConfigAccessInitialized )
+ {
+ initializeConfigAccess();
+ m_bConfigAccessInitialized = true;
+ fillCache();
+ }
+
+ // SAFE
+ Any a = getUINameFromID( rId );
+
+ if ( !a.hasValue() )
+ throw NoSuchElementException();
+
+ return a;
+}
+
+Sequence< OUString > SAL_CALL ConfigurationAccess_UICategory::getElementNames()
+{
+ return getAllIds();
+}
+
+sal_Bool SAL_CALL ConfigurationAccess_UICategory::hasByName( const OUString& rId )
+{
+ return getByName( rId ).hasValue();
+}
+
+// XElementAccess
+Type SAL_CALL ConfigurationAccess_UICategory::getElementType()
+{
+ return cppu::UnoType<OUString>::get();
+}
+
+sal_Bool SAL_CALL ConfigurationAccess_UICategory::hasElements()
+{
+ // There must be global categories!
+ return true;
+}
+
+void ConfigurationAccess_UICategory::fillCache()
+{
+ SAL_INFO( "fwk", "framework (cd100003) ::ConfigurationAccess_UICategory::fillCache" );
+
+ if ( m_bCacheFilled )
+ return;
+
+ OUString aUIName;
+ const Sequence< OUString > aNameSeq = m_xConfigAccess->getElementNames();
+
+ for ( OUString const & rName : aNameSeq )
+ {
+ try
+ {
+ Reference< XNameAccess > xNameAccess(m_xConfigAccess->getByName( rName ),UNO_QUERY);
+ if ( xNameAccess.is() )
+ {
+ xNameAccess->getByName( m_aPropUIName ) >>= aUIName;
+
+ m_aIdCache.emplace( rName, aUIName );
+ }
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+ }
+
+ m_bCacheFilled = true;
+}
+
+Any ConfigurationAccess_UICategory::getUINameFromID( const OUString& rId )
+{
+ Any a;
+
+ try
+ {
+ a = getUINameFromCache( rId );
+ if ( !a.hasValue() )
+ {
+ // Try to ask our global commands configuration access
+ if ( m_xGenericUICategories.is() )
+ {
+ try
+ {
+ return m_xGenericUICategories->getByName( rId );
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+ }
+ }
+ }
+ catch( const css::container::NoSuchElementException& )
+ {
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+
+ return a;
+}
+
+Any ConfigurationAccess_UICategory::getUINameFromCache( const OUString& rId )
+{
+ Any a;
+
+ IdToInfoCache::const_iterator pIter = m_aIdCache.find( rId );
+ if ( pIter != m_aIdCache.end() )
+ a <<= pIter->second;
+
+ return a;
+}
+
+Sequence< OUString > ConfigurationAccess_UICategory::getAllIds()
+{
+ // SAFE
+ osl::MutexGuard g(aMutex);
+
+ if ( !m_bConfigAccessInitialized )
+ {
+ initializeConfigAccess();
+ m_bConfigAccessInitialized = true;
+ fillCache();
+ }
+
+ if ( m_xConfigAccess.is() )
+ {
+ try
+ {
+ Sequence< OUString > aNameSeq = m_xConfigAccess->getElementNames();
+
+ if ( m_xGenericUICategories.is() )
+ {
+ // Create concat list of supported user interface commands of the module
+ Sequence< OUString > aGenericNameSeq = m_xGenericUICategories->getElementNames();
+ sal_uInt32 nCount1 = aNameSeq.getLength();
+ sal_uInt32 nCount2 = aGenericNameSeq.getLength();
+
+ aNameSeq.realloc( nCount1 + nCount2 );
+ OUString* pNameSeq = aNameSeq.getArray();
+ const OUString* pGenericSeq = aGenericNameSeq.getConstArray();
+ for ( sal_uInt32 i = 0; i < nCount2; i++ )
+ pNameSeq[nCount1+i] = pGenericSeq[i];
+ }
+
+ return aNameSeq;
+ }
+ catch( const css::container::NoSuchElementException& )
+ {
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ }
+
+ return Sequence< OUString >();
+}
+
+void ConfigurationAccess_UICategory::initializeConfigAccess()
+{
+ try
+ {
+ Sequence<Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", Any(m_aConfigCategoryAccess)}
+ }));
+
+ m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess", aArgs ),UNO_QUERY );
+ if ( m_xConfigAccess.is() )
+ {
+ // Add as container listener
+ Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY );
+ if ( xContainer.is() )
+ {
+ m_xConfigListener = new WeakContainerListener(this);
+ xContainer->addContainerListener(m_xConfigListener);
+ }
+ }
+ }
+ catch ( const WrappedTargetException& )
+ {
+ }
+ catch ( const Exception& )
+ {
+ }
+}
+
+// container.XContainerListener
+void SAL_CALL ConfigurationAccess_UICategory::elementInserted( const ContainerEvent& )
+{
+}
+
+void SAL_CALL ConfigurationAccess_UICategory::elementRemoved ( const ContainerEvent& )
+{
+}
+
+void SAL_CALL ConfigurationAccess_UICategory::elementReplaced( const ContainerEvent& )
+{
+}
+
+// lang.XEventListener
+void SAL_CALL ConfigurationAccess_UICategory::disposing( const EventObject& aEvent )
+{
+ // SAFE
+ // remove our reference to the config access
+ osl::MutexGuard g(aMutex);
+
+ Reference< XInterface > xIfac1( aEvent.Source, UNO_QUERY );
+ Reference< XInterface > xIfac2( m_xConfigAccess, UNO_QUERY );
+ if ( xIfac1 == xIfac2 )
+ m_xConfigAccess.clear();
+}
+
+class UICategoryDescription : public UICommandDescription
+{
+public:
+ explicit UICategoryDescription( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.UICategoryDescription";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.ui.UICategoryDescription"};
+ }
+
+};
+
+UICategoryDescription::UICategoryDescription( const Reference< XComponentContext >& rxContext ) :
+ UICommandDescription(rxContext,true)
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ Reference< XNameAccess > xEmpty;
+ OUString aGenericCategories( "GenericCategories" );
+ m_xGenericUICommands[rCurrentLanguage] = new ConfigurationAccess_UICategory( aGenericCategories, xEmpty, rxContext );
+
+ // insert generic categories mappings
+ m_aModuleToCommandFileMap.emplace( OUString("generic"), aGenericCategories );
+
+ auto& rMap = m_aUICommandsHashMap[rCurrentLanguage];
+ UICommandsHashMap::iterator pCatIter = rMap.find( aGenericCategories );
+ if ( pCatIter != rMap.end() )
+ pCatIter->second = m_xGenericUICommands[rCurrentLanguage];
+
+ impl_fillElements("ooSetupFactoryCmdCategoryConfigRef");
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_UICategoryDescription_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new UICategoryDescription(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/uiconfigurationmanager.cxx b/framework/source/uiconfiguration/uiconfigurationmanager.cxx
new file mode 100644
index 000000000..24dbe1639
--- /dev/null
+++ b/framework/source/uiconfiguration/uiconfigurationmanager.cxx
@@ -0,0 +1,1377 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uiconfiguration/imagemanager.hxx>
+#include <uielement/rootitemcontainer.hxx>
+#include <uielement/constitemcontainer.hxx>
+#include <uielement/uielementtypenames.hxx>
+#include <menuconfiguration.hxx>
+#include <statusbarconfiguration.hxx>
+#include <toolboxconfiguration.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/InvalidStorageException.hpp>
+#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/lang/IllegalAccessException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/ui/UIElementType.hpp>
+#include <com/sun/star/ui/ConfigurationEvent.hpp>
+#include <com/sun/star/ui/DocumentAcceleratorConfiguration.hpp>
+#include <com/sun/star/ui/XAcceleratorConfiguration.hpp>
+#include <com/sun/star/ui/XUIConfigurationManager2.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <mutex>
+#include <string_view>
+#include <unordered_map>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::io;
+using namespace com::sun::star::embed;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::ui;
+using namespace framework;
+
+namespace {
+
+class UIConfigurationManager : public ::cppu::WeakImplHelper<
+ css::lang::XServiceInfo ,
+ css::ui::XUIConfigurationManager2 >
+{
+public:
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.UIConfigurationManager";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.ui.UIConfigurationManager"};
+ }
+
+ explicit UIConfigurationManager( css::uno::Reference< css::uno::XComponentContext > xContext );
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+ // XUIConfiguration
+ virtual void SAL_CALL addConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override;
+ virtual void SAL_CALL removeConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override;
+
+ // XUIConfigurationManager
+ virtual void SAL_CALL reset() override;
+ virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getUIElementsInfo( sal_Int16 ElementType ) override;
+ virtual css::uno::Reference< css::container::XIndexContainer > SAL_CALL createSettings( ) override;
+ virtual sal_Bool SAL_CALL hasSettings( const OUString& ResourceURL ) override;
+ virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getSettings( const OUString& ResourceURL, sal_Bool bWriteable ) override;
+ virtual void SAL_CALL replaceSettings( const OUString& ResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override;
+ virtual void SAL_CALL removeSettings( const OUString& ResourceURL ) override;
+ virtual void SAL_CALL insertSettings( const OUString& NewResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override;
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getImageManager() override;
+ virtual css::uno::Reference< css::ui::XAcceleratorConfiguration > SAL_CALL getShortCutManager() override;
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getEventsManager() override;
+
+ // XUIConfigurationPersistence
+ virtual void SAL_CALL reload() override;
+ virtual void SAL_CALL store() override;
+ virtual void SAL_CALL storeToStorage( const css::uno::Reference< css::embed::XStorage >& Storage ) override;
+ virtual sal_Bool SAL_CALL isModified() override;
+ virtual sal_Bool SAL_CALL isReadOnly() override;
+
+ // XUIConfigurationStorage
+ virtual void SAL_CALL setStorage( const css::uno::Reference< css::embed::XStorage >& Storage ) override;
+ virtual sal_Bool SAL_CALL hasStorage() override;
+
+private:
+ // private data types
+ enum NotifyOp
+ {
+ NotifyOp_Remove,
+ NotifyOp_Insert,
+ NotifyOp_Replace
+ };
+
+ struct UIElementInfo
+ {
+ UIElementInfo( OUString _aResourceURL, OUString _aUIName ) :
+ aResourceURL(std::move( _aResourceURL)), aUIName(std::move( _aUIName )) {}
+ OUString aResourceURL;
+ OUString aUIName;
+ };
+
+ struct UIElementData
+ {
+ UIElementData() : bModified( false ), bDefault( true ) {};
+
+ OUString aResourceURL;
+ OUString aName;
+ bool bModified; // has been changed since last storing
+ bool bDefault; // default settings
+ css::uno::Reference< css::container::XIndexAccess > xSettings;
+ };
+
+ struct UIElementType;
+ friend struct UIElementType;
+ typedef std::unordered_map< OUString, UIElementData > UIElementDataHashMap;
+
+ struct UIElementType
+ {
+ UIElementType() : bModified( false ),
+ bLoaded( false ),
+ nElementType( css::ui::UIElementType::UNKNOWN ) {}
+
+ bool bModified;
+ bool bLoaded;
+ sal_Int16 nElementType;
+ UIElementDataHashMap aElementsHashMap;
+ css::uno::Reference< css::embed::XStorage > xStorage;
+ };
+
+ typedef std::vector< UIElementType > UIElementTypesVector;
+ typedef std::vector< css::ui::ConfigurationEvent > ConfigEventNotifyContainer;
+ typedef std::unordered_map< OUString, UIElementInfo > UIElementInfoHashMap;
+
+ void impl_Initialize();
+ void implts_notifyContainerListener( const css::ui::ConfigurationEvent& aEvent, NotifyOp eOp );
+ void impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType );
+ void impl_preloadUIElementTypeList( sal_Int16 nElementType );
+ UIElementData* impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad = true );
+ void impl_requestUIElementData( sal_Int16 nElementType, UIElementData& aUIElementData );
+ void impl_storeElementTypeData( css::uno::Reference< css::embed::XStorage > const & xStorage, UIElementType& rElementType, bool bResetModifyState = true );
+ void impl_resetElementTypeData( UIElementType& rDocElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer );
+ void impl_reloadElementTypeData( UIElementType& rDocElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer, ConfigEventNotifyContainer& rReplaceNotifyContainer );
+
+ UIElementTypesVector m_aUIElements;
+ css::uno::Reference< css::embed::XStorage > m_xDocConfigStorage;
+ bool m_bReadOnly;
+ bool m_bModified;
+ bool m_bDisposed;
+ OUString m_aPropUIName;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ std::mutex m_mutex;
+ comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_aEventListeners;
+ comphelper::OInterfaceContainerHelper4<css::ui::XUIConfigurationListener> m_aConfigListeners;
+ rtl::Reference< ImageManager > m_xImageManager;
+ css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xAccConfig;
+};
+
+// important: The order and position of the elements must match the constant
+// definition of "css::ui::UIElementType"
+std::u16string_view UIELEMENTTYPENAMES[] =
+{
+ u"", // Dummy value for unknown!
+ u"" UIELEMENTTYPE_MENUBAR_NAME,
+ u"" UIELEMENTTYPE_POPUPMENU_NAME,
+ u"" UIELEMENTTYPE_TOOLBAR_NAME,
+ u"" UIELEMENTTYPE_STATUSBAR_NAME,
+ u"" UIELEMENTTYPE_FLOATINGWINDOW_NAME,
+ u"" UIELEMENTTYPE_PROGRESSBAR_NAME,
+ u"" UIELEMENTTYPE_TOOLPANEL_NAME
+};
+
+const char RESOURCEURL_PREFIX[] = "private:resource/";
+const sal_Int32 RESOURCEURL_PREFIX_SIZE = 17;
+
+sal_Int16 RetrieveTypeFromResourceURL( const OUString& aResourceURL )
+{
+
+ if (( aResourceURL.startsWith( RESOURCEURL_PREFIX ) ) &&
+ ( aResourceURL.getLength() > RESOURCEURL_PREFIX_SIZE ))
+ {
+ std::u16string_view aTmpStr = aResourceURL.subView( RESOURCEURL_PREFIX_SIZE );
+ size_t nIndex = aTmpStr.find( '/' );
+ if (( nIndex > 0 ) && ( aTmpStr.size() > nIndex ))
+ {
+ std::u16string_view aTypeStr( aTmpStr.substr( 0, nIndex ));
+ for ( int i = 0; i < UIElementType::COUNT; i++ )
+ {
+ if ( aTypeStr == UIELEMENTTYPENAMES[i] )
+ return sal_Int16( i );
+ }
+ }
+ }
+
+ return UIElementType::UNKNOWN;
+}
+
+OUString RetrieveNameFromResourceURL( const OUString& aResourceURL )
+{
+ if (( aResourceURL.startsWith( RESOURCEURL_PREFIX ) ) &&
+ ( aResourceURL.getLength() > RESOURCEURL_PREFIX_SIZE ))
+ {
+ sal_Int32 nIndex = aResourceURL.lastIndexOf( '/' );
+ if (( nIndex > 0 ) && (( nIndex+1 ) < aResourceURL.getLength()))
+ return aResourceURL.copy( nIndex+1 );
+ }
+
+ return OUString();
+}
+
+void UIConfigurationManager::impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType )
+{
+ // preload list of element types on demand
+ impl_preloadUIElementTypeList( nElementType );
+
+ UIElementDataHashMap& rUserElements = m_aUIElements[nElementType].aElementsHashMap;
+
+ for (auto const& elem : rUserElements)
+ {
+ UIElementData* pDataSettings = impl_findUIElementData( elem.second.aResourceURL, nElementType );
+ if ( pDataSettings && !pDataSettings->bDefault )
+ {
+ // Retrieve user interface name from XPropertySet interface
+ OUString aUIName;
+ Reference< XPropertySet > xPropSet( pDataSettings->xSettings, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ Any a = xPropSet->getPropertyValue( m_aPropUIName );
+ a >>= aUIName;
+ }
+
+ UIElementInfo aInfo( elem.second.aResourceURL, aUIName );
+ aUIElementInfoCollection.emplace( elem.second.aResourceURL, aInfo );
+ }
+ }
+}
+
+void UIConfigurationManager::impl_preloadUIElementTypeList( sal_Int16 nElementType )
+{
+ UIElementType& rElementTypeData = m_aUIElements[nElementType];
+
+ if ( !rElementTypeData.bLoaded )
+ {
+ Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage;
+ if ( xElementTypeStorage.is() )
+ {
+ OUString aResURLPrefix =
+ OUString::Concat(RESOURCEURL_PREFIX) +
+ UIELEMENTTYPENAMES[ nElementType ] +
+ "/";
+
+ UIElementDataHashMap& rHashMap = rElementTypeData.aElementsHashMap;
+ const Sequence< OUString > aUIElementNames = xElementTypeStorage->getElementNames();
+ for ( OUString const & rElementName : aUIElementNames )
+ {
+ UIElementData aUIElementData;
+
+ // Resource name must be without ".xml"
+ sal_Int32 nIndex = rElementName.lastIndexOf( '.' );
+ if (( nIndex > 0 ) && ( nIndex < rElementName.getLength() ))
+ {
+ std::u16string_view aExtension( rElementName.subView( nIndex+1 ));
+ std::u16string_view aUIElementName( rElementName.subView( 0, nIndex ));
+
+ if (!aUIElementName.empty() &&
+ ( o3tl::equalsIgnoreAsciiCase(aExtension, u"xml")))
+ {
+ aUIElementData.aResourceURL = aResURLPrefix + aUIElementName;
+ aUIElementData.aName = rElementName;
+ aUIElementData.bModified = false;
+ aUIElementData.bDefault = false;
+
+ // Create unordered_map entries for all user interface elements inside the storage. We don't load the
+ // settings to speed up the process.
+ rHashMap.emplace( aUIElementData.aResourceURL, aUIElementData );
+ }
+ }
+ }
+ }
+ }
+
+ rElementTypeData.bLoaded = true;
+}
+
+void UIConfigurationManager::impl_requestUIElementData( sal_Int16 nElementType, UIElementData& aUIElementData )
+{
+ UIElementType& rElementTypeData = m_aUIElements[nElementType];
+
+ Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage;
+ if ( xElementTypeStorage.is() && !aUIElementData.aName.isEmpty() )
+ {
+ try
+ {
+ Reference< XStream > xStream = xElementTypeStorage->openStreamElement( aUIElementData.aName, ElementModes::READ );
+ Reference< XInputStream > xInputStream = xStream->getInputStream();
+
+ if ( xInputStream.is() )
+ {
+ switch ( nElementType )
+ {
+ case css::ui::UIElementType::UNKNOWN:
+ break;
+
+ case css::ui::UIElementType::MENUBAR:
+ case css::ui::UIElementType::POPUPMENU:
+ {
+ try
+ {
+ MenuConfiguration aMenuCfg( m_xContext );
+ Reference< XIndexAccess > xContainer( aMenuCfg.CreateMenuBarConfigurationFromXML( xInputStream ));
+ auto pRootItemContainer = comphelper::getFromUnoTunnel<RootItemContainer>( xContainer );
+ if ( pRootItemContainer )
+ aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true );
+ else
+ aUIElementData.xSettings = new ConstItemContainer( xContainer, true );
+ return;
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ }
+ break;
+
+ case css::ui::UIElementType::TOOLBAR:
+ {
+ try
+ {
+ Reference< XIndexContainer > xIndexContainer( new RootItemContainer() );
+ ToolBoxConfiguration::LoadToolBox( m_xContext, xInputStream, xIndexContainer );
+ auto pRootItemContainer = comphelper::getFromUnoTunnel<RootItemContainer>( xIndexContainer );
+ aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true );
+ return;
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+
+ break;
+ }
+
+ case css::ui::UIElementType::STATUSBAR:
+ {
+ try
+ {
+ Reference< XIndexContainer > xIndexContainer( new RootItemContainer() );
+ StatusBarConfiguration::LoadStatusBar( m_xContext, xInputStream, xIndexContainer );
+ auto pRootItemContainer = comphelper::getFromUnoTunnel<RootItemContainer>( xIndexContainer );
+ aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true );
+ return;
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+
+ break;
+ }
+
+ case css::ui::UIElementType::FLOATINGWINDOW:
+ {
+ break;
+ }
+ }
+ }
+ }
+ catch ( const css::embed::InvalidStorageException& )
+ {
+ }
+ catch ( const css::lang::IllegalArgumentException& )
+ {
+ }
+ catch ( const css::io::IOException& )
+ {
+ }
+ catch ( const css::embed::StorageWrappedTargetException& )
+ {
+ }
+ }
+
+ // At least we provide an empty settings container!
+ aUIElementData.xSettings = new ConstItemContainer();
+}
+
+UIConfigurationManager::UIElementData* UIConfigurationManager::impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad )
+{
+ // preload list of element types on demand
+ impl_preloadUIElementTypeList( nElementType );
+
+ // try to look into our document vector/unordered_map combination
+ UIElementDataHashMap& rUserHashMap = m_aUIElements[nElementType].aElementsHashMap;
+ UIElementDataHashMap::iterator pIter = rUserHashMap.find( aResourceURL );
+ if ( pIter != rUserHashMap.end() )
+ {
+ // Default data settings data means removed!
+ if ( pIter->second.bDefault )
+ return &(pIter->second);
+ else
+ {
+ if ( !pIter->second.xSettings.is() && bLoad )
+ impl_requestUIElementData( nElementType, pIter->second );
+ return &(pIter->second);
+ }
+ }
+
+ // Nothing has been found!
+ return nullptr;
+}
+
+void UIConfigurationManager::impl_storeElementTypeData( Reference< XStorage > const & xStorage, UIElementType& rElementType, bool bResetModifyState )
+{
+ UIElementDataHashMap& rHashMap = rElementType.aElementsHashMap;
+
+ for (auto & elem : rHashMap)
+ {
+ UIElementData& rElement = elem.second;
+ if ( rElement.bModified )
+ {
+ if ( rElement.bDefault )
+ {
+ xStorage->removeElement( rElement.aName );
+ rElement.bModified = false; // mark as not modified
+ }
+ else
+ {
+ Reference< XStream > xStream = xStorage->openStreamElement( rElement.aName, ElementModes::WRITE|ElementModes::TRUNCATE );
+ Reference< XOutputStream > xOutputStream( xStream->getOutputStream() );
+
+ if ( xOutputStream.is() )
+ {
+ switch( rElementType.nElementType )
+ {
+ case css::ui::UIElementType::MENUBAR:
+ case css::ui::UIElementType::POPUPMENU:
+ {
+ try
+ {
+ MenuConfiguration aMenuCfg( m_xContext );
+ aMenuCfg.StoreMenuBarConfigurationToXML(
+ rElement.xSettings, xOutputStream, rElementType.nElementType == css::ui::UIElementType::MENUBAR );
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ }
+ break;
+
+ case css::ui::UIElementType::TOOLBAR:
+ {
+ try
+ {
+ ToolBoxConfiguration::StoreToolBox( m_xContext, xOutputStream, rElement.xSettings );
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ }
+ break;
+
+ case css::ui::UIElementType::STATUSBAR:
+ {
+ try
+ {
+ StatusBarConfiguration::StoreStatusBar( m_xContext, xOutputStream, rElement.xSettings );
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // mark as not modified if we store to our own storage
+ if ( bResetModifyState )
+ rElement.bModified = false;
+ }
+ }
+ }
+
+ // commit element type storage
+ Reference< XTransactedObject > xTransactedObject( xStorage, UNO_QUERY );
+ if ( xTransactedObject.is() )
+ xTransactedObject->commit();
+
+ // mark UIElementType as not modified if we store to our own storage
+ if ( bResetModifyState )
+ rElementType.bModified = false;
+}
+
+void UIConfigurationManager::impl_resetElementTypeData(
+ UIElementType& rDocElementType,
+ ConfigEventNotifyContainer& rRemoveNotifyContainer )
+{
+ UIElementDataHashMap& rHashMap = rDocElementType.aElementsHashMap;
+
+ Reference< XUIConfigurationManager > xThis(this);
+ Reference< XInterface > xIfac( xThis, UNO_QUERY );
+
+ // Make copies of the event structures to be thread-safe. We have to unlock our mutex before calling
+ // our listeners!
+ for (auto & elem : rHashMap)
+ {
+ UIElementData& rElement = elem.second;
+ if ( !rElement.bDefault )
+ {
+ // Remove user-defined settings from document
+ ConfigurationEvent aEvent;
+ aEvent.ResourceURL = rElement.aResourceURL;
+ aEvent.Accessor <<= xThis;
+ aEvent.Source = xIfac;
+ aEvent.Element <<= rElement.xSettings;
+
+ rRemoveNotifyContainer.push_back( aEvent );
+
+ // Mark element as default.
+ rElement.bModified = false;
+ rElement.bDefault = true;
+ }
+ else
+ rElement.bModified = false;
+ }
+
+ // Remove all settings from our user interface elements
+ rHashMap.clear();
+}
+
+void UIConfigurationManager::impl_reloadElementTypeData(
+ UIElementType& rDocElementType,
+ ConfigEventNotifyContainer& rRemoveNotifyContainer,
+ ConfigEventNotifyContainer& rReplaceNotifyContainer )
+{
+ UIElementDataHashMap& rHashMap = rDocElementType.aElementsHashMap;
+ Reference< XStorage > xElementStorage( rDocElementType.xStorage );
+
+ Reference< XUIConfigurationManager > xThis(this);
+ Reference< XInterface > xIfac( xThis, UNO_QUERY );
+ sal_Int16 nType = rDocElementType.nElementType;
+
+ for (auto & elem : rHashMap)
+ {
+ UIElementData& rElement = elem.second;
+ if ( rElement.bModified )
+ {
+ if ( xElementStorage->hasByName( rElement.aName ))
+ {
+ // Replace settings with data from user layer
+ Reference< XIndexAccess > xOldSettings( rElement.xSettings );
+
+ impl_requestUIElementData( nType, rElement );
+
+ ConfigurationEvent aReplaceEvent;
+
+ aReplaceEvent.ResourceURL = rElement.aResourceURL;
+ aReplaceEvent.Accessor <<= xThis;
+ aReplaceEvent.Source = xIfac;
+ aReplaceEvent.ReplacedElement <<= xOldSettings;
+ aReplaceEvent.Element <<= rElement.xSettings;
+ rReplaceNotifyContainer.push_back( aReplaceEvent );
+
+ rElement.bModified = false;
+ }
+ else
+ {
+ // Element settings are not in any storage => remove
+ ConfigurationEvent aRemoveEvent;
+
+ aRemoveEvent.ResourceURL = rElement.aResourceURL;
+ aRemoveEvent.Accessor <<= xThis;
+ aRemoveEvent.Source = xIfac;
+ aRemoveEvent.Element <<= rElement.xSettings;
+
+ rRemoveNotifyContainer.push_back( aRemoveEvent );
+
+ // Mark element as default and not modified. That means "not active" in the document anymore
+ rElement.bModified = false;
+ rElement.bDefault = true;
+ }
+ }
+ }
+
+ rDocElementType.bModified = false;
+}
+
+void UIConfigurationManager::impl_Initialize()
+{
+ // Initialize the top-level structures with the storage data
+ if ( m_xDocConfigStorage.is() )
+ {
+ tools::Long nModes = m_bReadOnly ? ElementModes::READ : ElementModes::READWRITE;
+
+ // Try to access our module sub folder
+ for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT;
+ i++ )
+ {
+ Reference< XStorage > xElementTypeStorage;
+ try
+ {
+ xElementTypeStorage = m_xDocConfigStorage->openStorageElement( OUString(UIELEMENTTYPENAMES[i]), nModes );
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+ catch ( const css::embed::InvalidStorageException& )
+ {
+ }
+ catch ( const css::lang::IllegalArgumentException& )
+ {
+ }
+ catch ( const css::io::IOException& )
+ {
+ }
+ catch ( const css::embed::StorageWrappedTargetException& )
+ {
+ }
+
+ m_aUIElements[i].nElementType = i;
+ m_aUIElements[i].bModified = false;
+ m_aUIElements[i].xStorage = xElementTypeStorage;
+ }
+ }
+ else
+ {
+ // We have no storage, just initialize ui element types with empty storage!
+ for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
+ m_aUIElements[i].xStorage = m_xDocConfigStorage;
+ }
+}
+
+UIConfigurationManager::UIConfigurationManager( css::uno::Reference< css::uno::XComponentContext > xContext ) :
+ m_bReadOnly( true )
+ , m_bModified( false )
+ , m_bDisposed( false )
+ , m_aPropUIName( "UIName" )
+ , m_xContext(std::move( xContext ))
+{
+ // Make sure we have a default initialized entry for every layer and user interface element type!
+ // The following code depends on this!
+ m_aUIElements.resize( css::ui::UIElementType::COUNT );
+}
+
+// XComponent
+void SAL_CALL UIConfigurationManager::dispose()
+{
+ Reference< XComponent > xThis(this);
+
+ css::lang::EventObject aEvent( xThis );
+ {
+ std::unique_lock aGuard(m_mutex);
+ m_aEventListeners.disposeAndClear( aGuard, aEvent );
+ }
+ {
+ std::unique_lock aGuard(m_mutex);
+ m_aConfigListeners.disposeAndClear( aGuard, aEvent );
+ }
+
+ {
+ SolarMutexGuard g;
+ try
+ {
+ if ( m_xImageManager.is() )
+ m_xImageManager->dispose();
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ m_xImageManager.clear();
+ m_aUIElements.clear();
+ m_xDocConfigStorage.clear();
+ m_bModified = false;
+ m_bDisposed = true;
+ }
+}
+
+void SAL_CALL UIConfigurationManager::addEventListener( const Reference< XEventListener >& xListener )
+{
+ {
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+ }
+
+ std::unique_lock aGuard(m_mutex);
+ m_aEventListeners.addInterface( aGuard, xListener );
+}
+
+void SAL_CALL UIConfigurationManager::removeEventListener( const Reference< XEventListener >& xListener )
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ std::unique_lock aGuard(m_mutex);
+ m_aEventListeners.removeInterface( aGuard, xListener );
+}
+
+// XUIConfigurationManager
+void SAL_CALL UIConfigurationManager::addConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener )
+{
+ {
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+ }
+
+ std::unique_lock aGuard(m_mutex);
+ m_aConfigListeners.addInterface( aGuard, xListener );
+}
+
+void SAL_CALL UIConfigurationManager::removeConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener )
+{
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ std::unique_lock aGuard(m_mutex);
+ m_aConfigListeners.removeInterface( aGuard, xListener );
+}
+
+void SAL_CALL UIConfigurationManager::reset()
+{
+ SolarMutexClearableGuard aGuard;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( isReadOnly() )
+ return;
+
+ if ( !m_xDocConfigStorage.is() )
+ return;
+
+ try
+ {
+ // Remove all elements from our user-defined storage!
+ bool bCommit( false );
+ for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
+ {
+ UIElementType& rElementType = m_aUIElements[i];
+
+ if ( rElementType.xStorage.is() )
+ {
+ bool bCommitSubStorage( false );
+ const Sequence< OUString > aUIElementStreamNames = rElementType.xStorage->getElementNames();
+ for ( OUString const & rStreamName : aUIElementStreamNames )
+ {
+ rElementType.xStorage->removeElement( rStreamName );
+ bCommitSubStorage = true;
+ bCommit = true;
+ }
+
+ if ( bCommitSubStorage )
+ {
+ Reference< XTransactedObject > xTransactedObject( rElementType.xStorage, UNO_QUERY );
+ if ( xTransactedObject.is() )
+ xTransactedObject->commit();
+ }
+ }
+ }
+
+ // Commit changes
+ if ( bCommit )
+ {
+ Reference< XTransactedObject > xTransactedObject( m_xDocConfigStorage, UNO_QUERY );
+ if ( xTransactedObject.is() )
+ xTransactedObject->commit();
+ }
+
+ // remove settings from user defined layer and notify listener about removed settings data!
+ // Try to access our module sub folder
+ ConfigEventNotifyContainer aRemoveEventNotifyContainer;
+ for ( sal_Int16 j = 1; j < css::ui::UIElementType::COUNT; j++ )
+ {
+ UIElementType& rDocElementType = m_aUIElements[j];
+
+ impl_resetElementTypeData( rDocElementType, aRemoveEventNotifyContainer );
+ rDocElementType.bModified = false;
+ }
+
+ m_bModified = false;
+
+ // Unlock mutex before notify our listeners
+ aGuard.clear();
+
+ // Notify our listeners
+ for (const ConfigurationEvent & k : aRemoveEventNotifyContainer)
+ implts_notifyContainerListener( k, NotifyOp_Remove );
+ }
+ catch ( const css::lang::IllegalArgumentException& )
+ {
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+ catch ( const css::embed::InvalidStorageException& )
+ {
+ }
+ catch ( const css::embed::StorageWrappedTargetException& )
+ {
+ }
+}
+
+Sequence< Sequence< PropertyValue > > SAL_CALL UIConfigurationManager::getUIElementsInfo( sal_Int16 ElementType )
+{
+ if (( ElementType < 0 ) || ( ElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException();
+
+ SolarMutexGuard g;
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ std::vector< Sequence< PropertyValue > > aElementInfoSeq;
+ UIElementInfoHashMap aUIElementInfoCollection;
+
+ if ( ElementType == css::ui::UIElementType::UNKNOWN )
+ {
+ for ( sal_Int16 i = 0; i < css::ui::UIElementType::COUNT; i++ )
+ impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, i );
+ }
+ else
+ impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, ElementType );
+
+ aElementInfoSeq.resize( aUIElementInfoCollection.size() );
+ sal_Int32 n = 0;
+ for (auto const& elem : aUIElementInfoCollection)
+ {
+ Sequence< PropertyValue > aUIElementInfo{
+ comphelper::makePropertyValue("ResourceURL", elem.second.aResourceURL),
+ comphelper::makePropertyValue(m_aPropUIName, elem.second.aUIName)
+ };
+ aElementInfoSeq[n++] = aUIElementInfo;
+ }
+
+ return comphelper::containerToSequence(aElementInfoSeq);
+}
+
+Reference< XIndexContainer > SAL_CALL UIConfigurationManager::createSettings()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ // Creates an empty item container which can be filled from outside
+ return Reference< XIndexContainer >( new RootItemContainer() );
+}
+
+sal_Bool SAL_CALL UIConfigurationManager::hasSettings( const OUString& ResourceURL )
+{
+ sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
+
+ if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
+ ( nElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException();
+ UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType, false );
+ if ( pDataSettings && !pDataSettings->bDefault )
+ return true;
+
+ return false;
+}
+
+Reference< XIndexAccess > SAL_CALL UIConfigurationManager::getSettings( const OUString& ResourceURL, sal_Bool bWriteable )
+{
+ sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
+
+ if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
+ ( nElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException();
+
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
+ if ( pDataSettings && !pDataSettings->bDefault )
+ {
+ // Create a copy of our data if someone wants to change the data.
+ if ( bWriteable )
+ return Reference< XIndexAccess >( new RootItemContainer( pDataSettings->xSettings ) );
+ else
+ return pDataSettings->xSettings;
+ }
+
+ throw NoSuchElementException();
+}
+
+void SAL_CALL UIConfigurationManager::replaceSettings( const OUString& ResourceURL, const Reference< css::container::XIndexAccess >& aNewData )
+{
+ sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
+
+ if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
+ ( nElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException();
+ else if ( m_bReadOnly )
+ throw IllegalAccessException();
+ else
+ {
+ SolarMutexClearableGuard aGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
+ if ( !pDataSettings || pDataSettings->bDefault )
+ throw NoSuchElementException();
+ // we have a settings entry in our user-defined layer - replace
+ Reference< XIndexAccess > xOldSettings = pDataSettings->xSettings;
+
+ // Create a copy of the data if the container is not const
+ Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY );
+ if ( xReplace.is() )
+ pDataSettings->xSettings = new ConstItemContainer( aNewData );
+ else
+ pDataSettings->xSettings = aNewData;
+
+ pDataSettings->bDefault = false;
+ pDataSettings->bModified = true;
+ m_bModified = true;
+
+ // Modify type container
+ UIElementType& rElementType = m_aUIElements[nElementType];
+ rElementType.bModified = true;
+
+ Reference< XUIConfigurationManager > xThis(this);
+ Reference< XInterface > xIfac( xThis, UNO_QUERY );
+
+ // Create event to notify listener about replaced element settings
+ ConfigurationEvent aEvent;
+
+ aEvent.ResourceURL = ResourceURL;
+ aEvent.Accessor <<= xThis;
+ aEvent.Source = xIfac;
+ aEvent.ReplacedElement <<= xOldSettings;
+ aEvent.Element <<= pDataSettings->xSettings;
+
+ aGuard.clear();
+
+ implts_notifyContainerListener( aEvent, NotifyOp_Replace );
+ }
+}
+
+void SAL_CALL UIConfigurationManager::removeSettings( const OUString& ResourceURL )
+{
+ sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
+
+ if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
+ ( nElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException( "The ResourceURL is not valid or "
+ "describes an unknown type. "
+ "ResourceURL: " + ResourceURL, nullptr, 0 );
+ else if ( m_bReadOnly )
+ throw IllegalAccessException( "The configuration manager is read-only. "
+ "ResourceURL: " + ResourceURL, nullptr );
+ else
+ {
+ SolarMutexClearableGuard aGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException( "The configuration manager has been disposed, "
+ "and can't uphold its method specification anymore. "
+ "ResourceURL: " + ResourceURL, nullptr );
+
+ UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
+ if ( !pDataSettings )
+ throw NoSuchElementException( "The settings data cannot be found. "
+ "ResourceURL: " + ResourceURL, nullptr);
+ // If element settings are default, we don't need to change anything!
+ if ( pDataSettings->bDefault )
+ return;
+ else
+ {
+ Reference< XIndexAccess > xRemovedSettings = pDataSettings->xSettings;
+ pDataSettings->bDefault = true;
+
+ // check if this is a default layer node
+ pDataSettings->bModified = true; // we have to remove this node from the user layer!
+ pDataSettings->xSettings.clear();
+ m_bModified = true; // user layer must be written
+
+ // Modify type container
+ UIElementType& rElementType = m_aUIElements[nElementType];
+ rElementType.bModified = true;
+
+ Reference< XUIConfigurationManager > xThis(this);
+ Reference< XInterface > xIfac( xThis, UNO_QUERY );
+
+ // Create event to notify listener about removed element settings
+ ConfigurationEvent aEvent;
+
+ aEvent.ResourceURL = ResourceURL;
+ aEvent.Accessor <<= xThis;
+ aEvent.Source = xIfac;
+ aEvent.Element <<= xRemovedSettings;
+
+ aGuard.clear();
+
+ implts_notifyContainerListener( aEvent, NotifyOp_Remove );
+ }
+ }
+}
+
+void SAL_CALL UIConfigurationManager::insertSettings( const OUString& NewResourceURL, const Reference< XIndexAccess >& aNewData )
+{
+ sal_Int16 nElementType = RetrieveTypeFromResourceURL( NewResourceURL );
+
+ if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
+ ( nElementType >= css::ui::UIElementType::COUNT ))
+ throw IllegalArgumentException();
+ else if ( m_bReadOnly )
+ throw IllegalAccessException();
+ else
+ {
+ SolarMutexClearableGuard aGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ bool bInsertData( false );
+ UIElementData aUIElementData;
+ UIElementData* pDataSettings = impl_findUIElementData( NewResourceURL, nElementType );
+
+ if ( pDataSettings && !pDataSettings->bDefault )
+ throw ElementExistException();
+
+ if ( !pDataSettings )
+ {
+ pDataSettings = &aUIElementData;
+ bInsertData = true;
+ }
+
+ {
+ pDataSettings->bDefault = false;
+ pDataSettings->bModified = true;
+
+ // Create a copy of the data if the container is not const
+ Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY );
+ if ( xReplace.is() )
+ pDataSettings->xSettings = new ConstItemContainer( aNewData );
+ else
+ pDataSettings->xSettings = aNewData;
+
+ m_bModified = true;
+
+ UIElementType& rElementType = m_aUIElements[nElementType];
+ rElementType.bModified = true;
+
+ if ( bInsertData )
+ {
+ pDataSettings->aName = RetrieveNameFromResourceURL( NewResourceURL ) + ".xml";
+ pDataSettings->aResourceURL = NewResourceURL;
+
+ UIElementDataHashMap& rElements = rElementType.aElementsHashMap;
+ rElements.emplace( NewResourceURL, *pDataSettings );
+ }
+
+ Reference< XIndexAccess > xInsertSettings( aUIElementData.xSettings );
+ Reference< XUIConfigurationManager > xThis(this);
+ Reference< XInterface > xIfac( xThis, UNO_QUERY );
+
+ // Create event to notify listener about removed element settings
+ ConfigurationEvent aEvent;
+
+ aEvent.ResourceURL = NewResourceURL;
+ aEvent.Accessor <<= xThis;
+ aEvent.Source = xIfac;
+ aEvent.Element <<= xInsertSettings;
+
+ aGuard.clear();
+
+ implts_notifyContainerListener( aEvent, NotifyOp_Insert );
+ }
+ }
+}
+
+Reference< XInterface > SAL_CALL UIConfigurationManager::getImageManager()
+{
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !m_xImageManager.is() )
+ {
+ m_xImageManager = new ImageManager( m_xContext, /*bForModule*/false );
+
+ Sequence<Any> aPropSeq(comphelper::InitAnyPropertySequence(
+ {
+ {"UserConfigStorage", Any(m_xDocConfigStorage)},
+ {"ModuleIdentifier", Any(OUString())},
+ }));
+
+ m_xImageManager->initialize( aPropSeq );
+ }
+
+ return Reference< XInterface >( static_cast<cppu::OWeakObject*>(m_xImageManager.get()), UNO_QUERY );
+}
+
+Reference< XAcceleratorConfiguration > SAL_CALL UIConfigurationManager::getShortCutManager()
+{
+ // SAFE ->
+ SolarMutexGuard g;
+
+ if (!m_xAccConfig.is()) try
+ {
+ m_xAccConfig = DocumentAcceleratorConfiguration::
+ createWithDocumentRoot(m_xContext, m_xDocConfigStorage);
+ }
+ catch ( const css::uno::DeploymentException& )
+ {
+ SAL_WARN("fwk.uiconfiguration", "DocumentAcceleratorConfiguration"
+ " not available. This should happen only on mobile platforms.");
+ }
+
+ return m_xAccConfig;
+}
+
+Reference< XInterface > SAL_CALL UIConfigurationManager::getEventsManager()
+{
+ return Reference< XInterface >();
+}
+
+// XUIConfigurationStorage
+void SAL_CALL UIConfigurationManager::setStorage( const Reference< XStorage >& Storage )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( m_xDocConfigStorage.is() )
+ {
+ try
+ {
+ // Dispose old storage to be sure that it will be closed
+ m_xDocConfigStorage->dispose();
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+
+ // We store the new storage. Be careful it could be an empty reference!
+ m_xDocConfigStorage = Storage;
+ m_bReadOnly = true;
+
+ if ( m_xAccConfig.is() )
+ m_xAccConfig->setStorage( m_xDocConfigStorage );
+
+ if ( m_xImageManager )
+ m_xImageManager->setStorage( m_xDocConfigStorage );
+
+ if ( m_xDocConfigStorage.is() )
+ {
+ Reference< XPropertySet > xPropSet( m_xDocConfigStorage, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ tools::Long nOpenMode = 0;
+ Any a = xPropSet->getPropertyValue("OpenMode");
+ if ( a >>= nOpenMode )
+ m_bReadOnly = !( nOpenMode & ElementModes::WRITE );
+ }
+ catch ( const css::beans::UnknownPropertyException& )
+ {
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ }
+ }
+
+ impl_Initialize();
+}
+
+sal_Bool SAL_CALL UIConfigurationManager::hasStorage()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ return m_xDocConfigStorage.is();
+}
+
+// XUIConfigurationPersistence
+void SAL_CALL UIConfigurationManager::reload()
+{
+ SolarMutexClearableGuard aGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !m_xDocConfigStorage.is() || !m_bModified || m_bReadOnly )
+ return;
+
+ // Try to access our module sub folder
+ ConfigEventNotifyContainer aRemoveNotifyContainer;
+ ConfigEventNotifyContainer aReplaceNotifyContainer;
+ for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; i++ )
+ {
+ try
+ {
+ UIElementType& rDocElementType = m_aUIElements[i];
+ if ( rDocElementType.bModified )
+ impl_reloadElementTypeData( rDocElementType, aRemoveNotifyContainer, aReplaceNotifyContainer );
+ }
+ catch ( const Exception& )
+ {
+ throw IOException();
+ }
+ }
+
+ m_bModified = false;
+
+ // Unlock mutex before notify our listeners
+ aGuard.clear();
+
+ // Notify our listeners
+ for (const ConfigurationEvent & j : aRemoveNotifyContainer)
+ implts_notifyContainerListener( j, NotifyOp_Remove );
+ for (const ConfigurationEvent & k : aReplaceNotifyContainer)
+ implts_notifyContainerListener( k, NotifyOp_Replace );
+}
+
+void SAL_CALL UIConfigurationManager::store()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !m_xDocConfigStorage.is() || !m_bModified || m_bReadOnly )
+ return;
+
+ // Try to access our module sub folder
+ for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
+ {
+ try
+ {
+ UIElementType& rElementType = m_aUIElements[i];
+
+ if ( rElementType.bModified && rElementType.xStorage.is() )
+ impl_storeElementTypeData( rElementType.xStorage, rElementType );
+ }
+ catch ( const Exception& )
+ {
+ throw IOException();
+ }
+ }
+
+ m_bModified = false;
+ Reference< XTransactedObject > xTransactedObject( m_xDocConfigStorage, UNO_QUERY );
+ if ( xTransactedObject.is() )
+ xTransactedObject->commit();
+}
+
+void SAL_CALL UIConfigurationManager::storeToStorage( const Reference< XStorage >& Storage )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !m_xDocConfigStorage.is() || !m_bModified || m_bReadOnly )
+ return;
+
+ // Try to access our module sub folder
+ for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
+ {
+ try
+ {
+ Reference< XStorage > xElementTypeStorage( Storage->openStorageElement(
+ OUString(UIELEMENTTYPENAMES[i]), ElementModes::READWRITE ));
+ UIElementType& rElementType = m_aUIElements[i];
+
+ if ( rElementType.bModified && xElementTypeStorage.is() )
+ impl_storeElementTypeData( xElementTypeStorage, rElementType, false ); // store data to storage, but don't reset modify flag!
+ }
+ catch ( const Exception& )
+ {
+ throw IOException();
+ }
+ }
+
+ Reference< XTransactedObject > xTransactedObject( Storage, UNO_QUERY );
+ if ( xTransactedObject.is() )
+ xTransactedObject->commit();
+}
+
+sal_Bool SAL_CALL UIConfigurationManager::isModified()
+{
+ SolarMutexGuard g;
+
+ return m_bModified;
+}
+
+sal_Bool SAL_CALL UIConfigurationManager::isReadOnly()
+{
+ SolarMutexGuard g;
+
+ return m_bReadOnly;
+}
+
+void UIConfigurationManager::implts_notifyContainerListener( const ConfigurationEvent& aEvent, NotifyOp eOp )
+{
+ std::unique_lock aGuard(m_mutex);
+ m_aConfigListeners.forEach(aGuard, [&eOp, &aEvent](const css::uno::Reference<XUIConfigurationListener>& l) {
+ switch ( eOp )
+ {
+ case NotifyOp_Replace:
+ l->elementReplaced( aEvent );
+ break;
+ case NotifyOp_Insert:
+ l->elementInserted( aEvent );
+ break;
+ case NotifyOp_Remove:
+ l->elementRemoved( aEvent );
+ break;
+ }
+ });
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_UIConfigurationManager_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new UIConfigurationManager(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uiconfiguration/windowstateconfiguration.cxx b/framework/source/uiconfiguration/windowstateconfiguration.cxx
new file mode 100644
index 000000000..eb7e72aff
--- /dev/null
+++ b/framework/source/uiconfiguration/windowstateconfiguration.cxx
@@ -0,0 +1,1390 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uiconfiguration/windowstateproperties.hxx>
+#include <helper/mischelper.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XModuleManager2.hpp>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/ui/DockingArea.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <comphelper/compbase.hxx>
+#include <comphelper/string.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/sequence.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <string_view>
+#include <unordered_map>
+#include <vector>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+using namespace com::sun::star::configuration;
+using namespace com::sun::star::container;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::ui;
+using namespace framework;
+
+namespace {
+
+// Zero based indexes, order must be the same as WindowStateMask && CONFIGURATION_PROPERTIES!
+const sal_Int16 PROPERTY_LOCKED = 0;
+const sal_Int16 PROPERTY_DOCKED = 1;
+const sal_Int16 PROPERTY_VISIBLE = 2;
+const sal_Int16 PROPERTY_CONTEXT = 3;
+const sal_Int16 PROPERTY_HIDEFROMMENU = 4;
+const sal_Int16 PROPERTY_NOCLOSE = 5;
+const sal_Int16 PROPERTY_SOFTCLOSE = 6;
+const sal_Int16 PROPERTY_CONTEXTACTIVE = 7;
+const sal_Int16 PROPERTY_DOCKINGAREA = 8;
+const sal_Int16 PROPERTY_POS = 9;
+const sal_Int16 PROPERTY_SIZE = 10;
+const sal_Int16 PROPERTY_UINAME = 11;
+const sal_Int16 PROPERTY_INTERNALSTATE = 12;
+const sal_Int16 PROPERTY_STYLE = 13;
+const sal_Int16 PROPERTY_DOCKPOS = 14;
+const sal_Int16 PROPERTY_DOCKSIZE = 15;
+
+// Order must be the same as WindowStateMask!!
+rtl::OUStringConstExpr CONFIGURATION_PROPERTIES[]
+{
+ WINDOWSTATE_PROPERTY_LOCKED,
+ WINDOWSTATE_PROPERTY_DOCKED,
+ WINDOWSTATE_PROPERTY_VISIBLE,
+ WINDOWSTATE_PROPERTY_CONTEXT,
+ WINDOWSTATE_PROPERTY_HIDEFROMENU,
+ WINDOWSTATE_PROPERTY_NOCLOSE,
+ WINDOWSTATE_PROPERTY_SOFTCLOSE,
+ WINDOWSTATE_PROPERTY_CONTEXTACTIVE,
+ WINDOWSTATE_PROPERTY_DOCKINGAREA,
+ WINDOWSTATE_PROPERTY_POS,
+ WINDOWSTATE_PROPERTY_SIZE,
+ WINDOWSTATE_PROPERTY_UINAME,
+ WINDOWSTATE_PROPERTY_INTERNALSTATE,
+ WINDOWSTATE_PROPERTY_STYLE,
+ WINDOWSTATE_PROPERTY_DOCKPOS,
+ WINDOWSTATE_PROPERTY_DOCKSIZE
+};
+
+// Configuration access class for WindowState supplier implementation
+
+class ConfigurationAccess_WindowState : public ::cppu::WeakImplHelper< XNameContainer, XContainerListener >
+{
+ public:
+ ConfigurationAccess_WindowState( std::u16string_view aWindowStateConfigFile, const Reference< XComponentContext >& rxContext );
+ virtual ~ConfigurationAccess_WindowState() override;
+
+ // XNameAccess
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override;
+
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XNameContainer
+ virtual void SAL_CALL removeByName( const OUString& sName ) override;
+
+ virtual void SAL_CALL insertByName( const OUString& sName, const css::uno::Any& aPropertySet ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& sName, const css::uno::Any& aPropertySet ) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // container.XContainerListener
+ virtual void SAL_CALL elementInserted( const ContainerEvent& aEvent ) override;
+ virtual void SAL_CALL elementRemoved ( const ContainerEvent& aEvent ) override;
+ virtual void SAL_CALL elementReplaced( const ContainerEvent& aEvent ) override;
+
+ // lang.XEventListener
+ virtual void SAL_CALL disposing( const EventObject& aEvent ) override;
+
+ protected:
+ enum // WindowStateMask
+ {
+ WINDOWSTATE_MASK_DOCKINGAREA = 256,
+ WINDOWSTATE_MASK_POS = 512,
+ WINDOWSTATE_MASK_SIZE = 1024,
+ WINDOWSTATE_MASK_UINAME = 2048,
+ WINDOWSTATE_MASK_INTERNALSTATE = 4096,
+ WINDOWSTATE_MASK_STYLE = 8192,
+ WINDOWSTATE_MASK_DOCKPOS = 16384,
+ WINDOWSTATE_MASK_DOCKSIZE = 32768
+ };
+
+ // Cache structure. Valid values are described by the eMask member. All other values should not be
+ // provided to outside code!
+ struct WindowStateInfo
+ {
+ WindowStateInfo()
+ : bLocked(false)
+ , bDocked(false)
+ , bVisible(false)
+ , bContext(false)
+ , bHideFromMenu(false)
+ , bNoClose(false)
+ , bSoftClose(false)
+ , bContextActive(false)
+ , aDockingArea(css::ui::DockingArea_DOCKINGAREA_TOP)
+ , aDockPos(0, 0)
+ , aPos(0, 0)
+ , aSize(0, 0)
+ , nInternalState(0)
+ , nStyle(0)
+ , nMask(0)
+ {
+ }
+
+ bool bLocked : 1,
+ bDocked : 1,
+ bVisible : 1,
+ bContext : 1,
+ bHideFromMenu : 1,
+ bNoClose : 1,
+ bSoftClose : 1,
+ bContextActive : 1;
+ css::ui::DockingArea aDockingArea;
+ css::awt::Point aDockPos;
+ css::awt::Size aDockSize;
+ css::awt::Point aPos;
+ css::awt::Size aSize;
+ OUString aUIName;
+ sal_uInt32 nInternalState;
+ sal_uInt16 nStyle;
+ sal_uInt32 nMask; // see WindowStateMask
+ };
+
+ void impl_putPropertiesFromStruct( const WindowStateInfo& rWinStateInfo, Reference< XPropertySet > const & xPropSet );
+ Any impl_insertCacheAndReturnSequence( const OUString& rResourceURL, Reference< XNameAccess > const & rNameAccess );
+ WindowStateInfo& impl_insertCacheAndReturnWinState( const OUString& rResourceURL, Reference< XNameAccess > const & rNameAccess );
+ Any impl_getSequenceFromStruct( const WindowStateInfo& rWinStateInfo );
+ void impl_fillStructFromSequence( WindowStateInfo& rWinStateInfo, const Sequence< PropertyValue >& rSeq );
+ Any impl_getWindowStateFromResourceURL( const OUString& rResourceURL );
+ void impl_initializeConfigAccess();
+
+ private:
+ typedef std::unordered_map< OUString,
+ WindowStateInfo > ResourceURLToInfoCache;
+
+ osl::Mutex m_aMutex;
+ OUString m_aConfigWindowAccess;
+ Reference< XMultiServiceFactory > m_xConfigProvider;
+ Reference< XNameAccess > m_xConfigAccess;
+ Reference< XContainerListener > m_xConfigListener;
+ ResourceURLToInfoCache m_aResourceURLToInfoCache;
+ bool m_bConfigAccessInitialized : 1,
+ m_bModified : 1;
+ std::vector< OUString > m_aPropArray;
+};
+
+ConfigurationAccess_WindowState::ConfigurationAccess_WindowState( std::u16string_view aModuleName, const Reference< XComponentContext >& rxContext ) :
+ // Create configuration hierarchical access name
+ m_aConfigWindowAccess(
+ OUString::Concat("/org.openoffice.Office.UI.") + aModuleName + "/UIElements/States"),
+ m_xConfigProvider(theDefaultProvider::get( rxContext )),
+ m_bConfigAccessInitialized( false ),
+ m_bModified( false )
+{
+ // Initialize access array with property names.
+ for (const rtl::OUStringConstExpr & s : CONFIGURATION_PROPERTIES )
+ m_aPropArray.push_back(s);
+}
+
+ConfigurationAccess_WindowState::~ConfigurationAccess_WindowState()
+{
+ // SAFE
+ osl::MutexGuard g(m_aMutex);
+ Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY );
+ if ( xContainer.is() )
+ xContainer->removeContainerListener(m_xConfigListener);
+}
+
+// XNameAccess
+Any SAL_CALL ConfigurationAccess_WindowState::getByName( const OUString& rResourceURL )
+{
+ // SAFE
+ osl::MutexGuard g(m_aMutex);
+
+ ResourceURLToInfoCache::const_iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL );
+ if ( pIter != m_aResourceURLToInfoCache.end() )
+ return impl_getSequenceFromStruct( pIter->second );
+ else
+ {
+ Any a( impl_getWindowStateFromResourceURL( rResourceURL ) );
+ if ( a == Any() )
+ throw NoSuchElementException();
+ return a;
+ }
+}
+
+Sequence< OUString > SAL_CALL ConfigurationAccess_WindowState::getElementNames()
+{
+ // SAFE
+ osl::MutexGuard g(m_aMutex);
+
+ if ( !m_bConfigAccessInitialized )
+ {
+ impl_initializeConfigAccess();
+ m_bConfigAccessInitialized = true;
+ }
+
+ if ( m_xConfigAccess.is() )
+ return m_xConfigAccess->getElementNames();
+ else
+ return Sequence< OUString > ();
+}
+
+sal_Bool SAL_CALL ConfigurationAccess_WindowState::hasByName( const OUString& rResourceURL )
+{
+ // SAFE
+ osl::MutexGuard g(m_aMutex);
+
+ ResourceURLToInfoCache::const_iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL );
+ if ( pIter != m_aResourceURLToInfoCache.end() )
+ return true;
+ else
+ {
+ Any a( impl_getWindowStateFromResourceURL( rResourceURL ) );
+ if ( a == Any() )
+ return false;
+ else
+ return true;
+ }
+}
+
+// XElementAccess
+Type SAL_CALL ConfigurationAccess_WindowState::getElementType()
+{
+ return cppu::UnoType<Sequence< PropertyValue >>::get();
+}
+
+sal_Bool SAL_CALL ConfigurationAccess_WindowState::hasElements()
+{
+ // SAFE
+ osl::MutexGuard g(m_aMutex);
+
+ if ( !m_bConfigAccessInitialized )
+ {
+ impl_initializeConfigAccess();
+ m_bConfigAccessInitialized = true;
+ }
+
+ if ( m_xConfigAccess.is() )
+ return m_xConfigAccess->hasElements();
+ else
+ return false;
+}
+
+// XNameContainer
+void SAL_CALL ConfigurationAccess_WindowState::removeByName( const OUString& rResourceURL )
+{
+ // SAFE
+ osl::ClearableMutexGuard g(m_aMutex);
+
+ ResourceURLToInfoCache::iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL );
+ if ( pIter != m_aResourceURLToInfoCache.end() )
+ m_aResourceURLToInfoCache.erase( pIter );
+
+ if ( !m_bConfigAccessInitialized )
+ {
+ impl_initializeConfigAccess();
+ m_bConfigAccessInitialized = true;
+ }
+
+ try
+ {
+ // Remove must be write-through => remove element from configuration
+ Reference< XNameContainer > xNameContainer( m_xConfigAccess, UNO_QUERY );
+ if ( xNameContainer.is() )
+ {
+ g.clear();
+
+ xNameContainer->removeByName( rResourceURL );
+ Reference< XChangesBatch > xFlush( m_xConfigAccess, UNO_QUERY );
+ if ( xFlush.is() )
+ xFlush->commitChanges();
+ }
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+}
+
+void SAL_CALL ConfigurationAccess_WindowState::insertByName( const OUString& rResourceURL, const css::uno::Any& aPropertySet )
+{
+ // SAFE
+ osl::ClearableMutexGuard g(m_aMutex);
+
+ Sequence< PropertyValue > aPropSet;
+ if ( !(aPropertySet >>= aPropSet) )
+ throw IllegalArgumentException();
+
+ ResourceURLToInfoCache::const_iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL );
+ if ( pIter != m_aResourceURLToInfoCache.end() )
+ throw ElementExistException();
+
+ if ( !m_bConfigAccessInitialized )
+ {
+ impl_initializeConfigAccess();
+ m_bConfigAccessInitialized = true;
+ }
+
+ // Try to ask our configuration access
+ if ( !m_xConfigAccess.is() )
+ return;
+
+ if ( m_xConfigAccess->hasByName( rResourceURL ) )
+ throw ElementExistException();
+
+ WindowStateInfo aWinStateInfo;
+ impl_fillStructFromSequence( aWinStateInfo, aPropSet );
+ m_aResourceURLToInfoCache.emplace( rResourceURL, aWinStateInfo );
+
+ // insert must be write-through => insert element into configuration
+ Reference< XNameContainer > xNameContainer( m_xConfigAccess, UNO_QUERY );
+ if ( !xNameContainer.is() )
+ return;
+
+ Reference< XSingleServiceFactory > xFactory( m_xConfigAccess, UNO_QUERY );
+ g.clear();
+
+ try
+ {
+ Reference< XPropertySet > xPropSet( xFactory->createInstance(), UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ Any a;
+ impl_putPropertiesFromStruct( aWinStateInfo, xPropSet );
+ a <<= xPropSet;
+ xNameContainer->insertByName( rResourceURL, a );
+ Reference< XChangesBatch > xFlush( xFactory, UNO_QUERY );
+ if ( xFlush.is() )
+ xFlush->commitChanges();
+ }
+ }
+ catch ( const Exception& )
+ {
+ }
+}
+
+// XNameReplace
+void SAL_CALL ConfigurationAccess_WindowState::replaceByName( const OUString& rResourceURL, const css::uno::Any& aPropertySet )
+{
+ // SAFE
+ osl::ClearableMutexGuard g(m_aMutex);
+
+ Sequence< PropertyValue > aPropSet;
+ if ( !(aPropertySet >>= aPropSet) )
+ throw IllegalArgumentException();
+
+ ResourceURLToInfoCache::iterator pIter = m_aResourceURLToInfoCache.find( rResourceURL );
+ if ( pIter != m_aResourceURLToInfoCache.end() )
+ {
+ WindowStateInfo& rWinStateInfo = pIter->second;
+ impl_fillStructFromSequence( rWinStateInfo, aPropSet );
+ m_bModified = true;
+ }
+ else
+ {
+ if ( !m_bConfigAccessInitialized )
+ {
+ impl_initializeConfigAccess();
+ m_bConfigAccessInitialized = true;
+ }
+
+ // Try to ask our configuration access
+ Reference< XNameAccess > xNameAccess;
+ Any a( m_xConfigAccess->getByName( rResourceURL ));
+
+ if ( !(a >>= xNameAccess) )
+ throw NoSuchElementException();
+
+ WindowStateInfo& rWinStateInfo( impl_insertCacheAndReturnWinState( rResourceURL, xNameAccess ));
+ impl_fillStructFromSequence( rWinStateInfo, aPropSet );
+ m_bModified = true;
+ pIter = m_aResourceURLToInfoCache.find( rResourceURL );
+
+ }
+
+ if ( !(m_bModified && pIter != m_aResourceURLToInfoCache.end()) )
+ return;
+
+ Reference< XNameContainer > xNameContainer( m_xConfigAccess, UNO_QUERY );
+ if ( !xNameContainer.is() )
+ return;
+
+ WindowStateInfo aWinStateInfo( pIter->second );
+ OUString aResourceURL( pIter->first );
+ m_bModified = false;
+ g.clear();
+
+ try
+ {
+ Reference< XPropertySet > xPropSet;
+ if ( xNameContainer->getByName( aResourceURL ) >>= xPropSet )
+ {
+ impl_putPropertiesFromStruct( aWinStateInfo, xPropSet );
+
+ Reference< XChangesBatch > xFlush( m_xConfigAccess, UNO_QUERY );
+ if ( xFlush.is() )
+ xFlush->commitChanges();
+ }
+ }
+ catch ( const Exception& )
+ {
+ }
+
+}
+
+// container.XContainerListener
+void SAL_CALL ConfigurationAccess_WindowState::elementInserted( const ContainerEvent& )
+{
+ // do nothing - next time someone wants to retrieve this node we will find it in the configuration
+}
+
+void SAL_CALL ConfigurationAccess_WindowState::elementRemoved ( const ContainerEvent& )
+{
+}
+
+void SAL_CALL ConfigurationAccess_WindowState::elementReplaced( const ContainerEvent& )
+{
+}
+
+// lang.XEventListener
+void SAL_CALL ConfigurationAccess_WindowState::disposing( const EventObject& aEvent )
+{
+ // SAFE
+ // remove our reference to the config access
+ osl::MutexGuard g(m_aMutex);
+
+ Reference< XInterface > xIfac1( aEvent.Source, UNO_QUERY );
+ Reference< XInterface > xIfac2( m_xConfigAccess, UNO_QUERY );
+ if ( xIfac1 == xIfac2 )
+ m_xConfigAccess.clear();
+}
+
+// private helper methods
+Any ConfigurationAccess_WindowState::impl_getSequenceFromStruct( const WindowStateInfo& rWinStateInfo )
+{
+ sal_Int32 i( 0 );
+ sal_Int32 nCount( m_aPropArray.size() );
+ std::vector< PropertyValue > aPropVec;
+
+ for ( i = 0; i < nCount; i++ )
+ {
+ if ( rWinStateInfo.nMask & ( 1 << i ))
+ {
+ // put value into the return sequence
+ PropertyValue pv;
+ pv.Name = m_aPropArray[i];
+
+ switch ( i )
+ {
+ case PROPERTY_LOCKED:
+ pv.Value <<= rWinStateInfo.bLocked; break;
+ case PROPERTY_DOCKED:
+ pv.Value <<= rWinStateInfo.bDocked; break;
+ case PROPERTY_VISIBLE:
+ pv.Value <<= rWinStateInfo.bVisible; break;
+ case PROPERTY_CONTEXT:
+ pv.Value <<= rWinStateInfo.bContext; break;
+ case PROPERTY_HIDEFROMMENU:
+ pv.Value <<= rWinStateInfo.bHideFromMenu; break;
+ case PROPERTY_NOCLOSE:
+ pv.Value <<= rWinStateInfo.bNoClose; break;
+ case PROPERTY_SOFTCLOSE:
+ pv.Value <<= rWinStateInfo.bSoftClose; break;
+ case PROPERTY_CONTEXTACTIVE:
+ pv.Value <<= rWinStateInfo.bContextActive; break;
+ case PROPERTY_DOCKINGAREA:
+ pv.Value <<= rWinStateInfo.aDockingArea; break;
+ case PROPERTY_POS:
+ pv.Value <<= rWinStateInfo.aPos; break;
+ case PROPERTY_SIZE:
+ pv.Value <<= rWinStateInfo.aSize; break;
+ case PROPERTY_UINAME:
+ pv.Value <<= rWinStateInfo.aUIName; break;
+ case PROPERTY_INTERNALSTATE:
+ pv.Value <<= sal_Int32( rWinStateInfo.nInternalState ); break;
+ case PROPERTY_STYLE:
+ pv.Value <<= sal_Int16( rWinStateInfo.nStyle ); break;
+ case PROPERTY_DOCKPOS:
+ pv.Value <<= rWinStateInfo.aDockPos; break;
+ case PROPERTY_DOCKSIZE:
+ pv.Value <<= rWinStateInfo.aDockSize; break;
+ default:
+ assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" );
+ }
+ aPropVec.push_back(pv);
+ }
+ }
+
+ return Any( comphelper::containerToSequence(aPropVec) );
+}
+
+Any ConfigurationAccess_WindowState::impl_insertCacheAndReturnSequence( const OUString& rResourceURL, Reference< XNameAccess > const & xNameAccess )
+{
+ sal_Int32 nMask( 0 );
+ sal_Int32 nCount( m_aPropArray.size() );
+ sal_Int32 i( 0 );
+ std::vector< PropertyValue > aPropVec;
+ WindowStateInfo aWindowStateInfo;
+
+ for ( i = 0; i < nCount; i++ )
+ {
+ try
+ {
+ bool bAddToSeq( false );
+ Any a( xNameAccess->getByName( m_aPropArray[i] ) );
+ switch ( i )
+ {
+ case PROPERTY_LOCKED:
+ case PROPERTY_DOCKED:
+ case PROPERTY_VISIBLE:
+ case PROPERTY_CONTEXT:
+ case PROPERTY_HIDEFROMMENU:
+ case PROPERTY_NOCLOSE:
+ case PROPERTY_SOFTCLOSE:
+ case PROPERTY_CONTEXTACTIVE:
+ {
+ bool bValue;
+ if ( a >>= bValue )
+ {
+ sal_Int32 nValue( 1 << i );
+ nMask |= nValue;
+ bAddToSeq = true;
+ switch ( i )
+ {
+ case PROPERTY_LOCKED:
+ aWindowStateInfo.bLocked = bValue; break;
+ case PROPERTY_DOCKED:
+ aWindowStateInfo.bDocked = bValue; break;
+ case PROPERTY_VISIBLE:
+ aWindowStateInfo.bVisible = bValue; break;
+ case PROPERTY_CONTEXT:
+ aWindowStateInfo.bContext = bValue; break;
+ case PROPERTY_HIDEFROMMENU:
+ aWindowStateInfo.bHideFromMenu = bValue; break;
+ case PROPERTY_NOCLOSE:
+ aWindowStateInfo.bNoClose = bValue; break;
+ case PROPERTY_SOFTCLOSE:
+ aWindowStateInfo.bSoftClose = bValue; break;
+ case PROPERTY_CONTEXTACTIVE:
+ aWindowStateInfo.bContextActive = bValue; break;
+ }
+ }
+ }
+ break;
+
+ case PROPERTY_DOCKINGAREA:
+ {
+ sal_Int32 nDockingArea = 0;
+ if ( a >>= nDockingArea )
+ {
+ if (( nDockingArea >= 0 ) &&
+ ( nDockingArea <= sal_Int32( DockingArea_DOCKINGAREA_RIGHT )))
+ {
+ aWindowStateInfo.aDockingArea = static_cast<DockingArea>(nDockingArea);
+ nMask |= WINDOWSTATE_MASK_DOCKINGAREA;
+ a <<= aWindowStateInfo.aDockingArea;
+ bAddToSeq = true;
+ }
+ }
+ }
+ break;
+
+ case PROPERTY_POS:
+ case PROPERTY_DOCKPOS:
+ {
+ OUString aString;
+ if ( a >>= aString )
+ {
+ sal_Int32 nToken( 0 );
+ std::u16string_view aXStr = o3tl::getToken(aString, 0, ',', nToken );
+ if ( nToken > 0 )
+ {
+ css::awt::Point aPos;
+ aPos.X = o3tl::toInt32(aXStr);
+ aPos.Y = o3tl::toInt32(o3tl::getToken(aString, 0, ',', nToken ));
+
+ if ( i == PROPERTY_POS )
+ {
+ aWindowStateInfo.aPos = aPos;
+ nMask |= WINDOWSTATE_MASK_POS;
+ }
+ else
+ {
+ aWindowStateInfo.aDockPos = aPos;
+ nMask |= WINDOWSTATE_MASK_DOCKPOS;
+ }
+
+ a <<= aPos;
+ bAddToSeq = true;
+ }
+ }
+ }
+ break;
+
+ case PROPERTY_SIZE:
+ case PROPERTY_DOCKSIZE:
+ {
+ OUString aString;
+ if ( a >>= aString )
+ {
+ sal_Int32 nToken( 0 );
+ std::u16string_view aStr = o3tl::getToken(aString, 0, ',', nToken );
+ if ( nToken > 0 )
+ {
+ css::awt::Size aSize;
+ aSize.Width = o3tl::toInt32(aStr);
+ aSize.Height = o3tl::toInt32(o3tl::getToken(aString, 0, ',', nToken ));
+ if ( i == PROPERTY_SIZE )
+ {
+ aWindowStateInfo.aSize = aSize;
+ nMask |= WINDOWSTATE_MASK_SIZE;
+ }
+ else
+ {
+ aWindowStateInfo.aDockSize = aSize;
+ nMask |= WINDOWSTATE_MASK_DOCKSIZE;
+ }
+
+ a <<= aSize;
+ bAddToSeq = true;
+ }
+ }
+ }
+ break;
+
+ case PROPERTY_UINAME:
+ {
+ OUString aValue;
+ if ( a >>= aValue )
+ {
+ nMask |= WINDOWSTATE_MASK_UINAME;
+ aWindowStateInfo.aUIName = aValue;
+ bAddToSeq = true;
+ }
+ }
+ break;
+
+ case PROPERTY_INTERNALSTATE:
+ {
+ sal_uInt32 nValue = 0;
+ if ( a >>= nValue )
+ {
+ nMask |= WINDOWSTATE_MASK_INTERNALSTATE;
+ aWindowStateInfo.nInternalState = nValue;
+ bAddToSeq = true;
+ }
+ }
+ break;
+
+ case PROPERTY_STYLE:
+ {
+ sal_Int32 nValue = 0;
+ if ( a >>= nValue )
+ {
+ nMask |= WINDOWSTATE_MASK_STYLE;
+ aWindowStateInfo.nStyle = sal_uInt16( nValue );
+ bAddToSeq = true;
+ }
+ }
+ break;
+
+ default:
+ assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" );
+ }
+
+ if ( bAddToSeq )
+ {
+ // put value into the return sequence
+ PropertyValue pv;
+ pv.Name = m_aPropArray[i];
+ pv.Value = a;
+ aPropVec.push_back(pv);
+ }
+ }
+ catch( const css::container::NoSuchElementException& )
+ {
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ }
+
+ aWindowStateInfo.nMask = nMask;
+ m_aResourceURLToInfoCache.emplace( rResourceURL, aWindowStateInfo );
+ return Any( comphelper::containerToSequence(aPropVec) );
+}
+
+ConfigurationAccess_WindowState::WindowStateInfo& ConfigurationAccess_WindowState::impl_insertCacheAndReturnWinState( const OUString& rResourceURL, Reference< XNameAccess > const & rNameAccess )
+{
+ sal_Int32 nMask( 0 );
+ sal_Int32 nCount( m_aPropArray.size() );
+ sal_Int32 i( 0 );
+ WindowStateInfo aWindowStateInfo;
+
+ for ( i = 0; i < nCount; i++ )
+ {
+ try
+ {
+ Any a( rNameAccess->getByName( m_aPropArray[i] ) );
+ switch ( i )
+ {
+ case PROPERTY_LOCKED:
+ case PROPERTY_DOCKED:
+ case PROPERTY_VISIBLE:
+ case PROPERTY_CONTEXT:
+ case PROPERTY_HIDEFROMMENU:
+ case PROPERTY_NOCLOSE:
+ case PROPERTY_SOFTCLOSE:
+ case PROPERTY_CONTEXTACTIVE:
+ {
+ bool bValue;
+ if ( a >>= bValue )
+ {
+ sal_Int32 nValue( 1 << i );
+ nMask |= nValue;
+ switch ( i )
+ {
+ case PROPERTY_LOCKED:
+ aWindowStateInfo.bLocked = bValue; break;
+ case PROPERTY_DOCKED:
+ aWindowStateInfo.bDocked = bValue; break;
+ case PROPERTY_VISIBLE:
+ aWindowStateInfo.bVisible = bValue; break;
+ case PROPERTY_CONTEXT:
+ aWindowStateInfo.bContext = bValue; break;
+ case PROPERTY_HIDEFROMMENU:
+ aWindowStateInfo.bHideFromMenu = bValue; break;
+ case PROPERTY_NOCLOSE:
+ aWindowStateInfo.bNoClose = bValue; break;
+ case PROPERTY_SOFTCLOSE:
+ aWindowStateInfo.bNoClose = bValue; break;
+ case PROPERTY_CONTEXTACTIVE:
+ aWindowStateInfo.bContextActive = bValue; break;
+ default:
+ SAL_WARN( "fwk.uiconfiguration", "Unknown boolean property in WindowState found!" );
+ }
+ }
+ }
+ break;
+
+ case PROPERTY_DOCKINGAREA:
+ {
+ sal_Int32 nDockingArea = 0;
+ if ( a >>= nDockingArea )
+ {
+ if (( nDockingArea >= 0 ) &&
+ ( nDockingArea <= sal_Int32( DockingArea_DOCKINGAREA_RIGHT )))
+ {
+ aWindowStateInfo.aDockingArea = static_cast<DockingArea>(nDockingArea);
+ nMask |= WINDOWSTATE_MASK_DOCKINGAREA;
+ }
+ }
+ }
+ break;
+
+ case PROPERTY_POS:
+ case PROPERTY_DOCKPOS:
+ {
+ OUString aString;
+ if ( a >>= aString )
+ {
+ sal_Int32 nToken( 0 );
+ std::u16string_view aXStr = o3tl::getToken(aString, 0, ',', nToken );
+ if ( nToken > 0 )
+ {
+ css::awt::Point aPos;
+ aPos.X = o3tl::toInt32(aXStr);
+ aPos.Y = o3tl::toInt32(o3tl::getToken(aString, 0, ',', nToken ));
+
+ if ( i == PROPERTY_POS )
+ {
+ aWindowStateInfo.aPos = aPos;
+ nMask |= WINDOWSTATE_MASK_POS;
+ }
+ else
+ {
+ aWindowStateInfo.aDockPos = aPos;
+ nMask |= WINDOWSTATE_MASK_DOCKPOS;
+ }
+ }
+ }
+ }
+ break;
+
+ case PROPERTY_SIZE:
+ case PROPERTY_DOCKSIZE:
+ {
+ OUString aString;
+ if ( a >>= aString )
+ {
+ sal_Int32 nToken( 0 );
+ std::u16string_view aStr = o3tl::getToken(aString, 0, ',', nToken );
+ if ( nToken > 0 )
+ {
+ css::awt::Size aSize;
+ aSize.Width = o3tl::toInt32(aStr);
+ aSize.Height = o3tl::toInt32(o3tl::getToken(aString, 0, ',', nToken ));
+ if ( i == PROPERTY_SIZE )
+ {
+ aWindowStateInfo.aSize = aSize;
+ nMask |= WINDOWSTATE_MASK_SIZE;
+ }
+ else
+ {
+ aWindowStateInfo.aDockSize = aSize;
+ nMask |= WINDOWSTATE_MASK_DOCKSIZE;
+ }
+ }
+ }
+ }
+ break;
+
+ case PROPERTY_UINAME:
+ {
+ OUString aValue;
+ if ( a >>= aValue )
+ {
+ nMask |= WINDOWSTATE_MASK_UINAME;
+ aWindowStateInfo.aUIName = aValue;
+ }
+ }
+ break;
+
+ case PROPERTY_INTERNALSTATE:
+ {
+ sal_Int32 nValue = 0;
+ if ( a >>= nValue )
+ {
+ nMask |= WINDOWSTATE_MASK_INTERNALSTATE;
+ aWindowStateInfo.nInternalState = sal_uInt32( nValue );
+ }
+ }
+ break;
+
+ case PROPERTY_STYLE:
+ {
+ sal_Int32 nValue = 0;
+ if ( a >>= nValue )
+ {
+ nMask |= WINDOWSTATE_MASK_STYLE;
+ aWindowStateInfo.nStyle = sal_uInt16( nValue );
+ }
+ }
+ break;
+
+ default:
+ assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" );
+ }
+ }
+ catch( const css::container::NoSuchElementException& )
+ {
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+ }
+
+ aWindowStateInfo.nMask = nMask;
+ ResourceURLToInfoCache::iterator pIter = m_aResourceURLToInfoCache.emplace( rResourceURL, aWindowStateInfo ).first;
+ return pIter->second;
+}
+
+Any ConfigurationAccess_WindowState::impl_getWindowStateFromResourceURL( const OUString& rResourceURL )
+{
+ if ( !m_bConfigAccessInitialized )
+ {
+ impl_initializeConfigAccess();
+ m_bConfigAccessInitialized = true;
+ }
+
+ try
+ {
+ // Try to ask our configuration access
+ if ( m_xConfigAccess.is() && m_xConfigAccess->hasByName( rResourceURL ) )
+ {
+
+ Reference< XNameAccess > xNameAccess( m_xConfigAccess->getByName( rResourceURL ), UNO_QUERY );
+ if ( xNameAccess.is() )
+ return impl_insertCacheAndReturnSequence( rResourceURL, xNameAccess );
+ }
+ }
+ catch( const css::container::NoSuchElementException& )
+ {
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ }
+
+ return Any();
+}
+
+void ConfigurationAccess_WindowState::impl_fillStructFromSequence( WindowStateInfo& rWinStateInfo, const Sequence< PropertyValue >& rSeq )
+{
+ sal_Int32 nCompareCount( m_aPropArray.size() );
+ sal_Int32 nCount( rSeq.getLength() );
+ sal_Int32 i( 0 );
+
+ for ( i = 0; i < nCount; i++ )
+ {
+ for ( sal_Int32 j = 0; j < nCompareCount; j++ )
+ {
+ if ( rSeq[i].Name == m_aPropArray[j] )
+ {
+ switch ( j )
+ {
+ case PROPERTY_LOCKED:
+ case PROPERTY_DOCKED:
+ case PROPERTY_VISIBLE:
+ case PROPERTY_CONTEXT:
+ case PROPERTY_HIDEFROMMENU:
+ case PROPERTY_NOCLOSE:
+ case PROPERTY_SOFTCLOSE:
+ case PROPERTY_CONTEXTACTIVE:
+ {
+ bool bValue;
+ if ( rSeq[i].Value >>= bValue )
+ {
+ sal_Int32 nValue( 1 << j );
+ rWinStateInfo.nMask |= nValue;
+ switch ( j )
+ {
+ case PROPERTY_LOCKED:
+ rWinStateInfo.bLocked = bValue;
+ break;
+ case PROPERTY_DOCKED:
+ rWinStateInfo.bDocked = bValue;
+ break;
+ case PROPERTY_VISIBLE:
+ rWinStateInfo.bVisible = bValue;
+ break;
+ case PROPERTY_CONTEXT:
+ rWinStateInfo.bContext = bValue;
+ break;
+ case PROPERTY_HIDEFROMMENU:
+ rWinStateInfo.bHideFromMenu = bValue;
+ break;
+ case PROPERTY_NOCLOSE:
+ rWinStateInfo.bNoClose = bValue;
+ break;
+ case PROPERTY_SOFTCLOSE:
+ rWinStateInfo.bSoftClose = bValue;
+ break;
+ case PROPERTY_CONTEXTACTIVE:
+ rWinStateInfo.bContextActive = bValue;
+ break;
+ }
+ }
+ }
+ break;
+
+ case PROPERTY_DOCKINGAREA:
+ {
+ css::ui::DockingArea eDockingArea;
+ if ( rSeq[i].Value >>= eDockingArea )
+ {
+ rWinStateInfo.aDockingArea = eDockingArea;
+ rWinStateInfo.nMask |= WINDOWSTATE_MASK_DOCKINGAREA;
+ }
+ }
+ break;
+
+ case PROPERTY_POS:
+ case PROPERTY_DOCKPOS:
+ {
+ css::awt::Point aPoint;
+ if ( rSeq[i].Value >>= aPoint )
+ {
+ if ( j == PROPERTY_POS )
+ {
+ rWinStateInfo.aPos = aPoint;
+ rWinStateInfo.nMask |= WINDOWSTATE_MASK_POS;
+ }
+ else
+ {
+ rWinStateInfo.aDockPos = aPoint;
+ rWinStateInfo.nMask |= WINDOWSTATE_MASK_DOCKPOS;
+ }
+ }
+ }
+ break;
+
+ case PROPERTY_SIZE:
+ case PROPERTY_DOCKSIZE:
+ {
+ css::awt::Size aSize;
+ if ( rSeq[i].Value >>= aSize )
+ {
+ if ( j == PROPERTY_SIZE )
+ {
+ rWinStateInfo.aSize = aSize;
+ rWinStateInfo.nMask |= WINDOWSTATE_MASK_SIZE;
+ }
+ else
+ {
+ rWinStateInfo.aDockSize = aSize;
+ rWinStateInfo.nMask |= WINDOWSTATE_MASK_DOCKSIZE;
+ }
+ }
+ }
+ break;
+
+ case PROPERTY_UINAME:
+ {
+ OUString aValue;
+ if ( rSeq[i].Value >>= aValue )
+ {
+ rWinStateInfo.aUIName = aValue;
+ rWinStateInfo.nMask |= WINDOWSTATE_MASK_UINAME;
+ }
+ }
+ break;
+
+ case PROPERTY_INTERNALSTATE:
+ {
+ sal_Int32 nValue = 0;
+ if ( rSeq[i].Value >>= nValue )
+ {
+ rWinStateInfo.nInternalState = sal_uInt32( nValue );
+ rWinStateInfo.nMask |= WINDOWSTATE_MASK_INTERNALSTATE;
+ }
+ }
+ break;
+
+ case PROPERTY_STYLE:
+ {
+ sal_Int32 nValue = 0;
+ if ( rSeq[i].Value >>= nValue )
+ {
+ rWinStateInfo.nStyle = sal_uInt16( nValue );
+ rWinStateInfo.nMask |= WINDOWSTATE_MASK_STYLE;
+ }
+ }
+ break;
+
+ default:
+ assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" );
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+void ConfigurationAccess_WindowState::impl_putPropertiesFromStruct( const WindowStateInfo& rWinStateInfo, Reference< XPropertySet > const & xPropSet )
+{
+ sal_Int32 i( 0 );
+ sal_Int32 nCount( m_aPropArray.size() );
+ OUString aDelim( "," );
+
+ for ( i = 0; i < nCount; i++ )
+ {
+ if ( rWinStateInfo.nMask & ( 1 << i ))
+ {
+ try
+ {
+ // put values into the property set
+ switch ( i )
+ {
+ case PROPERTY_LOCKED:
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bLocked ) ); break;
+ case PROPERTY_DOCKED:
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bDocked ) ); break;
+ case PROPERTY_VISIBLE:
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bVisible ) ); break;
+ case PROPERTY_CONTEXT:
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bContext ) ); break;
+ case PROPERTY_HIDEFROMMENU:
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bHideFromMenu ) ); break;
+ case PROPERTY_NOCLOSE:
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bNoClose ) ); break;
+ case PROPERTY_SOFTCLOSE:
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bSoftClose ) ); break;
+ case PROPERTY_CONTEXTACTIVE:
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.bContextActive ) ); break;
+ case PROPERTY_DOCKINGAREA:
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( sal_Int16( rWinStateInfo.aDockingArea ) ) ); break;
+ case PROPERTY_POS:
+ case PROPERTY_DOCKPOS:
+ {
+ OUString aPosStr;
+ if ( i == PROPERTY_POS )
+ aPosStr = OUString::number( rWinStateInfo.aPos.X );
+ else
+ aPosStr = OUString::number( rWinStateInfo.aDockPos.X );
+ aPosStr += aDelim;
+ if ( i == PROPERTY_POS )
+ aPosStr += OUString::number( rWinStateInfo.aPos.Y );
+ else
+ aPosStr += OUString::number( rWinStateInfo.aDockPos.Y );
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( aPosStr ) );
+ break;
+ }
+ case PROPERTY_SIZE:
+ case PROPERTY_DOCKSIZE:
+ {
+ OUString aSizeStr;
+ if ( i == PROPERTY_SIZE )
+ aSizeStr = OUString::number( rWinStateInfo.aSize.Width );
+ else
+ aSizeStr = OUString::number( rWinStateInfo.aDockSize.Width );
+ aSizeStr += aDelim;
+ if ( i == PROPERTY_SIZE )
+ aSizeStr += OUString::number( rWinStateInfo.aSize.Height );
+ else
+ aSizeStr += OUString::number( rWinStateInfo.aDockSize.Height );
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( aSizeStr ) );
+ break;
+ }
+ case PROPERTY_UINAME:
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( rWinStateInfo.aUIName ) ); break;
+ case PROPERTY_INTERNALSTATE:
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( sal_Int32( rWinStateInfo.nInternalState )) ); break;
+ case PROPERTY_STYLE:
+ xPropSet->setPropertyValue( m_aPropArray[i], Any( sal_Int32( rWinStateInfo.nStyle )) ); break;
+ default:
+ assert( false && "Wrong value for ConfigurationAccess_WindowState. Who has forgotten to add this new property!" );
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+ }
+}
+
+void ConfigurationAccess_WindowState::impl_initializeConfigAccess()
+{
+ try
+ {
+ Sequence<Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", Any(m_aConfigWindowAccess)}
+ }));
+ m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess", aArgs ), UNO_QUERY );
+ if ( m_xConfigAccess.is() )
+ {
+ // Add as container listener
+ Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY );
+ if ( xContainer.is() )
+ {
+ m_xConfigListener = new WeakContainerListener(this);
+ xContainer->addContainerListener(m_xConfigListener);
+ }
+ }
+ }
+ catch ( const WrappedTargetException& )
+ {
+ }
+ catch ( const Exception& )
+ {
+ }
+}
+
+typedef comphelper::WeakComponentImplHelper< css::container::XNameAccess,
+ css::lang::XServiceInfo> WindowStateConfiguration_BASE;
+
+class WindowStateConfiguration : public WindowStateConfiguration_BASE
+{
+public:
+ explicit WindowStateConfiguration( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~WindowStateConfiguration() override;
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.WindowStateConfiguration";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.ui.WindowStateConfiguration"};
+ }
+
+ // XNameAccess
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override;
+
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ typedef std::unordered_map< OUString,
+ OUString > ModuleToWindowStateFileMap;
+
+ typedef std::unordered_map< OUString,
+ css::uno::Reference< css::container::XNameAccess > > ModuleToWindowStateConfigHashMap;
+
+private:
+ css::uno::Reference< css::uno::XComponentContext> m_xContext;
+ ModuleToWindowStateFileMap m_aModuleToFileHashMap;
+ ModuleToWindowStateConfigHashMap m_aModuleToWindowStateHashMap;
+};
+
+WindowStateConfiguration::WindowStateConfiguration( const Reference< XComponentContext >& rxContext ) :
+ m_xContext( rxContext )
+{
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager =
+ ModuleManager::create( m_xContext );
+ Reference< XNameAccess > xEmptyNameAccess;
+ Sequence< OUString > aElementNames;
+ try
+ {
+ aElementNames = xModuleManager->getElementNames();
+ }
+ catch (const css::uno::RuntimeException &)
+ {
+ }
+ Sequence< PropertyValue > aSeq;
+
+ for ( OUString const & aModuleIdentifier : std::as_const(aElementNames) )
+ {
+ if ( xModuleManager->getByName( aModuleIdentifier ) >>= aSeq )
+ {
+ OUString aWindowStateFileStr;
+ for ( PropertyValue const & rProp : std::as_const(aSeq) )
+ {
+ if ( rProp.Name == "ooSetupFactoryWindowStateConfigRef" )
+ {
+ rProp.Value >>= aWindowStateFileStr;
+ break;
+ }
+ }
+
+ if ( !aWindowStateFileStr.isEmpty() )
+ {
+ // Create first mapping ModuleIdentifier ==> Window state configuration file
+ m_aModuleToFileHashMap.emplace( aModuleIdentifier, aWindowStateFileStr );
+
+ // Create second mapping Command File ==> Window state configuration instance
+ ModuleToWindowStateConfigHashMap::iterator pIter = m_aModuleToWindowStateHashMap.find( aWindowStateFileStr );
+ if ( pIter == m_aModuleToWindowStateHashMap.end() )
+ m_aModuleToWindowStateHashMap.emplace( aWindowStateFileStr, xEmptyNameAccess );
+ }
+ }
+ }
+}
+
+WindowStateConfiguration::~WindowStateConfiguration()
+{
+ std::unique_lock g(m_aMutex);
+ m_aModuleToFileHashMap.clear();
+ m_aModuleToWindowStateHashMap.clear();
+}
+
+Any SAL_CALL WindowStateConfiguration::getByName( const OUString& aModuleIdentifier )
+{
+ std::unique_lock g(m_aMutex);
+
+ ModuleToWindowStateFileMap::const_iterator pIter = m_aModuleToFileHashMap.find( aModuleIdentifier );
+ if ( pIter != m_aModuleToFileHashMap.end() )
+ {
+ Any a;
+ OUString aWindowStateConfigFile( pIter->second );
+
+ ModuleToWindowStateConfigHashMap::iterator pModuleIter = m_aModuleToWindowStateHashMap.find( aWindowStateConfigFile );
+ if ( pModuleIter != m_aModuleToWindowStateHashMap.end() )
+ {
+ if ( pModuleIter->second.is() )
+ a <<= pModuleIter->second;
+ else
+ {
+ Reference< XNameAccess > xResourceURLWindowState = new ConfigurationAccess_WindowState( aWindowStateConfigFile, m_xContext );
+ pModuleIter->second = xResourceURLWindowState;
+ a <<= xResourceURLWindowState;
+ }
+
+ return a;
+ }
+ }
+
+ throw NoSuchElementException();
+}
+
+Sequence< OUString > SAL_CALL WindowStateConfiguration::getElementNames()
+{
+ std::unique_lock g(m_aMutex);
+
+ return comphelper::mapKeysToSequence( m_aModuleToFileHashMap );
+}
+
+sal_Bool SAL_CALL WindowStateConfiguration::hasByName( const OUString& aName )
+{
+ std::unique_lock g(m_aMutex);
+
+ ModuleToWindowStateFileMap::const_iterator pIter = m_aModuleToFileHashMap.find( aName );
+ return ( pIter != m_aModuleToFileHashMap.end() );
+}
+
+// XElementAccess
+Type SAL_CALL WindowStateConfiguration::getElementType()
+{
+ return cppu::UnoType<XNameAccess>::get();
+}
+
+sal_Bool SAL_CALL WindowStateConfiguration::hasElements()
+{
+ // We always have at least one module. So it is valid to return true!
+ return true;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_WindowStateConfiguration_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new WindowStateConfiguration(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/FixedImageToolbarController.cxx b/framework/source/uielement/FixedImageToolbarController.cxx
new file mode 100644
index 000000000..303913432
--- /dev/null
+++ b/framework/source/uielement/FixedImageToolbarController.cxx
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/FixedImageToolbarController.hxx>
+
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <svtools/miscopt.hxx>
+#include <svtools/imgdef.hxx>
+#include <framework/addonsoptions.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::util;
+
+namespace framework
+{
+class FixedImageControl final : public InterimItemWindow
+{
+public:
+ FixedImageControl(vcl::Window* pParent, const OUString& rCommand);
+ virtual ~FixedImageControl() override;
+ virtual void dispose() override;
+ DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool);
+
+private:
+ std::unique_ptr<weld::Image> m_xWidget;
+};
+
+FixedImageControl::FixedImageControl(vcl::Window* pParent, const OUString& rCommand)
+ : InterimItemWindow(pParent, "svt/ui/fixedimagecontrol.ui", "FixedImageControl")
+ , m_xWidget(m_xBuilder->weld_image("image"))
+{
+ InitControlBase(m_xWidget.get());
+
+ m_xWidget->connect_key_press(LINK(this, FixedImageControl, KeyInputHdl));
+
+ bool bBigImages(SvtMiscOptions().AreCurrentSymbolsLarge());
+ auto xImage
+ = Graphic(AddonsOptions().GetImageFromURL(rCommand, bBigImages, true)).GetXGraphic();
+ m_xWidget->set_image(xImage);
+
+ SetSizePixel(get_preferred_size());
+}
+
+IMPL_LINK(FixedImageControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
+{
+ return ChildKeyInput(rKEvt);
+}
+
+FixedImageControl::~FixedImageControl() { disposeOnce(); }
+
+void FixedImageControl::dispose()
+{
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+}
+
+FixedImageToolbarController::FixedImageToolbarController(
+ const Reference<XComponentContext>& rxContext, const Reference<XFrame>& rFrame,
+ ToolBox* pToolbar, ToolBoxItemId nID, const OUString& rCommand)
+ : ComplexToolbarController(rxContext, rFrame, pToolbar, nID, rCommand)
+ , m_eSymbolSize(SvtMiscOptions().GetCurrentSymbolsSize())
+{
+ m_pFixedImageControl = VclPtr<FixedImageControl>::Create(m_xToolbar, rCommand);
+ m_xToolbar->SetItemWindow(m_nID, m_pFixedImageControl);
+
+ SvtMiscOptions().AddListenerLink(LINK(this, FixedImageToolbarController, MiscOptionsChanged));
+}
+
+void SAL_CALL FixedImageToolbarController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+ SvtMiscOptions().RemoveListenerLink(
+ LINK(this, FixedImageToolbarController, MiscOptionsChanged));
+ m_xToolbar->SetItemWindow(m_nID, nullptr);
+ m_pFixedImageControl.disposeAndClear();
+ ComplexToolbarController::dispose();
+}
+
+void FixedImageToolbarController::executeControlCommand(const css::frame::ControlCommand&) {}
+
+void FixedImageToolbarController::CheckAndUpdateImages()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ SvtMiscOptions aMiscOptions;
+ const sal_Int16 eNewSymbolSize = aMiscOptions.GetCurrentSymbolsSize();
+
+ if (m_eSymbolSize == eNewSymbolSize)
+ return;
+
+ m_eSymbolSize = eNewSymbolSize;
+
+ // Refresh images if requested
+ ::Size aSize(16, 16);
+ if (m_eSymbolSize == SFX_SYMBOLS_SIZE_LARGE)
+ {
+ aSize.setWidth(26);
+ aSize.setHeight(26);
+ }
+ else if (m_eSymbolSize == SFX_SYMBOLS_SIZE_32)
+ {
+ aSize.setWidth(32);
+ aSize.setHeight(32);
+ }
+ m_pFixedImageControl->SetSizePixel(aSize);
+}
+
+IMPL_LINK_NOARG(FixedImageToolbarController, MiscOptionsChanged, LinkParamNone*, void)
+{
+ CheckAndUpdateImages();
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/FixedTextToolbarController.cxx b/framework/source/uielement/FixedTextToolbarController.cxx
new file mode 100644
index 000000000..02bfd4c5d
--- /dev/null
+++ b/framework/source/uielement/FixedTextToolbarController.cxx
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/FixedTextToolbarController.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::util;
+
+namespace framework
+{
+class FixedTextControl final : public InterimItemWindow
+{
+public:
+ FixedTextControl(vcl::Window* pParent);
+ virtual ~FixedTextControl() override;
+ virtual void dispose() override;
+ OUString get_label() const { return m_xWidget->get_label(); }
+ void set_label(const OUString& rLabel) { return m_xWidget->set_label(rLabel); }
+ DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool);
+
+private:
+ std::unique_ptr<weld::Label> m_xWidget;
+};
+
+FixedTextControl::FixedTextControl(vcl::Window* pParent)
+ : InterimItemWindow(pParent, "svt/ui/fixedtextcontrol.ui", "FixedTextControl")
+ , m_xWidget(m_xBuilder->weld_label("label"))
+{
+ InitControlBase(m_xWidget.get());
+
+ m_xWidget->connect_key_press(LINK(this, FixedTextControl, KeyInputHdl));
+}
+
+IMPL_LINK(FixedTextControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
+{
+ return ChildKeyInput(rKEvt);
+}
+
+FixedTextControl::~FixedTextControl() { disposeOnce(); }
+
+void FixedTextControl::dispose()
+{
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+}
+
+FixedTextToolbarController::FixedTextToolbarController(
+ const Reference<XComponentContext>& rxContext, const Reference<XFrame>& rFrame,
+ ToolBox* pToolbar, ToolBoxItemId nID, const OUString& aCommand)
+ : ComplexToolbarController(rxContext, rFrame, pToolbar, nID, aCommand)
+{
+ m_pFixedTextControl = VclPtr<FixedTextControl>::Create(m_xToolbar);
+ m_xToolbar->SetItemWindow(m_nID, m_pFixedTextControl);
+ m_xToolbar->SetItemBits(m_nID, ToolBoxItemBits::AUTOSIZE | m_xToolbar->GetItemBits(m_nID));
+}
+
+void SAL_CALL FixedTextToolbarController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+ m_xToolbar->SetItemWindow(m_nID, nullptr);
+ m_pFixedTextControl.disposeAndClear();
+ ComplexToolbarController::dispose();
+}
+
+Sequence<PropertyValue> FixedTextToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const
+{
+ const OUString aSelectedText = m_pFixedTextControl->get_label();
+
+ // Add key modifier to argument list
+ Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier),
+ comphelper::makePropertyValue("Text", aSelectedText) };
+ return aArgs;
+}
+
+void FixedTextToolbarController::executeControlCommand(
+ const css::frame::ControlCommand& rControlCommand)
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ if (rControlCommand.Command != "SetText")
+ return;
+
+ for (const NamedValue& rArg : rControlCommand.Arguments)
+ {
+ if (rArg.Name == "Text")
+ {
+ OUString aText;
+ rArg.Value >>= aText;
+ m_pFixedTextControl->set_label(aText);
+ m_pFixedTextControl->SetSizePixel(m_pFixedTextControl->get_preferred_size());
+
+ // send notification
+ notifyTextChanged(aText);
+ break;
+ }
+ }
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/addonstoolbarwrapper.cxx b/framework/source/uielement/addonstoolbarwrapper.cxx
new file mode 100644
index 000000000..6547d1c6b
--- /dev/null
+++ b/framework/source/uielement/addonstoolbarwrapper.cxx
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/addonstoolbarwrapper.hxx>
+#include <uielement/toolbarmanager.hxx>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/ui/UIElementType.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+using namespace com::sun::star::awt;
+using namespace ::com::sun::star::ui;
+
+namespace framework
+{
+
+AddonsToolBarWrapper::AddonsToolBarWrapper( const Reference< XComponentContext >& xContext ) :
+ UIElementWrapperBase( UIElementType::TOOLBAR ),
+ m_xContext( xContext ),
+ m_bCreatedImages( false )
+{
+}
+
+AddonsToolBarWrapper::~AddonsToolBarWrapper()
+{
+}
+
+// XComponent
+void SAL_CALL AddonsToolBarWrapper::dispose()
+{
+ Reference< XComponent > xThis(this);
+
+ css::lang::EventObject aEvent( xThis );
+ m_aListenerContainer.disposeAndClear( aEvent );
+
+ SolarMutexGuard g;
+
+ if ( m_xToolBarManager.is() )
+ m_xToolBarManager->dispose();
+ m_xToolBarManager.clear();
+
+ m_bDisposed = true;
+}
+
+// XInitialization
+void SAL_CALL AddonsToolBarWrapper::initialize( const Sequence< Any >& aArguments )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( m_bInitialized )
+ return;
+
+ UIElementWrapperBase::initialize( aArguments );
+
+ for ( const Any& rArg : aArguments )
+ {
+ PropertyValue aPropValue;
+ if ( rArg >>= aPropValue )
+ {
+ if ( aPropValue.Name == "ConfigurationData" )
+ aPropValue.Value >>= m_aConfigData;
+ }
+ }
+
+ Reference< XFrame > xFrame( m_xWeakFrame );
+ if ( !(xFrame.is() && m_aConfigData.hasElements()) )
+ return;
+
+ // Create VCL based toolbar which will be filled with settings data
+ VclPtr<ToolBox> pToolBar;
+ rtl::Reference<ToolBarManager> pToolBarManager;
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() );
+ if ( pWindow )
+ {
+ sal_uLong nStyles = WB_BORDER | WB_SCROLL | WB_MOVEABLE | WB_3DLOOK | WB_DOCKABLE | WB_SIZEABLE | WB_CLOSEABLE;
+
+ pToolBar = VclPtr<ToolBox>::Create( pWindow, nStyles );
+ pToolBar->SetLineSpacing(true);
+ pToolBarManager = new ToolBarManager( m_xContext, xFrame, m_aResourceURL, pToolBar );
+ m_xToolBarManager = pToolBarManager;
+ }
+ }
+
+ try
+ {
+ if ( m_aConfigData.hasElements() && pToolBar && pToolBarManager )
+ {
+ // Fill toolbar with container contents
+ pToolBarManager->FillAddonToolbar( m_aConfigData );
+ pToolBar->EnableCustomize();
+ ::Size aActSize( pToolBar->GetSizePixel() );
+ ::Size aSize( pToolBar->CalcWindowSizePixel() );
+ aSize.setWidth( aActSize.Width() );
+ pToolBar->SetSizePixel( aSize );
+ }
+ }
+ catch ( const NoSuchElementException& )
+ {
+ }
+}
+
+// XUIElement interface
+Reference< XInterface > SAL_CALL AddonsToolBarWrapper::getRealInterface()
+{
+ SolarMutexGuard g;
+
+ if ( m_xToolBarManager.is() )
+ {
+ ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() );
+ if ( pToolBarManager )
+ {
+ vcl::Window* pWindow = pToolBarManager->GetToolBar();
+ return Reference< XInterface >( VCLUnoHelper::GetInterface( pWindow ), UNO_QUERY );
+ }
+ }
+
+ return Reference< XInterface >();
+}
+
+// allow late population of images for add-on toolbars
+void AddonsToolBarWrapper::populateImages()
+{
+ SolarMutexGuard g;
+
+ if (m_bCreatedImages)
+ return;
+
+ if ( m_xToolBarManager.is() )
+ {
+ ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() );
+ if (pToolBarManager)
+ {
+ pToolBarManager->RequestImages();
+ m_bCreatedImages = true;
+ }
+ }
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/buttontoolbarcontroller.cxx b/framework/source/uielement/buttontoolbarcontroller.cxx
new file mode 100644
index 000000000..4972b6da7
--- /dev/null
+++ b/framework/source/uielement/buttontoolbarcontroller.cxx
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/buttontoolbarcontroller.hxx>
+
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <cppuhelper/queryinterface.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+
+using namespace ::com::sun::star;
+using namespace css::awt;
+using namespace css::uno;
+using namespace css::beans;
+using namespace css::lang;
+using namespace css::frame;
+using namespace css::util;
+
+namespace framework
+{
+
+ButtonToolbarController::ButtonToolbarController(
+ uno::Reference< uno::XComponentContext > xContext,
+ ToolBox* pToolBar,
+ OUString aCommand ) :
+ m_bInitialized( false ),
+ m_bDisposed( false ),
+ m_aCommandURL(std::move( aCommand )),
+ m_xContext(std::move( xContext )),
+ m_pToolbar( pToolBar )
+{
+}
+
+ButtonToolbarController::~ButtonToolbarController()
+{
+}
+
+ // XInterface
+uno::Any SAL_CALL ButtonToolbarController::queryInterface( const uno::Type& rType )
+{
+ Any a = ::cppu::queryInterface(
+ rType ,
+ static_cast< frame::XStatusListener* >( this ),
+ static_cast< frame::XToolbarController* >( this ),
+ static_cast< lang::XInitialization* >( this ),
+ static_cast< lang::XComponent* >( this ),
+ static_cast< util::XUpdatable* >( this ));
+
+ if ( a.hasValue() )
+ return a;
+
+ return cppu::OWeakObject::queryInterface( rType );
+}
+
+void SAL_CALL ButtonToolbarController::acquire() noexcept
+{
+ cppu::OWeakObject::acquire();
+}
+
+void SAL_CALL ButtonToolbarController::release() noexcept
+{
+ cppu::OWeakObject::release();
+}
+
+// XInitialization
+void SAL_CALL ButtonToolbarController::initialize(
+ const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ bool bInitialized( true );
+
+ {
+ SolarMutexGuard aSolarMutexGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ bInitialized = m_bInitialized;
+ }
+
+ if ( bInitialized )
+ return;
+
+ SolarMutexGuard aSolarMutexGuard;
+ m_bInitialized = true;
+
+ PropertyValue aPropValue;
+ for ( const css::uno::Any& rArg : aArguments )
+ {
+ if ( rArg >>= aPropValue )
+ {
+ if ( aPropValue.Name == "Frame" )
+ m_xFrame.set(aPropValue.Value,UNO_QUERY);
+ else if ( aPropValue.Name == "CommandURL" )
+ aPropValue.Value >>= m_aCommandURL;
+ else if ( aPropValue.Name == "ServiceManager" )
+ {
+ Reference<XMultiServiceFactory> xServiceManager(aPropValue.Value,UNO_QUERY);
+ m_xContext = comphelper::getComponentContext(xServiceManager);
+ }
+ }
+ }
+}
+
+// XComponent
+void SAL_CALL ButtonToolbarController::dispose()
+{
+ Reference< XComponent > xThis = this;
+
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ m_xContext.clear();
+ m_xURLTransformer.clear();
+ m_xFrame.clear();
+ m_pToolbar.clear();
+ m_bDisposed = true;
+ }
+}
+
+void SAL_CALL ButtonToolbarController::addEventListener(
+ const css::uno::Reference< css::lang::XEventListener >& )
+{
+ // do nothing
+}
+
+void SAL_CALL ButtonToolbarController::removeEventListener(
+ const css::uno::Reference< css::lang::XEventListener >& )
+{
+ // do nothing
+}
+
+// XUpdatable
+void SAL_CALL ButtonToolbarController::update()
+{
+ SolarMutexGuard aSolarMutexGuard;
+ if ( m_bDisposed )
+ throw DisposedException();
+}
+
+// XEventListener
+void SAL_CALL ButtonToolbarController::disposing(
+ const css::lang::EventObject& Source )
+{
+ uno::Reference< uno::XInterface > xSource( Source.Source );
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ if ( m_bDisposed )
+ return;
+
+ uno::Reference< uno::XInterface > xIfac( m_xFrame, uno::UNO_QUERY );
+ if ( xIfac == xSource )
+ m_xFrame.clear();
+}
+
+void SAL_CALL ButtonToolbarController::statusChanged( const css::frame::FeatureStateEvent& )
+{
+ // do nothing
+ if ( m_bDisposed )
+ throw DisposedException();
+}
+
+// XToolbarController
+void SAL_CALL ButtonToolbarController::execute( sal_Int16 KeyModifier )
+{
+ uno::Reference< frame::XDispatch > xDispatch;
+ uno::Reference< frame::XFrame > xFrame;
+ uno::Reference< util::XURLTransformer > xURLTransformer;
+ OUString aCommandURL;
+ css::util::URL aTargetURL;
+
+ {
+ SolarMutexGuard aSolarMutexGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( m_bInitialized &&
+ m_xFrame.is() &&
+ m_xContext.is() &&
+ !m_aCommandURL.isEmpty() )
+ {
+ if ( !m_xURLTransformer.is() )
+ {
+ m_xURLTransformer = util::URLTransformer::create( m_xContext );
+ }
+
+ xFrame = m_xFrame;
+ aCommandURL = m_aCommandURL;
+ xURLTransformer = m_xURLTransformer;
+ }
+ }
+
+ uno::Reference< frame::XDispatchProvider > xDispatchProvider( xFrame, uno::UNO_QUERY );
+ if ( xDispatchProvider.is() )
+ {
+ aTargetURL.Complete = aCommandURL;
+ xURLTransformer->parseStrict( aTargetURL );
+ xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+ }
+
+ if ( !xDispatch.is() )
+ return;
+
+ try
+ {
+ // Provide key modifier information to dispatch function
+ Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier) };
+
+ xDispatch->dispatch( aTargetURL, aArgs );
+ }
+ catch ( const DisposedException& )
+ {
+ }
+}
+
+void SAL_CALL ButtonToolbarController::click()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ sal_Int16 nKeyModifier( static_cast<sal_Int16>(m_pToolbar->GetModifier()) );
+ execute( nKeyModifier );
+}
+
+void SAL_CALL ButtonToolbarController::doubleClick()
+{
+ // do nothing
+ if ( m_bDisposed )
+ throw DisposedException();
+}
+
+uno::Reference< awt::XWindow > SAL_CALL ButtonToolbarController::createPopupWindow()
+{
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ return uno::Reference< awt::XWindow >();
+}
+
+uno::Reference< awt::XWindow > SAL_CALL ButtonToolbarController::createItemWindow(
+ const css::uno::Reference< css::awt::XWindow >& )
+{
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ return uno::Reference< awt::XWindow >();
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/comboboxtoolbarcontroller.cxx b/framework/source/uielement/comboboxtoolbarcontroller.cxx
new file mode 100644
index 000000000..77d6e3e8c
--- /dev/null
+++ b/framework/source/uielement/comboboxtoolbarcontroller.cxx
@@ -0,0 +1,333 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/comboboxtoolbarcontroller.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <svtools/toolboxcontroller.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+
+using namespace ::com::sun::star;
+using namespace css::uno;
+using namespace css::beans;
+using namespace css::lang;
+using namespace css::frame;
+using namespace css::util;
+
+namespace framework
+{
+
+// Wrapper class to notify controller about events from combobox.
+// Unfortunately the events are notified through virtual methods instead
+// of Listeners.
+
+class ComboBoxControl final : public InterimItemWindow
+{
+public:
+ ComboBoxControl(vcl::Window* pParent, ComboboxToolbarController* pComboboxToolbarController);
+ virtual ~ComboBoxControl() override;
+ virtual void dispose() override;
+
+ void set_active_or_entry_text(const OUString& rText);
+ OUString get_active_text() const { return m_xWidget->get_active_text(); }
+
+ void clear() { m_xWidget->clear(); }
+ void remove(int nIndex) { m_xWidget->remove(nIndex); }
+ void append_text(const OUString& rStr) { m_xWidget->append_text(rStr); }
+ void insert_text(int pos, const OUString& rStr) { m_xWidget->insert_text(pos, rStr); }
+ int get_count() const { return m_xWidget->get_count(); }
+ int find_text(const OUString& rStr) const { return m_xWidget->find_text(rStr); }
+
+ DECL_LINK(FocusInHdl, weld::Widget&, void);
+ DECL_LINK(FocusOutHdl, weld::Widget&, void);
+ DECL_LINK(ModifyHdl, weld::ComboBox&, void);
+ DECL_LINK(ActivateHdl, weld::ComboBox&, bool);
+ DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool);
+
+private:
+ std::unique_ptr<weld::ComboBox> m_xWidget;
+ ComboboxToolbarController* m_pComboboxToolbarController;
+};
+
+ComboBoxControl::ComboBoxControl(vcl::Window* pParent, ComboboxToolbarController* pComboboxToolbarController)
+ : InterimItemWindow(pParent, "svt/ui/combocontrol.ui", "ComboControl")
+ , m_xWidget(m_xBuilder->weld_combo_box("combobox"))
+ , m_pComboboxToolbarController(pComboboxToolbarController)
+{
+ InitControlBase(m_xWidget.get());
+
+ m_xWidget->connect_focus_in(LINK(this, ComboBoxControl, FocusInHdl));
+ m_xWidget->connect_focus_out(LINK(this, ComboBoxControl, FocusOutHdl));
+ m_xWidget->connect_changed(LINK(this, ComboBoxControl, ModifyHdl));
+ m_xWidget->connect_entry_activate(LINK(this, ComboBoxControl, ActivateHdl));
+ m_xWidget->connect_key_press(LINK(this, ComboBoxControl, KeyInputHdl));
+
+ m_xWidget->set_entry_width_chars(1); // so a smaller than default width can be used by ComboboxToolbarController
+ SetSizePixel(get_preferred_size());
+}
+
+IMPL_LINK(ComboBoxControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
+{
+ return ChildKeyInput(rKEvt);
+}
+
+void ComboBoxControl::set_active_or_entry_text(const OUString& rText)
+{
+ const int nFound = m_xWidget->find_text(rText);
+ if (nFound != -1)
+ m_xWidget->set_active(nFound);
+ else
+ m_xWidget->set_entry_text(rText);
+}
+
+ComboBoxControl::~ComboBoxControl()
+{
+ disposeOnce();
+}
+
+void ComboBoxControl::dispose()
+{
+ m_pComboboxToolbarController = nullptr;
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+}
+
+IMPL_LINK_NOARG(ComboBoxControl, ModifyHdl, weld::ComboBox&, void)
+{
+ if (m_pComboboxToolbarController)
+ {
+ if (m_xWidget->get_count() && m_xWidget->changed_by_direct_pick())
+ m_pComboboxToolbarController->Select();
+ else
+ m_pComboboxToolbarController->Modify();
+ }
+}
+
+IMPL_LINK_NOARG(ComboBoxControl, FocusInHdl, weld::Widget&, void)
+{
+ if (m_pComboboxToolbarController)
+ m_pComboboxToolbarController->GetFocus();
+}
+
+IMPL_LINK_NOARG(ComboBoxControl, FocusOutHdl, weld::Widget&, void)
+{
+ if (m_pComboboxToolbarController)
+ m_pComboboxToolbarController->LoseFocus();
+}
+
+IMPL_LINK_NOARG(ComboBoxControl, ActivateHdl, weld::ComboBox&, bool)
+{
+ if (m_pComboboxToolbarController)
+ m_pComboboxToolbarController->Activate();
+ return true;
+}
+
+ComboboxToolbarController::ComboboxToolbarController(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ ToolBox* pToolbar,
+ ToolBoxItemId nID,
+ sal_Int32 nWidth,
+ const OUString& aCommand ) :
+ ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand )
+ , m_pComboBox( nullptr )
+{
+ m_pComboBox = VclPtr<ComboBoxControl>::Create(m_xToolbar, this);
+ if ( nWidth == 0 )
+ nWidth = 100;
+
+ // ComboBoxControl ctor has set a suitable height already
+ auto nHeight = m_pComboBox->GetSizePixel().Height();
+
+ m_pComboBox->SetSizePixel( ::Size( nWidth, nHeight ));
+ m_xToolbar->SetItemWindow( m_nID, m_pComboBox );
+}
+
+ComboboxToolbarController::~ComboboxToolbarController()
+{
+}
+
+void SAL_CALL ComboboxToolbarController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ m_xToolbar->SetItemWindow( m_nID, nullptr );
+ m_pComboBox.disposeAndClear();
+
+ ComplexToolbarController::dispose();
+}
+
+Sequence<PropertyValue> ComboboxToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const
+{
+ OUString aSelectedText = m_pComboBox->get_active_text();
+
+ // Add key modifier to argument list
+ Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier),
+ comphelper::makePropertyValue("Text", aSelectedText) };
+ return aArgs;
+}
+
+void ComboboxToolbarController::Select()
+{
+ vcl::Window::PointerState aState = m_pComboBox->GetPointerState();
+
+ sal_uInt16 nKeyModifier = sal_uInt16( aState.mnState & KEY_MODIFIERS_MASK );
+ execute( nKeyModifier );
+}
+
+void ComboboxToolbarController::Modify()
+{
+ notifyTextChanged(m_pComboBox->get_active_text());
+}
+
+void ComboboxToolbarController::GetFocus()
+{
+ notifyFocusGet();
+}
+
+void ComboboxToolbarController::LoseFocus()
+{
+ notifyFocusLost();
+}
+
+void ComboboxToolbarController::Activate()
+{
+ // Call execute only with non-empty text
+ if (!m_pComboBox->get_active_text().isEmpty())
+ execute(0);
+}
+
+void ComboboxToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand )
+{
+ if ( rControlCommand.Command == "SetText" )
+ {
+ for ( const NamedValue& rArg : rControlCommand.Arguments )
+ {
+ if ( rArg.Name == "Text" )
+ {
+ OUString aText;
+ rArg.Value >>= aText;
+ m_pComboBox->set_active_or_entry_text(aText);
+
+ // send notification
+ notifyTextChanged( aText );
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "SetList" )
+ {
+ for ( const NamedValue& rArg : rControlCommand.Arguments )
+ {
+ if ( rArg.Name == "List" )
+ {
+ Sequence< OUString > aList;
+ m_pComboBox->clear();
+
+ rArg.Value >>= aList;
+ for (OUString const & rName : std::as_const(aList))
+ m_pComboBox->append_text(rName);
+
+ // send notification
+ uno::Sequence< beans::NamedValue > aInfo { { "List", css::uno::Any(aList) } };
+ addNotifyInfo( "ListChanged",
+ getDispatchFromCommand( m_aCommandURL ),
+ aInfo );
+
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "AddEntry" )
+ {
+ OUString aText;
+ for ( const NamedValue& rArg : rControlCommand.Arguments )
+ {
+ if ( rArg.Name == "Text" )
+ {
+ if ( rArg.Value >>= aText )
+ m_pComboBox->append_text(aText);
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "InsertEntry" )
+ {
+ sal_Int32 nPos(-1);
+ OUString aText;
+ for ( const NamedValue& rArg : rControlCommand.Arguments )
+ {
+ if ( rArg.Name == "Pos" )
+ {
+ sal_Int32 nTmpPos = 0;
+ if ( rArg.Value >>= nTmpPos )
+ {
+ if (( nTmpPos >= 0 ) &&
+ ( nTmpPos < m_pComboBox->get_count() ))
+ nPos = nTmpPos;
+ }
+ }
+ else if ( rArg.Name == "Text" )
+ rArg.Value >>= aText;
+ }
+
+ m_pComboBox->insert_text(nPos, aText);
+ }
+ else if ( rControlCommand.Command == "RemoveEntryPos" )
+ {
+ for ( const NamedValue& rArg : rControlCommand.Arguments )
+ {
+ if ( rArg.Name == "Pos" )
+ {
+ sal_Int32 nPos( -1 );
+ if ( rArg.Value >>= nPos )
+ {
+ if (0 <= nPos && nPos < m_pComboBox->get_count())
+ m_pComboBox->remove(nPos);
+ }
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "RemoveEntryText" )
+ {
+ for ( const NamedValue& rArg : rControlCommand.Arguments )
+ {
+ if ( rArg.Name == "Text")
+ {
+ OUString aText;
+ if ( rArg.Value >>= aText )
+ {
+ auto nPos = m_pComboBox->find_text(aText);
+ if (nPos != -1)
+ m_pComboBox->remove(nPos);
+ }
+ break;
+ }
+ }
+ }
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/complextoolbarcontroller.cxx b/framework/source/uielement/complextoolbarcontroller.cxx
new file mode 100644
index 000000000..c3f9f191d
--- /dev/null
+++ b/framework/source/uielement/complextoolbarcontroller.cxx
@@ -0,0 +1,326 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/complextoolbarcontroller.hxx>
+
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/frame/status/ItemStatus.hpp>
+#include <com/sun/star/frame/status/Visibility.hpp>
+#include <com/sun/star/frame/XControlNotificationListener.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <svtools/toolboxcontroller.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/mnemonic.hxx>
+#include <vcl/toolbox.hxx>
+
+using namespace ::com::sun::star;
+using namespace css::awt;
+using namespace css::uno;
+using namespace css::beans;
+using namespace css::lang;
+using namespace css::frame;
+using namespace css::frame::status;
+using namespace css::util;
+
+namespace framework
+{
+
+ComplexToolbarController::ComplexToolbarController(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ ToolBox* pToolbar,
+ ToolBoxItemId nID,
+ const OUString& aCommand ) :
+ svt::ToolboxController( rxContext, rFrame, aCommand )
+ , m_xToolbar( pToolbar )
+ , m_nID( nID )
+ , m_bMadeInvisible( false )
+{
+ m_xURLTransformer.set( URLTransformer::create(m_xContext) );
+}
+
+ComplexToolbarController::~ComplexToolbarController()
+{
+}
+
+void SAL_CALL ComplexToolbarController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ m_xToolbar->SetItemWindow( m_nID, nullptr );
+ svt::ToolboxController::dispose();
+
+ m_xURLTransformer.clear();
+ m_xToolbar.clear();
+ m_nID = ToolBoxItemId(0);
+}
+
+Sequence<PropertyValue> ComplexToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const
+{
+ // Add key modifier to argument list
+ Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier) };
+ return aArgs;
+}
+
+void SAL_CALL ComplexToolbarController::execute( sal_Int16 KeyModifier )
+{
+ Reference< XDispatch > xDispatch;
+ Reference< XURLTransformer > xURLTransformer;
+ css::util::URL aTargetURL;
+ Sequence<PropertyValue> aArgs;
+
+ {
+ SolarMutexGuard aSolarMutexGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( m_bInitialized &&
+ m_xFrame.is() &&
+ !m_aCommandURL.isEmpty() )
+ {
+ xURLTransformer = m_xURLTransformer;
+ xDispatch = getDispatchFromCommand( m_aCommandURL );
+ aTargetURL = getInitializedURL();
+ aArgs = getExecuteArgs(KeyModifier);
+ }
+ }
+
+ if ( xDispatch.is() && !aTargetURL.Complete.isEmpty() )
+ {
+ // Execute dispatch asynchronously
+ ExecuteInfo* pExecuteInfo = new ExecuteInfo;
+ pExecuteInfo->xDispatch = xDispatch;
+ pExecuteInfo->aTargetURL = aTargetURL;
+ pExecuteInfo->aArgs = aArgs;
+ Application::PostUserEvent( LINK(nullptr, ComplexToolbarController , ExecuteHdl_Impl), pExecuteInfo );
+ }
+}
+
+void ComplexToolbarController::statusChanged( const FeatureStateEvent& Event )
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ if ( m_bDisposed )
+ return;
+
+ if ( !m_xToolbar )
+ return;
+
+ m_xToolbar->EnableItem( m_nID, Event.IsEnabled );
+
+ ToolBoxItemBits nItemBits = m_xToolbar->GetItemBits( m_nID );
+ nItemBits &= ~ToolBoxItemBits::CHECKABLE;
+ TriState eTri = TRISTATE_FALSE;
+
+ bool bValue;
+ OUString aStrValue;
+ ItemStatus aItemState;
+ Visibility aItemVisibility;
+ ControlCommand aControlCommand;
+
+ if ( Event.State >>= bValue )
+ {
+ // Boolean, treat it as checked/unchecked
+ if ( m_bMadeInvisible )
+ m_xToolbar->ShowItem( m_nID );
+ m_xToolbar->CheckItem( m_nID, bValue );
+ if ( bValue )
+ eTri = TRISTATE_TRUE;
+ nItemBits |= ToolBoxItemBits::CHECKABLE;
+ }
+ else if ( Event.State >>= aStrValue )
+ {
+ OUString aText( MnemonicGenerator::EraseAllMnemonicChars( aStrValue ) );
+ m_xToolbar->SetItemText( m_nID, aText );
+ m_xToolbar->SetQuickHelpText( m_nID, aText );
+
+ if ( m_bMadeInvisible )
+ m_xToolbar->ShowItem( m_nID );
+ }
+ else if ( Event.State >>= aItemState )
+ {
+ eTri = TRISTATE_INDET;
+ nItemBits |= ToolBoxItemBits::CHECKABLE;
+ if ( m_bMadeInvisible )
+ m_xToolbar->ShowItem( m_nID );
+ }
+ else if ( Event.State >>= aItemVisibility )
+ {
+ m_xToolbar->ShowItem( m_nID, aItemVisibility.bVisible );
+ m_bMadeInvisible = !aItemVisibility.bVisible;
+ }
+ else if ( Event.State >>= aControlCommand )
+ {
+ if (aControlCommand.Command == "SetQuickHelpText")
+ {
+ for (NamedValue const & rArg : std::as_const(aControlCommand.Arguments))
+ {
+ if (rArg.Name == "HelpText")
+ {
+ OUString aHelpText;
+ rArg.Value >>= aHelpText;
+ m_xToolbar->SetQuickHelpText(m_nID, aHelpText);
+ break;
+ }
+ }
+ }
+ else
+ {
+ executeControlCommand( aControlCommand );
+ }
+ if ( m_bMadeInvisible )
+ m_xToolbar->ShowItem( m_nID );
+ }
+
+ else if ( m_bMadeInvisible )
+ m_xToolbar->ShowItem( m_nID );
+
+ m_xToolbar->SetItemState( m_nID, eTri );
+ m_xToolbar->SetItemBits( m_nID, nItemBits );
+}
+
+IMPL_STATIC_LINK( ComplexToolbarController, ExecuteHdl_Impl, void*, p, void )
+{
+ ExecuteInfo* pExecuteInfo = static_cast<ExecuteInfo*>(p);
+ SolarMutexReleaser aReleaser;
+ try
+ {
+ // Asynchronous execution as this can lead to our own destruction!
+ // Framework can recycle our current frame and the layout manager disposes all user interface
+ // elements if a component gets detached from its frame!
+ pExecuteInfo->xDispatch->dispatch( pExecuteInfo->aTargetURL, pExecuteInfo->aArgs );
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ delete pExecuteInfo;
+}
+
+IMPL_STATIC_LINK( ComplexToolbarController, Notify_Impl, void*, p, void )
+{
+ NotifyInfo* pNotifyInfo = static_cast<NotifyInfo*>(p);
+ SolarMutexReleaser aReleaser;
+ try
+ {
+ // Asynchronous execution: As this can lead to our own destruction!
+ // Framework can recycle our current frame and the layout manager disposes all user interface
+ // elements if a component gets detached from its frame!
+ frame::ControlEvent aEvent;
+ aEvent.aURL = pNotifyInfo->aSourceURL;
+ aEvent.Event = pNotifyInfo->aEventName;
+ aEvent.aInformation = pNotifyInfo->aInfoSeq;
+ pNotifyInfo->xNotifyListener->controlEvent( aEvent );
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ delete pNotifyInfo;
+}
+
+void ComplexToolbarController::addNotifyInfo(
+ const OUString& aEventName,
+ const uno::Reference< frame::XDispatch >& xDispatch,
+ const uno::Sequence< beans::NamedValue >& rInfo )
+{
+ uno::Reference< frame::XControlNotificationListener > xControlNotify( xDispatch, uno::UNO_QUERY );
+
+ if ( !xControlNotify.is() )
+ return;
+
+ // Execute notification asynchronously
+ NotifyInfo* pNotifyInfo = new NotifyInfo;
+
+ pNotifyInfo->aEventName = aEventName;
+ pNotifyInfo->xNotifyListener = xControlNotify;
+ pNotifyInfo->aSourceURL = getInitializedURL();
+
+ // Add frame as source to the information sequence
+ sal_Int32 nCount = rInfo.getLength();
+ uno::Sequence< beans::NamedValue > aInfoSeq( rInfo );
+ aInfoSeq.realloc( nCount+1 );
+ auto pInfoSeq = aInfoSeq.getArray();
+ pInfoSeq[nCount].Name = "Source";
+ pInfoSeq[nCount].Value <<= getFrameInterface();
+ pNotifyInfo->aInfoSeq = aInfoSeq;
+
+ Application::PostUserEvent( LINK(nullptr, ComplexToolbarController, Notify_Impl), pNotifyInfo );
+}
+
+uno::Reference< frame::XDispatch > ComplexToolbarController::getDispatchFromCommand( const OUString& aCommand ) const
+{
+ uno::Reference< frame::XDispatch > xDispatch;
+
+ if ( m_bInitialized && m_xFrame.is() && !aCommand.isEmpty() )
+ {
+ URLToDispatchMap::const_iterator pIter = m_aListenerMap.find( aCommand );
+ if ( pIter != m_aListenerMap.end() )
+ xDispatch = pIter->second;
+ }
+
+ return xDispatch;
+}
+
+const css::util::URL& ComplexToolbarController::getInitializedURL()
+{
+ if ( m_aURL.Complete.isEmpty() )
+ {
+ m_aURL.Complete = m_aCommandURL;
+ m_xURLTransformer->parseStrict( m_aURL );
+ }
+ return m_aURL;
+}
+
+void ComplexToolbarController::notifyFocusGet()
+{
+ // send focus get notification
+ uno::Sequence< beans::NamedValue > aInfo;
+ addNotifyInfo( "FocusSet",
+ getDispatchFromCommand( m_aCommandURL ),
+ aInfo );
+}
+
+void ComplexToolbarController::notifyFocusLost()
+{
+ // send focus lost notification
+ uno::Sequence< beans::NamedValue > aInfo;
+ addNotifyInfo( "FocusLost",
+ getDispatchFromCommand( m_aCommandURL ),
+ aInfo );
+}
+
+void ComplexToolbarController::notifyTextChanged( const OUString& aText )
+{
+ // send text changed notification
+ uno::Sequence< beans::NamedValue > aInfo { { "Text", css::uno::Any(aText) } };
+ addNotifyInfo( "TextChanged",
+ getDispatchFromCommand( m_aCommandURL ),
+ aInfo );
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/controlmenucontroller.cxx b/framework/source/uielement/controlmenucontroller.cxx
new file mode 100644
index 000000000..2c31081bb
--- /dev/null
+++ b/framework/source/uielement/controlmenucontroller.cxx
@@ -0,0 +1,327 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/image.hxx>
+#include <svtools/popupmenucontrollerbase.hxx>
+#include <osl/mutex.hxx>
+#include <unordered_map>
+
+#include <classes/fwkresid.hxx>
+#include <bitmaps.hlst>
+#include <strings.hrc>
+
+static const char* aCommands[] =
+{
+ ".uno:ConvertToEdit",
+ ".uno:ConvertToButton",
+ ".uno:ConvertToFixed",
+ ".uno:ConvertToList",
+ ".uno:ConvertToCheckBox",
+ ".uno:ConvertToRadio",
+ ".uno:ConvertToGroup",
+ ".uno:ConvertToCombo",
+ ".uno:ConvertToImageBtn",
+ ".uno:ConvertToFileControl",
+ ".uno:ConvertToDate",
+ ".uno:ConvertToTime",
+ ".uno:ConvertToNumeric",
+ ".uno:ConvertToCurrency",
+ ".uno:ConvertToPattern",
+ ".uno:ConvertToImageControl",
+ ".uno:ConvertToFormatted",
+ ".uno:ConvertToScrollBar",
+ ".uno:ConvertToSpinButton",
+ ".uno:ConvertToNavigationBar"
+};
+
+static TranslateId aLabels[] =
+{
+ RID_STR_PROPTITLE_EDIT,
+ RID_STR_PROPTITLE_PUSHBUTTON,
+ RID_STR_PROPTITLE_FIXEDTEXT,
+ RID_STR_PROPTITLE_LISTBOX,
+ RID_STR_PROPTITLE_CHECKBOX,
+ RID_STR_PROPTITLE_RADIOBUTTON,
+ RID_STR_PROPTITLE_GROUPBOX,
+ RID_STR_PROPTITLE_COMBOBOX,
+ RID_STR_PROPTITLE_IMAGEBUTTON,
+ RID_STR_PROPTITLE_FILECONTROL,
+ RID_STR_PROPTITLE_DATEFIELD,
+ RID_STR_PROPTITLE_TIMEFIELD,
+ RID_STR_PROPTITLE_NUMERICFIELD,
+ RID_STR_PROPTITLE_CURRENCYFIELD,
+ RID_STR_PROPTITLE_PATTERNFIELD,
+ RID_STR_PROPTITLE_IMAGECONTROL,
+ RID_STR_PROPTITLE_FORMATTED,
+ RID_STR_PROPTITLE_SCROLLBAR,
+ RID_STR_PROPTITLE_SPINBUTTON,
+ RID_STR_PROPTITLE_NAVBAR
+};
+
+const rtl::OUStringConstExpr aImgIds[]
+{
+ RID_SVXBMP_EDITBOX,
+ RID_SVXBMP_BUTTON,
+ RID_SVXBMP_FIXEDTEXT,
+ RID_SVXBMP_LISTBOX,
+ RID_SVXBMP_CHECKBOX,
+ RID_SVXBMP_RADIOBUTTON,
+ RID_SVXBMP_GROUPBOX,
+ RID_SVXBMP_COMBOBOX,
+ RID_SVXBMP_IMAGEBUTTON,
+ RID_SVXBMP_FILECONTROL,
+ RID_SVXBMP_DATEFIELD,
+ RID_SVXBMP_TIMEFIELD,
+ RID_SVXBMP_NUMERICFIELD,
+ RID_SVXBMP_CURRENCYFIELD,
+ RID_SVXBMP_PATTERNFIELD,
+ RID_SVXBMP_IMAGECONTROL,
+ RID_SVXBMP_FORMATTEDFIELD,
+ RID_SVXBMP_SCROLLBAR,
+ RID_SVXBMP_SPINBUTTON,
+ RID_SVXBMP_NAVIGATIONBAR
+};
+
+using namespace css;
+using namespace css::uno;
+using namespace css::lang;
+using namespace css::frame;
+using namespace css::beans;
+
+namespace {
+
+class ControlMenuController : public svt::PopupMenuControllerBase
+{
+ using svt::PopupMenuControllerBase::disposing;
+
+public:
+ explicit ControlMenuController( const uno::Reference< uno::XComponentContext >& xContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.ControlMenuController";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.frame.PopupMenuController"};
+ }
+
+ // XPopupMenuController
+ virtual void SAL_CALL updatePopupMenu() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const uno::Sequence< uno::Any >& aArguments ) override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const frame::FeatureStateEvent& Event ) override;
+
+ // XMenuListener
+ virtual void SAL_CALL itemActivated( const awt::MenuEvent& rEvent ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const lang::EventObject& Source ) override;
+
+private:
+ class UrlToDispatchMap : public std::unordered_map< OUString,
+ uno::Reference< frame::XDispatch > >
+ {
+ public:
+ void free()
+ {
+ UrlToDispatchMap().swap( *this );// get rid of reserved capacity
+ }
+ };
+
+ void updateImagesPopupMenu(Reference<awt::XPopupMenu> const& rPopupMenu);
+ void fillPopupMenu(uno::Reference<awt::XPopupMenu> const& rPopupMenu);
+
+ bool m_bShowMenuImages : 1;
+ UrlToDispatchMap m_aURLToDispatchMap;
+};
+
+ControlMenuController::ControlMenuController(const css::uno::Reference< css::uno::XComponentContext >& xContext)
+ : svt::PopupMenuControllerBase(xContext)
+{
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ m_bShowMenuImages = rSettings.GetUseImagesInMenus();
+
+}
+
+// private function
+void ControlMenuController::updateImagesPopupMenu(Reference<awt::XPopupMenu> const& rPopupMenu)
+{
+ if (!rPopupMenu)
+ return;
+ for (size_t i=0; i < SAL_N_ELEMENTS(aCommands); ++i)
+ {
+ sal_Int16 nItemId = i + 1;
+ if (m_bShowMenuImages)
+ {
+ Image aImage(StockImage::Yes, OUString(aImgIds[i]));
+ Graphic aGraphic(aImage);
+ rPopupMenu->setItemImage(nItemId, aGraphic.GetXGraphic(), false);
+ }
+ else
+ rPopupMenu->setItemImage(nItemId, nullptr, false);
+ }
+}
+
+// private function
+void ControlMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ resetPopupMenu( rPopupMenu );
+
+ for (size_t i=0; i < SAL_N_ELEMENTS(aCommands); ++i)
+ {
+ sal_Int16 nItemId = i + 1;
+ OUString sCommand(OUString::createFromAscii(aCommands[i]));
+ rPopupMenu->insertItem(nItemId, FwkResId(aLabels[i]), 0, i);
+ rPopupMenu->setCommand(nItemId, sCommand);
+ rPopupMenu->enableItem(nItemId, false);
+ }
+
+ updateImagesPopupMenu(rPopupMenu);
+
+ rPopupMenu->hideDisabledEntries(true);
+}
+
+}
+
+// XEventListener
+void SAL_CALL ControlMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(this);
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) );
+ m_xPopupMenu.clear();
+}
+
+// XStatusListener
+void SAL_CALL ControlMenuController::statusChanged( const FeatureStateEvent& Event )
+{
+ osl::MutexGuard aLock( m_aMutex );
+
+ if (!m_xPopupMenu)
+ return;
+
+ sal_Int16 nItemId = 0;
+ for (size_t i=0; i < SAL_N_ELEMENTS(aCommands); ++i)
+ {
+ if ( Event.FeatureURL.Complete.equalsAscii( aCommands[i] ))
+ {
+ nItemId = i + 1;
+ break;
+ }
+ }
+
+ if (!nItemId)
+ return;
+
+ m_xPopupMenu->enableItem(nItemId, Event.IsEnabled);
+}
+
+// XMenuListener
+void SAL_CALL ControlMenuController::itemActivated( const css::awt::MenuEvent& )
+{
+ osl::MutexGuard aLock( m_aMutex );
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ // Check if some modes have changed so we have to update our menu images
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ bool bShowMenuImages = rSettings.GetUseImagesInMenus();
+
+ if (bShowMenuImages != m_bShowMenuImages)
+ {
+ m_bShowMenuImages = bShowMenuImages;
+ updateImagesPopupMenu(m_xPopupMenu);
+ }
+}
+
+// XPopupMenuController
+void SAL_CALL ControlMenuController::updatePopupMenu()
+{
+ osl::MutexGuard aLock( m_aMutex );
+
+ throwIfDisposed();
+
+ if ( !(m_xFrame.is() && m_xPopupMenu.is()) )
+ return;
+
+ css::util::URL aTargetURL;
+ Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
+ fillPopupMenu( m_xPopupMenu );
+ m_aURLToDispatchMap.free();
+
+ for (const char* aCommand : aCommands)
+ {
+ aTargetURL.Complete = OUString::createFromAscii( aCommand );
+ m_xURLTransformer->parseStrict( aTargetURL );
+
+ Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+ if ( xDispatch.is() )
+ {
+ xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
+ xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
+ m_aURLToDispatchMap.emplace( aTargetURL.Complete, xDispatch );
+ }
+ }
+}
+
+// XInitialization
+void SAL_CALL ControlMenuController::initialize( const Sequence< Any >& aArguments )
+{
+ osl::MutexGuard aLock( m_aMutex );
+ svt::PopupMenuControllerBase::initialize(aArguments);
+ m_aBaseURL.clear();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_ControlMenuController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ControlMenuController(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/dropdownboxtoolbarcontroller.cxx b/framework/source/uielement/dropdownboxtoolbarcontroller.cxx
new file mode 100644
index 000000000..b3478a7b1
--- /dev/null
+++ b/framework/source/uielement/dropdownboxtoolbarcontroller.cxx
@@ -0,0 +1,282 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/dropdownboxtoolbarcontroller.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <svtools/toolboxcontroller.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+
+using namespace ::com::sun::star;
+using namespace css::awt;
+using namespace css::uno;
+using namespace css::beans;
+using namespace css::lang;
+using namespace css::frame;
+using namespace css::util;
+
+namespace framework
+{
+
+// Wrapper class to notify controller about events from ListBox.
+// Unfortunaltly the events are notified through virtual methods instead
+// of Listeners.
+
+class ListBoxControl final : public InterimItemWindow
+{
+public:
+ ListBoxControl(vcl::Window* pParent, DropdownToolbarController* pListBoxListener);
+ virtual ~ListBoxControl() override;
+ virtual void dispose() override;
+
+ void set_active(int nPos) { m_xWidget->set_active(nPos); }
+ void append_text(const OUString& rStr) { m_xWidget->append_text(rStr); }
+ void insert_text(int nPos, const OUString& rStr) { m_xWidget->insert_text(nPos, rStr); }
+ int get_count() const { return m_xWidget->get_count(); }
+ int find_text(const OUString& rStr) const { return m_xWidget->find_text(rStr); }
+ OUString get_active_text() const { return m_xWidget->get_active_text(); }
+ void clear() { return m_xWidget->clear(); }
+ void remove(int nPos) { m_xWidget->remove(nPos); }
+
+ DECL_LINK(FocusInHdl, weld::Widget&, void);
+ DECL_LINK(FocusOutHdl, weld::Widget&, void);
+ DECL_LINK(ModifyHdl, weld::ComboBox&, void);
+ DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool);
+
+private:
+ std::unique_ptr<weld::ComboBox> m_xWidget;
+ DropdownToolbarController* m_pListBoxListener;
+};
+
+ListBoxControl::ListBoxControl(vcl::Window* pParent, DropdownToolbarController* pListBoxListener)
+ : InterimItemWindow(pParent, "svt/ui/listcontrol.ui", "ListControl")
+ , m_xWidget(m_xBuilder->weld_combo_box("listbox"))
+ , m_pListBoxListener( pListBoxListener )
+{
+ InitControlBase(m_xWidget.get());
+
+ m_xWidget->connect_focus_in(LINK(this, ListBoxControl, FocusInHdl));
+ m_xWidget->connect_focus_out(LINK(this, ListBoxControl, FocusOutHdl));
+ m_xWidget->connect_changed(LINK(this, ListBoxControl, ModifyHdl));
+ m_xWidget->connect_key_press(LINK(this, ListBoxControl, KeyInputHdl));
+
+ m_xWidget->set_size_request(42, -1); // so a later narrow size request can stick
+ SetSizePixel(get_preferred_size());
+}
+
+IMPL_LINK(ListBoxControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
+{
+ return ChildKeyInput(rKEvt);
+}
+
+ListBoxControl::~ListBoxControl()
+{
+ disposeOnce();
+}
+
+void ListBoxControl::dispose()
+{
+ m_pListBoxListener = nullptr;
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+}
+
+IMPL_LINK_NOARG(ListBoxControl, ModifyHdl, weld::ComboBox&, void)
+{
+ if (m_pListBoxListener)
+ m_pListBoxListener->Select();
+}
+
+IMPL_LINK_NOARG(ListBoxControl, FocusInHdl, weld::Widget&, void)
+{
+ if (m_pListBoxListener)
+ m_pListBoxListener->GetFocus();
+}
+
+IMPL_LINK_NOARG(ListBoxControl, FocusOutHdl, weld::Widget&, void)
+{
+ if (m_pListBoxListener)
+ m_pListBoxListener->LoseFocus();
+}
+
+DropdownToolbarController::DropdownToolbarController(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ ToolBox* pToolbar,
+ ToolBoxItemId nID,
+ sal_Int32 nWidth,
+ const OUString& aCommand ) :
+ ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand )
+ , m_pListBoxControl( nullptr )
+{
+ m_pListBoxControl = VclPtr<ListBoxControl>::Create(m_xToolbar, this);
+ if ( nWidth == 0 )
+ nWidth = 100;
+
+ // ListBoxControl ctor has set a suitable height already
+ auto nHeight = m_pListBoxControl->GetSizePixel().Height();
+
+ m_pListBoxControl->SetSizePixel( ::Size( nWidth, nHeight ));
+ m_xToolbar->SetItemWindow( m_nID, m_pListBoxControl );
+}
+
+DropdownToolbarController::~DropdownToolbarController()
+{
+}
+
+void SAL_CALL DropdownToolbarController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ m_xToolbar->SetItemWindow( m_nID, nullptr );
+ m_pListBoxControl.disposeAndClear();
+
+ ComplexToolbarController::dispose();
+}
+
+Sequence<PropertyValue> DropdownToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const
+{
+ OUString aSelectedText = m_pListBoxControl->get_active_text();
+
+ // Add key modifier to argument list
+ Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier),
+ comphelper::makePropertyValue("Text", aSelectedText) };
+ return aArgs;
+}
+
+void DropdownToolbarController::Select()
+{
+ if (m_pListBoxControl->get_count() > 0)
+ execute(0);
+}
+
+void DropdownToolbarController::GetFocus()
+{
+ notifyFocusGet();
+}
+
+void DropdownToolbarController::LoseFocus()
+{
+ notifyFocusLost();
+}
+
+void DropdownToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand )
+{
+ if ( rControlCommand.Command == "SetList" )
+ {
+ for ( const NamedValue& rArg : rControlCommand.Arguments )
+ {
+ if ( rArg.Name == "List" )
+ {
+ Sequence< OUString > aList;
+ m_pListBoxControl->clear();
+
+ rArg.Value >>= aList;
+ for (OUString const & rName : std::as_const(aList))
+ m_pListBoxControl->append_text(rName);
+
+ m_pListBoxControl->set_active(0);
+
+ // send notification
+ uno::Sequence< beans::NamedValue > aInfo { { "List", css::uno::Any(aList) } };
+ addNotifyInfo( "ListChanged",
+ getDispatchFromCommand( m_aCommandURL ),
+ aInfo );
+
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "AddEntry" )
+ {
+ OUString aText;
+ for ( const NamedValue& rArg : rControlCommand.Arguments )
+ {
+ if ( rArg.Name == "Text" )
+ {
+ if ( rArg.Value >>= aText )
+ m_pListBoxControl->append_text(aText);
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "InsertEntry" )
+ {
+ sal_Int32 nPos(-1);
+ OUString aText;
+ for ( const NamedValue& rArg : rControlCommand.Arguments )
+ {
+ if ( rArg.Name == "Pos" )
+ {
+ sal_Int32 nTmpPos = 0;
+ if ( rArg.Value >>= nTmpPos )
+ {
+ if (( nTmpPos >= 0 ) &&
+ ( nTmpPos < m_pListBoxControl->get_count() ))
+ nPos = nTmpPos;
+ }
+ }
+ else if ( rArg.Name == "Text" )
+ rArg.Value >>= aText;
+ }
+
+ m_pListBoxControl->insert_text(nPos, aText);
+ }
+ else if ( rControlCommand.Command == "RemoveEntryPos" )
+ {
+ for ( const NamedValue& rArg : rControlCommand.Arguments )
+ {
+ if ( rArg.Name == "Pos" )
+ {
+ sal_Int32 nPos( -1 );
+ if ( rArg.Value >>= nPos )
+ {
+ if ( 0 <= nPos && nPos < m_pListBoxControl->get_count() )
+ m_pListBoxControl->remove(nPos);
+ }
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "RemoveEntryText" )
+ {
+ for ( const NamedValue& rArg : rControlCommand.Arguments )
+ {
+ if ( rArg.Name == "Text" )
+ {
+ OUString aText;
+ if ( rArg.Value >>= aText )
+ {
+ auto nPos = m_pListBoxControl->find_text(aText);
+ if (nPos != -1)
+ m_pListBoxControl->remove(nPos);
+ }
+ break;
+ }
+ }
+ }
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/edittoolbarcontroller.cxx b/framework/source/uielement/edittoolbarcontroller.cxx
new file mode 100644
index 000000000..1f974e77c
--- /dev/null
+++ b/framework/source/uielement/edittoolbarcontroller.cxx
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/edittoolbarcontroller.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <svtools/toolboxcontroller.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/event.hxx>
+
+using namespace ::com::sun::star;
+using namespace css::uno;
+using namespace css::beans;
+using namespace css::lang;
+using namespace css::frame;
+using namespace css::util;
+
+namespace framework
+{
+
+// Wrapper class to notify controller about events from edit.
+// Unfortunaltly the events are notified through virtual methods instead
+// of Listeners.
+
+class EditControl final : public InterimItemWindow
+{
+public:
+ EditControl(vcl::Window* pParent, EditToolbarController* pEditToolbarController);
+ virtual ~EditControl() override;
+ virtual void dispose() override;
+
+ OUString get_text() const { return m_xWidget->get_text(); }
+ void set_text(const OUString& rText) { m_xWidget->set_text(rText); }
+
+private:
+ std::unique_ptr<weld::Entry> m_xWidget;
+ EditToolbarController* m_pEditToolbarController;
+
+ DECL_LINK(FocusInHdl, weld::Widget&, void);
+ DECL_LINK(FocusOutHdl, weld::Widget&, void);
+ DECL_LINK(ModifyHdl, weld::Entry&, void);
+ DECL_LINK(ActivateHdl, weld::Entry&, bool);
+ DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool);
+};
+
+EditControl::EditControl(vcl::Window* pParent, EditToolbarController* pEditToolbarController)
+ : InterimItemWindow(pParent, "svt/ui/editcontrol.ui", "EditControl")
+ , m_xWidget(m_xBuilder->weld_entry("entry"))
+ , m_pEditToolbarController(pEditToolbarController)
+{
+ OString sEmpty;
+ m_xWidget->set_help_id(sEmpty);
+ m_xContainer->set_help_id(sEmpty);
+
+ InitControlBase(m_xWidget.get());
+
+ m_xWidget->connect_focus_in(LINK(this, EditControl, FocusInHdl));
+ m_xWidget->connect_focus_out(LINK(this, EditControl, FocusOutHdl));
+ m_xWidget->connect_changed(LINK(this, EditControl, ModifyHdl));
+ m_xWidget->connect_activate(LINK(this, EditControl, ActivateHdl));
+ m_xWidget->connect_key_press(LINK(this, EditControl, KeyInputHdl));
+
+ SetSizePixel(get_preferred_size());
+}
+
+IMPL_LINK(EditControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
+{
+ return ChildKeyInput(rKEvt);
+}
+
+EditControl::~EditControl()
+{
+ disposeOnce();
+}
+
+void EditControl::dispose()
+{
+ m_pEditToolbarController = nullptr;
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+}
+
+IMPL_LINK_NOARG(EditControl, ModifyHdl, weld::Entry&, void)
+{
+ if (m_pEditToolbarController)
+ m_pEditToolbarController->Modify();
+}
+
+IMPL_LINK_NOARG(EditControl, FocusInHdl, weld::Widget&, void)
+{
+ if (m_pEditToolbarController)
+ m_pEditToolbarController->GetFocus();
+}
+
+IMPL_LINK_NOARG(EditControl, FocusOutHdl, weld::Widget&, void)
+{
+ if ( m_pEditToolbarController )
+ m_pEditToolbarController->LoseFocus();
+}
+
+IMPL_LINK_NOARG(EditControl, ActivateHdl, weld::Entry&, bool)
+{
+ if (m_pEditToolbarController)
+ m_pEditToolbarController->Activate();
+ return true;
+}
+
+EditToolbarController::EditToolbarController(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ ToolBox* pToolbar,
+ ToolBoxItemId nID,
+ sal_Int32 nWidth,
+ const OUString& aCommand ) :
+ ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand )
+ , m_pEditControl( nullptr )
+{
+ m_pEditControl = VclPtr<EditControl>::Create(m_xToolbar, this);
+ if ( nWidth == 0 )
+ nWidth = 100;
+
+ // EditControl ctor has set a suitable height already
+ auto nHeight = m_pEditControl->GetSizePixel().Height();
+
+ m_pEditControl->SetSizePixel( ::Size( nWidth, nHeight ));
+ m_xToolbar->SetItemWindow( m_nID, m_pEditControl );
+}
+
+EditToolbarController::~EditToolbarController()
+{
+}
+
+void SAL_CALL EditToolbarController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ m_xToolbar->SetItemWindow( m_nID, nullptr );
+ m_pEditControl.disposeAndClear();
+
+ ComplexToolbarController::dispose();
+}
+
+Sequence<PropertyValue> EditToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const
+{
+ OUString aSelectedText = m_pEditControl->get_text();
+
+ // Add key modifier to argument list
+ Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier),
+ comphelper::makePropertyValue("Text", aSelectedText) };
+ return aArgs;
+}
+
+void EditToolbarController::Modify()
+{
+ notifyTextChanged(m_pEditControl->get_text());
+}
+
+void EditToolbarController::GetFocus()
+{
+ notifyFocusGet();
+}
+
+void EditToolbarController::LoseFocus()
+{
+ notifyFocusLost();
+}
+
+void EditToolbarController::Activate()
+{
+ // Call execute only with non-empty text
+ if (!m_pEditControl->get_text().isEmpty())
+ execute(0);
+}
+
+void EditToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand )
+{
+ if ( !rControlCommand.Command.startsWith( "SetText" ))
+ return;
+
+ for ( const NamedValue& rArg : rControlCommand.Arguments )
+ {
+ if ( rArg.Name.startsWith( "Text" ))
+ {
+ OUString aText;
+ rArg.Value >>= aText;
+ m_pEditControl->set_text(aText);
+
+ // send notification
+ notifyTextChanged( aText );
+ break;
+ }
+ }
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/fontmenucontroller.cxx b/framework/source/uielement/fontmenucontroller.cxx
new file mode 100644
index 000000000..c0dd19e2e
--- /dev/null
+++ b/framework/source/uielement/fontmenucontroller.cxx
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/fontmenucontroller.hxx>
+
+#include <services.h>
+
+#include <com/sun/star/awt/MenuItemStyle.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/i18nhelp.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/mnemonic.hxx>
+#include <osl/mutex.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+// Defines
+
+using namespace css::uno;
+using namespace css::lang;
+using namespace css::frame;
+using namespace css::beans;
+using namespace css::util;
+
+static bool lcl_I18nCompareString(const OUString& rStr1, const OUString& rStr2)
+{
+ const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
+ return rI18nHelper.CompareString( rStr1, rStr2 ) < 0;
+}
+
+namespace framework
+{
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL FontMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.FontMenuController";
+}
+
+sal_Bool SAL_CALL FontMenuController::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL FontMenuController::getSupportedServiceNames()
+{
+ return { SERVICENAME_POPUPMENUCONTROLLER };
+}
+
+FontMenuController::FontMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
+ svt::PopupMenuControllerBase( xContext )
+{
+}
+
+FontMenuController::~FontMenuController()
+{
+}
+
+// private function
+void FontMenuController::fillPopupMenu( const Sequence< OUString >& rFontNameSeq, Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu( rPopupMenu );
+
+ std::vector<OUString> aVector;
+ aVector.reserve(rFontNameSeq.getLength());
+ for ( OUString const & s : rFontNameSeq )
+ {
+ aVector.push_back(MnemonicGenerator::EraseAllMnemonicChars(s));
+ }
+ sort(aVector.begin(), aVector.end(), lcl_I18nCompareString );
+
+ static const OUStringLiteral aFontNameCommandPrefix( u".uno:CharFontName?CharFontName.FamilyName:string=" );
+ const sal_Int16 nCount = static_cast<sal_Int16>(aVector.size());
+ for ( sal_Int16 i = 0; i < nCount; i++ )
+ {
+ const OUString& rName = aVector[i];
+ m_xPopupMenu->insertItem( i+1, rName, css::awt::MenuItemStyle::RADIOCHECK | css::awt::MenuItemStyle::AUTOCHECK, i );
+ if ( rName == m_aFontFamilyName )
+ m_xPopupMenu->checkItem( i+1, true );
+ OUString aFontNameCommand = aFontNameCommandPrefix + INetURLObject::encode( rName, INetURLObject::PART_HTTP_QUERY, INetURLObject::EncodeMechanism::All );
+ m_xPopupMenu->setCommand(i + 1, aFontNameCommand); // Store font name into item command.
+ }
+}
+
+// XEventListener
+void SAL_CALL FontMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(this);
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+ m_xFontListDispatch.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) );
+ m_xPopupMenu.clear();
+}
+
+// XStatusListener
+void SAL_CALL FontMenuController::statusChanged( const FeatureStateEvent& Event )
+{
+ css::awt::FontDescriptor aFontDescriptor;
+ Sequence< OUString > aFontNameSeq;
+
+ if ( Event.State >>= aFontDescriptor )
+ {
+ osl::MutexGuard aLock( m_aMutex );
+ m_aFontFamilyName = aFontDescriptor.Name;
+ }
+ else if ( Event.State >>= aFontNameSeq )
+ {
+ osl::MutexGuard aLock( m_aMutex );
+ if ( m_xPopupMenu.is() )
+ fillPopupMenu( aFontNameSeq, m_xPopupMenu );
+ }
+}
+
+// XMenuListener
+void SAL_CALL FontMenuController::itemActivated( const css::awt::MenuEvent& )
+{
+ osl::MutexGuard aLock( m_aMutex );
+
+ if ( !m_xPopupMenu.is() )
+ return;
+
+ // find new font name and set check mark!
+ sal_uInt16 nChecked = 0;
+ sal_uInt16 nItemCount = m_xPopupMenu->getItemCount();
+ for( sal_uInt16 i = 0; i < nItemCount; i++ )
+ {
+ sal_uInt16 nItemId = m_xPopupMenu->getItemId( i );
+
+ if ( m_xPopupMenu->isItemChecked( nItemId ) )
+ nChecked = nItemId;
+
+ OUString aText = m_xPopupMenu->getItemText( nItemId );
+
+ // TODO: must be replaced by implementation of VCL, when available
+ sal_Int32 nIndex = aText.indexOf( '~' );
+ if ( nIndex >= 0 )
+ aText = aText.replaceAt( nIndex, 1, u"" );
+ // TODO: must be replaced by implementation of VCL, when available
+
+ if ( aText == m_aFontFamilyName )
+ {
+ m_xPopupMenu->checkItem( nItemId, true );
+ return;
+ }
+ }
+
+ if ( nChecked )
+ m_xPopupMenu->checkItem( nChecked, false );
+}
+
+// XPopupMenuController
+void FontMenuController::impl_setPopupMenu()
+{
+ Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
+
+ css::util::URL aTargetURL;
+ // Register for font list updates to get the current font list from the controller
+ aTargetURL.Complete = ".uno:FontNameList";
+ m_xURLTransformer->parseStrict( aTargetURL );
+ m_xFontListDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+}
+
+void SAL_CALL FontMenuController::updatePopupMenu()
+{
+ svt::PopupMenuControllerBase::updatePopupMenu();
+
+ osl::ClearableMutexGuard aLock( m_aMutex );
+ Reference< XDispatch > xDispatch( m_xFontListDispatch );
+ css::util::URL aTargetURL;
+ aTargetURL.Complete = ".uno:FontNameList";
+ m_xURLTransformer->parseStrict( aTargetURL );
+ aLock.clear();
+
+ if ( xDispatch.is() )
+ {
+ xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
+ xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
+ }
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_FontMenuController_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::FontMenuController(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/fontsizemenucontroller.cxx b/framework/source/uielement/fontsizemenucontroller.cxx
new file mode 100644
index 000000000..4c5c68688
--- /dev/null
+++ b/framework/source/uielement/fontsizemenucontroller.cxx
@@ -0,0 +1,285 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <uielement/fontsizemenucontroller.hxx>
+
+#include <services.h>
+
+#include <com/sun/star/awt/MenuItemStyle.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/view/XPrintable.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+#include <vcl/svapp.hxx>
+#include <vcl/i18nhelp.hxx>
+#include <vcl/print.hxx>
+#include <vcl/settings.hxx>
+#include <svtools/ctrltool.hxx>
+#include <osl/mutex.hxx>
+#include <memory>
+#include <cppuhelper/supportsservice.hxx>
+
+// Defines
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+using namespace com::sun::star::view;
+
+namespace framework
+{
+
+OUString SAL_CALL FontSizeMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.FontSizeMenuController";
+}
+
+sal_Bool SAL_CALL FontSizeMenuController::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL FontSizeMenuController::getSupportedServiceNames()
+{
+ return { SERVICENAME_POPUPMENUCONTROLLER };
+}
+
+FontSizeMenuController::FontSizeMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
+ svt::PopupMenuControllerBase( xContext )
+{
+}
+
+FontSizeMenuController::~FontSizeMenuController()
+{
+}
+
+// private function
+OUString FontSizeMenuController::retrievePrinterName( css::uno::Reference< css::frame::XFrame > const & rFrame )
+{
+ OUString aPrinterName;
+
+ if ( rFrame.is() )
+ {
+ Reference< XController > xController = m_xFrame->getController();
+ if ( xController.is() )
+ {
+ Reference< XPrintable > xPrintable( xController->getModel(), UNO_QUERY );
+ if ( xPrintable.is() )
+ {
+ const Sequence< PropertyValue > aPrinterSeq = xPrintable->getPrinter();
+ for ( PropertyValue const & prop : aPrinterSeq )
+ {
+ if ( prop.Name == "Name" )
+ {
+ prop.Value >>= aPrinterName;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return aPrinterName;
+}
+
+// private function
+void FontSizeMenuController::setCurHeight( tools::Long nHeight, Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ // check menu item
+ sal_uInt16 nChecked = 0;
+ sal_uInt16 nItemCount = rPopupMenu->getItemCount();
+ for( sal_uInt16 i = 0; i < nItemCount; i++ )
+ {
+ sal_uInt16 nItemId = rPopupMenu->getItemId( i );
+
+ if ( m_aHeightArray[i] == nHeight )
+ {
+ rPopupMenu->checkItem( nItemId, true );
+ return;
+ }
+
+ if ( rPopupMenu->isItemChecked( nItemId ) )
+ nChecked = nItemId;
+ }
+
+ if ( nChecked )
+ rPopupMenu->checkItem( nChecked, false );
+}
+
+// private function
+void FontSizeMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ resetPopupMenu( rPopupMenu );
+
+ std::unique_ptr<FontList> pFontList;
+ ScopedVclPtr<Printer> pInfoPrinter;
+ OUString aPrinterName;
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ // try to retrieve printer name of document
+ aPrinterName = retrievePrinterName( m_xFrame );
+ if ( !aPrinterName.isEmpty() )
+ {
+ pInfoPrinter.disposeAndReset(VclPtr<Printer>::Create( aPrinterName ));
+ if ( pInfoPrinter && pInfoPrinter->GetFontFaceCollectionCount() > 0 )
+ pFontList.reset(new FontList( pInfoPrinter.get() ));
+ }
+
+ if ( !pFontList )
+ pFontList.reset(new FontList( Application::GetDefaultDevice() ));
+
+ // setup font size array
+ m_aHeightArray.clear();
+
+ sal_uInt16 nPos = 0; // Id is nPos+1
+ static const OUStringLiteral aFontHeightCommand( u".uno:FontHeight?FontHeight.Height:float=" );
+
+ // first insert font size names (for simplified/traditional chinese)
+ FontSizeNames aFontSizeNames( Application::GetSettings().GetUILanguageTag().getLanguageType() );
+ OUString aCommand;
+
+ if (!aFontSizeNames.IsEmpty())
+ {
+ // for scalable fonts all font size names
+ sal_Int32 nCount = aFontSizeNames.Count();
+ for( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ OUString aSizeName = aFontSizeNames.GetIndexName( i );
+ sal_Int32 nSize = aFontSizeNames.GetIndexSize( i );
+ m_aHeightArray.push_back(nSize);
+ rPopupMenu->insertItem(nPos + 1, aSizeName, css::awt::MenuItemStyle::RADIOCHECK | css::awt::MenuItemStyle::AUTOCHECK, nPos);
+
+ // Create dispatchable .uno command and set it
+ float fPoint = float(nSize) / 10;
+ aCommand = aFontHeightCommand + OUString::number( fPoint );
+ rPopupMenu->setCommand(nPos + 1, aCommand);
+
+ ++nPos;
+ }
+ }
+
+ // then insert numerical font size values
+ const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
+ const int* pAry = FontList::GetStdSizeAry();
+ const int* pTempAry = pAry;
+ while ( *pTempAry )
+ {
+ m_aHeightArray.push_back(*pTempAry);
+ rPopupMenu->insertItem(nPos + 1, rI18nHelper.GetNum(*pTempAry, 1, true, false),
+ css::awt::MenuItemStyle::RADIOCHECK | css::awt::MenuItemStyle::AUTOCHECK, nPos);
+
+ // Create dispatchable .uno command and set it
+ float fPoint = float(*pTempAry) / 10;
+ aCommand = aFontHeightCommand + OUString::number( fPoint );
+ rPopupMenu->setCommand(nPos + 1, aCommand);
+
+ ++nPos;
+ pTempAry++;
+ }
+
+ setCurHeight( tools::Long( m_aFontHeight.Height * 10), rPopupMenu );
+}
+
+// XEventListener
+void SAL_CALL FontSizeMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(this);
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+ m_xCurrentFontDispatch.clear();
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) );
+ m_xPopupMenu.clear();
+}
+
+// XStatusListener
+void SAL_CALL FontSizeMenuController::statusChanged( const FeatureStateEvent& Event )
+{
+ css::awt::FontDescriptor aFontDescriptor;
+ css::frame::status::FontHeight aFontHeight;
+
+ if ( Event.State >>= aFontDescriptor )
+ {
+ osl::MutexGuard aLock( m_aMutex );
+
+ if ( m_xPopupMenu.is() )
+ fillPopupMenu( m_xPopupMenu );
+ }
+ else if ( Event.State >>= aFontHeight )
+ {
+ osl::MutexGuard aLock( m_aMutex );
+ m_aFontHeight = aFontHeight;
+
+ if ( m_xPopupMenu.is() )
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ setCurHeight( tools::Long( m_aFontHeight.Height * 10), m_xPopupMenu );
+ }
+ }
+}
+
+// XPopupMenuController
+void FontSizeMenuController::impl_setPopupMenu()
+{
+ Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
+ css::util::URL aTargetURL;
+ // Register for font name updates which gives us info about the current font!
+ aTargetURL.Complete = ".uno:CharFontName";
+ m_xURLTransformer->parseStrict( aTargetURL );
+ m_xCurrentFontDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+}
+
+void SAL_CALL FontSizeMenuController::updatePopupMenu()
+{
+ osl::ClearableMutexGuard aLock( m_aMutex );
+
+ throwIfDisposed();
+
+ Reference< XDispatch > xDispatch( m_xCurrentFontDispatch );
+ css::util::URL aTargetURL;
+ aTargetURL.Complete = ".uno:CharFontName";
+ m_xURLTransformer->parseStrict( aTargetURL );
+ aLock.clear();
+
+ if ( xDispatch.is() )
+ {
+ xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
+ xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
+ }
+
+ svt::PopupMenuControllerBase::updatePopupMenu();
+}
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_FontSizeMenuController_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::FontSizeMenuController(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/footermenucontroller.cxx b/framework/source/uielement/footermenucontroller.cxx
new file mode 100644
index 000000000..9a941a825
--- /dev/null
+++ b/framework/source/uielement/footermenucontroller.cxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/footermenucontroller.hxx>
+
+#include <services.h>
+
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+// Defines
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+using namespace com::sun::star::style;
+using namespace com::sun::star::container;
+
+namespace framework
+{
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL FooterMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.FooterMenuController";
+}
+
+sal_Bool SAL_CALL FooterMenuController::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL FooterMenuController::getSupportedServiceNames()
+{
+ return { SERVICENAME_POPUPMENUCONTROLLER };
+}
+
+FooterMenuController::FooterMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
+ HeaderMenuController( xContext,true )
+{
+}
+
+FooterMenuController::~FooterMenuController()
+{
+}
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_FooterMenuController_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::FooterMenuController(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/genericstatusbarcontroller.cxx b/framework/source/uielement/genericstatusbarcontroller.cxx
new file mode 100644
index 000000000..4a5aa4605
--- /dev/null
+++ b/framework/source/uielement/genericstatusbarcontroller.cxx
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/genericstatusbarcontroller.hxx>
+#include <uielement/statusbarmerger.hxx>
+
+#include <osl/diagnose.h>
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/ui/ItemStyle.hpp>
+#include <com/sun/star/ui/XStatusbarItem.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/awt/ImageDrawMode.hpp>
+#include <com/sun/star/awt/XGraphics2.hpp>
+#include <com/sun/star/graphic/GraphicType.hpp>
+
+using namespace ::cppu;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+
+namespace framework
+{
+
+GenericStatusbarController::GenericStatusbarController(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rxFrame,
+ const Reference< ui::XStatusbarItem >& rxItem,
+ AddonStatusbarItemData *pItemData )
+ : svt::StatusbarController( rxContext, rxFrame, OUString(), 0 )
+ , m_bEnabled( false )
+ , m_bOwnerDraw( false )
+ , m_pItemData( pItemData )
+{
+ m_xStatusbarItem = rxItem;
+ if ( m_xStatusbarItem.is() )
+ {
+ assert(m_aCommandURL.pData);
+ m_aCommandURL = m_xStatusbarItem->getCommand();
+ m_nID = m_xStatusbarItem->getItemId();
+ m_bOwnerDraw = ( m_xStatusbarItem->getStyle() & ui::ItemStyle::OWNER_DRAW ) == ui::ItemStyle::OWNER_DRAW;
+ if ( !m_bOwnerDraw && m_pItemData && m_pItemData->aLabel.getLength() )
+ m_xStatusbarItem->setText( m_pItemData->aLabel );
+ }
+}
+
+GenericStatusbarController::~GenericStatusbarController()
+{
+}
+
+void SAL_CALL GenericStatusbarController::dispose()
+{
+ svt::StatusbarController::dispose();
+
+ SolarMutexGuard aGuard;
+ m_pItemData = nullptr;
+ m_xGraphic.clear();
+ m_xStatusbarItem.clear();
+
+}
+
+void SAL_CALL GenericStatusbarController::statusChanged(
+ const FeatureStateEvent& rEvent)
+{
+ SolarMutexGuard aGuard;
+
+ if ( m_bDisposed || !m_xStatusbarItem.is() )
+ return;
+
+ m_bEnabled = rEvent.IsEnabled;
+
+ OUString aStrValue;
+ Reference< graphic::XGraphic > aGraphic;
+
+ if ( rEvent.State >>= aStrValue )
+ {
+ if ( !m_bOwnerDraw )
+ m_xStatusbarItem->setText( aStrValue );
+ else
+ {
+ if ( aStrValue.getLength() )
+ {
+ m_xStatusbarItem->setQuickHelpText( aStrValue );
+ }
+ }
+ }
+ else if ( ( rEvent.State >>= aGraphic ) && m_bOwnerDraw )
+ {
+ m_xGraphic = aGraphic;
+ }
+
+ // when the status is updated, and the controller is responsible for
+ // painting the statusbar item content, we must trigger a repaint
+ if ( m_bOwnerDraw && m_xStatusbarItem->getVisible() )
+ {
+ m_xStatusbarItem->repaint();
+ }
+}
+
+void SAL_CALL GenericStatusbarController::paint(
+ const Reference< awt::XGraphics >& xGraphics,
+ const awt::Rectangle& rOutputRectangle,
+ ::sal_Int32 /*nStyle*/ )
+{
+ SolarMutexGuard aGuard;
+
+ const Reference< awt::XGraphics2 > xGraphics2(xGraphics, UNO_QUERY);
+
+ if ( !m_xStatusbarItem.is() || !xGraphics2.is() )
+ return;
+
+ Reference< beans::XPropertySet > xGraphicProps( m_xGraphic, UNO_QUERY );
+
+ if ( xGraphicProps.is() && m_xGraphic->getType() != graphic::GraphicType::EMPTY )
+ {
+ awt::Size aGraphicSize;
+ xGraphicProps->getPropertyValue( "SizePixel" ) >>= aGraphicSize;
+ OSL_ENSURE( aGraphicSize.Height > 0 && aGraphicSize.Width > 0, "Empty status bar graphic!" );
+
+ sal_Int32 nOffset = m_xStatusbarItem->getOffset( );
+ awt::Point aPos;
+ aPos.X = ( rOutputRectangle.Width + nOffset ) / 2 - aGraphicSize.Width / 2;
+ aPos.Y = rOutputRectangle.Height / 2 - aGraphicSize.Height / 2;
+
+ xGraphics2->drawImage( rOutputRectangle.X + aPos.X,
+ rOutputRectangle.Y + aPos.Y,
+ aGraphicSize.Width,
+ aGraphicSize.Height,
+ m_bEnabled ? awt::ImageDrawMode::NONE : awt::ImageDrawMode::DISABLE,
+ m_xGraphic );
+ }
+ else
+ {
+ xGraphics2->clear( rOutputRectangle );
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/generictoolbarcontroller.cxx b/framework/source/uielement/generictoolbarcontroller.cxx
new file mode 100644
index 000000000..25f2e0157
--- /dev/null
+++ b/framework/source/uielement/generictoolbarcontroller.cxx
@@ -0,0 +1,323 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/generictoolbarcontroller.hxx>
+
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/frame/status/ItemStatus.hpp>
+#include <com/sun/star/frame/status/Visibility.hpp>
+#include <com/sun/star/frame/ControlCommand.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/weld.hxx>
+#include <tools/urlobj.hxx>
+#include <strings.hrc>
+#include <classes/fwkresid.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::frame::status;
+
+namespace framework
+{
+
+static bool isEnumCommand( std::u16string_view rCommand )
+{
+ INetURLObject aURL( rCommand );
+
+ return ( aURL.GetProtocol() == INetProtocol::Uno ) &&
+ ( aURL.GetURLPath().indexOf( '.' ) != -1);
+}
+
+static OUString getEnumCommand( std::u16string_view rCommand )
+{
+ INetURLObject aURL( rCommand );
+
+ OUString aEnumCommand;
+ OUString aURLPath = aURL.GetURLPath();
+ sal_Int32 nIndex = aURLPath.indexOf( '.' );
+ if (( nIndex > 0 ) && ( nIndex < aURLPath.getLength() ))
+ aEnumCommand = aURLPath.copy( nIndex+1 );
+
+ return aEnumCommand;
+}
+
+static OUString getMasterCommand( const OUString& rCommand )
+{
+ OUString aMasterCommand( rCommand );
+ INetURLObject aURL( rCommand );
+ if ( aURL.GetProtocol() == INetProtocol::Uno )
+ {
+ sal_Int32 nIndex = aURL.GetURLPath().indexOf( '.' );
+ if ( nIndex )
+ {
+ aURL.SetURLPath( aURL.GetURLPath().subView( 0, nIndex ) );
+ aMasterCommand = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+ }
+ return aMasterCommand;
+}
+
+GenericToolbarController::GenericToolbarController( const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ ToolBox* pToolbar,
+ ToolBoxItemId nID,
+ const OUString& aCommand ) :
+ svt::ToolboxController( rxContext, rFrame, aCommand )
+ , m_xToolbar( pToolbar )
+ , m_nID( nID )
+ , m_bEnumCommand( isEnumCommand( aCommand ))
+ , m_bMadeInvisible( false )
+ , m_aEnumCommand( getEnumCommand( aCommand ))
+{
+ if ( m_bEnumCommand )
+ addStatusListener( getMasterCommand( aCommand ) );
+
+ addStatusListener( aCommand );
+
+ // Initialization is done through ctor
+ m_bInitialized = true;
+}
+
+GenericToolbarController::GenericToolbarController( const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ weld::Toolbar& rToolbar,
+ const OUString& aCommand ) :
+ GenericToolbarController( rxContext, rFrame, nullptr, ToolBoxItemId(0), aCommand )
+{
+ m_pToolbar = &rToolbar;
+}
+
+GenericToolbarController::~GenericToolbarController()
+{
+}
+
+void SAL_CALL GenericToolbarController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ svt::ToolboxController::dispose();
+
+ m_pToolbar = nullptr;
+ m_xToolbar.clear();
+ m_nID = ToolBoxItemId(0);
+}
+
+void SAL_CALL GenericToolbarController::execute( sal_Int16 KeyModifier )
+{
+ Reference< XDispatch > xDispatch;
+ OUString aCommandURL;
+
+ {
+ SolarMutexGuard aSolarMutexGuard;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( m_bInitialized &&
+ m_xFrame.is() &&
+ !m_aCommandURL.isEmpty() )
+ {
+ aCommandURL = m_aCommandURL;
+ URLToDispatchMap::iterator pIter = m_aListenerMap.find( m_aCommandURL );
+ if ( pIter != m_aListenerMap.end() )
+ xDispatch = pIter->second;
+ }
+ }
+
+ if ( !xDispatch.is() )
+ return;
+
+ css::util::URL aTargetURL;
+
+ // Add key modifier to argument list
+ Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("KeyModifier", KeyModifier) };
+
+ // handle also command aliases
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(m_aCommandURL,
+ vcl::CommandInfoProvider::GetModuleIdentifier(m_xFrame));
+ OUString sRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties);
+
+ aTargetURL.Complete = sRealCommand.isEmpty() ? aCommandURL : sRealCommand;
+ if ( m_xUrlTransformer.is() )
+ m_xUrlTransformer->parseStrict( aTargetURL );
+
+ // Execute dispatch asynchronously
+ ExecuteInfo* pExecuteInfo = new ExecuteInfo;
+ pExecuteInfo->xDispatch = xDispatch;
+ pExecuteInfo->aTargetURL = aTargetURL;
+ pExecuteInfo->aArgs = aArgs;
+ Application::PostUserEvent( LINK(nullptr, GenericToolbarController , ExecuteHdl_Impl), pExecuteInfo );
+}
+
+void GenericToolbarController::statusChanged( const FeatureStateEvent& Event )
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ if ( m_bDisposed )
+ return;
+
+ if ( m_pToolbar )
+ {
+ OString sId = m_aCommandURL.toUtf8();
+
+ m_pToolbar->set_item_sensitive(sId, Event.IsEnabled);
+
+ bool bValue;
+ OUString aStrValue;
+
+ if ( Event.State >>= bValue )
+ {
+ // Boolean, treat it as checked/unchecked
+ m_pToolbar->set_item_active(sId, bValue);
+ }
+ else if ( Event.State >>= aStrValue )
+ {
+ m_pToolbar->set_item_label(sId, aStrValue);
+ }
+ else
+ m_pToolbar->set_item_active(sId, false);
+
+ return;
+ }
+
+ if ( !m_xToolbar )
+ return;
+
+ m_xToolbar->EnableItem( m_nID, Event.IsEnabled );
+
+ ToolBoxItemBits nItemBits = m_xToolbar->GetItemBits( m_nID );
+ nItemBits &= ~ToolBoxItemBits::CHECKABLE;
+ TriState eTri = TRISTATE_FALSE;
+
+ bool bValue;
+ OUString aStrValue;
+ ItemStatus aItemState;
+ Visibility aItemVisibility;
+ ControlCommand aControlCommand;
+
+ if (( Event.State >>= bValue ) && !m_bEnumCommand )
+ {
+ // Boolean, treat it as checked/unchecked
+ if ( m_bMadeInvisible )
+ m_xToolbar->ShowItem( m_nID );
+ m_xToolbar->CheckItem( m_nID, bValue );
+ if ( bValue )
+ eTri = TRISTATE_TRUE;
+ nItemBits |= ToolBoxItemBits::CHECKABLE;
+ }
+ else if ( Event.State >>= aStrValue )
+ {
+ if ( m_bEnumCommand )
+ {
+ bValue = aStrValue == m_aEnumCommand;
+
+ m_xToolbar->CheckItem( m_nID, bValue );
+ if ( bValue )
+ eTri = TRISTATE_TRUE;
+ nItemBits |= ToolBoxItemBits::CHECKABLE;
+ }
+ else
+ {
+ // Replacement for place holders
+ if ( aStrValue.startsWith("($1)") )
+ {
+ aStrValue = FwkResId(STR_UPDATEDOC) + " " + aStrValue.subView( 4 );
+ }
+ else if ( aStrValue.startsWith("($2)") )
+ {
+ aStrValue = FwkResId(STR_CLOSEDOC_ANDRETURN) + aStrValue.subView( 4 );
+ }
+ else if ( aStrValue.startsWith("($3)") )
+ {
+ aStrValue = FwkResId(STR_SAVECOPYDOC) + aStrValue.subView( 4 );
+ }
+ m_xToolbar->SetItemText( m_nID, aStrValue );
+ // tdf#124267 strip mnemonic from tooltip
+ m_xToolbar->SetQuickHelpText(m_nID, aStrValue.replaceFirst("~", ""));
+ }
+
+ if ( m_bMadeInvisible )
+ m_xToolbar->ShowItem( m_nID );
+ }
+ else if (( Event.State >>= aItemState ) && !m_bEnumCommand )
+ {
+ eTri = TRISTATE_INDET;
+ nItemBits |= ToolBoxItemBits::CHECKABLE;
+ if ( m_bMadeInvisible )
+ m_xToolbar->ShowItem( m_nID );
+ }
+ else if ( Event.State >>= aItemVisibility )
+ {
+ m_xToolbar->ShowItem( m_nID, aItemVisibility.bVisible );
+ m_bMadeInvisible = !aItemVisibility.bVisible;
+ }
+ else if ( Event.State >>= aControlCommand )
+ {
+ if (aControlCommand.Command == "SetQuickHelpText")
+ {
+ for ( NamedValue const & rArg : std::as_const(aControlCommand.Arguments) )
+ {
+ if (rArg.Name == "HelpText")
+ {
+ OUString aHelpText;
+ rArg.Value >>= aHelpText;
+ m_xToolbar->SetQuickHelpText(m_nID, aHelpText);
+ break;
+ }
+ }
+ }
+ if ( m_bMadeInvisible )
+ m_xToolbar->ShowItem( m_nID );
+ }
+ else if ( m_bMadeInvisible )
+ m_xToolbar->ShowItem( m_nID );
+
+ m_xToolbar->SetItemState( m_nID, eTri );
+ m_xToolbar->SetItemBits( m_nID, nItemBits );
+}
+
+IMPL_STATIC_LINK( GenericToolbarController, ExecuteHdl_Impl, void*, p, void )
+{
+ ExecuteInfo* pExecuteInfo = static_cast<ExecuteInfo*>(p);
+ SolarMutexReleaser aReleaser;
+ try
+ {
+ // Asynchronous execution as this can lead to our own destruction!
+ // Framework can recycle our current frame and the layout manager disposes all user interface
+ // elements if a component gets detached from its frame!
+ pExecuteInfo->xDispatch->dispatch( pExecuteInfo->aTargetURL, pExecuteInfo->aArgs );
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ delete pExecuteInfo;
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/headermenucontroller.cxx b/framework/source/uielement/headermenucontroller.cxx
new file mode 100644
index 000000000..cbcdc33bc
--- /dev/null
+++ b/framework/source/uielement/headermenucontroller.cxx
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/headermenucontroller.hxx>
+
+#include <services.h>
+
+#include <strings.hrc>
+#include <classes/fwkresid.hxx>
+
+#include <com/sun/star/awt/MenuItemStyle.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <vcl/svapp.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/mutex.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+// Defines
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+using namespace com::sun::star::style;
+using namespace com::sun::star::container;
+
+const sal_uInt16 ALL_MENUITEM_ID = 1;
+
+namespace framework
+{
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL HeaderMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.HeaderMenuController";
+}
+
+sal_Bool SAL_CALL HeaderMenuController::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL HeaderMenuController::getSupportedServiceNames()
+{
+ return { SERVICENAME_POPUPMENUCONTROLLER };
+}
+
+HeaderMenuController::HeaderMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext, bool _bFooter ) :
+ svt::PopupMenuControllerBase( xContext )
+ ,m_bFooter(_bFooter)
+{
+}
+
+HeaderMenuController::~HeaderMenuController()
+{
+}
+
+// private function
+void HeaderMenuController::fillPopupMenu( const Reference< css::frame::XModel >& rModel, Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu( rPopupMenu );
+
+ Reference< XStyleFamiliesSupplier > xStyleFamiliesSupplier( rModel, UNO_QUERY );
+ if (!xStyleFamiliesSupplier.is())
+ return;
+
+ Reference< XNameAccess > xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
+
+ OUString aCmd( ".uno:InsertPageHeader" );
+ OUString aHeaderFooterIsOnStr( "HeaderIsOn" );
+ if ( m_bFooter )
+ {
+ aCmd = ".uno:InsertPageFooter";
+ aHeaderFooterIsOnStr = "FooterIsOn";
+ }
+ static const OUStringLiteral aIsPhysicalStr( u"IsPhysical" );
+ static const OUStringLiteral aDisplayNameStr( u"DisplayName" );
+
+ try
+ {
+ Reference< XNameContainer > xNameContainer;
+ if ( xStyleFamilies->getByName("PageStyles") >>= xNameContainer )
+ {
+ Sequence< OUString > aSeqNames = xNameContainer->getElementNames();
+
+ sal_uInt16 nId = 2;
+ sal_uInt16 nCount = 0;
+ bool bAllOneState( true );
+ bool bLastCheck( true );
+ bool bFirstChecked( false );
+ bool bFirstItemInserted( false );
+ for ( sal_Int32 n = 0; n < aSeqNames.getLength(); n++ )
+ {
+ OUString aName = aSeqNames[n];
+ Reference< XPropertySet > xPropSet( xNameContainer->getByName( aName ), UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ bool bIsPhysical( false );
+ if (( xPropSet->getPropertyValue( aIsPhysicalStr ) >>= bIsPhysical ) && bIsPhysical )
+ {
+ OUString aDisplayName;
+ bool bHeaderIsOn( false );
+ xPropSet->getPropertyValue( aDisplayNameStr ) >>= aDisplayName;
+ xPropSet->getPropertyValue( aHeaderFooterIsOnStr ) >>= bHeaderIsOn;
+
+ OUStringBuffer aStrBuf( aCmd );
+ aStrBuf.append( "?PageStyle:string=");
+ aStrBuf.append( aDisplayName );
+ aStrBuf.append( "&On:bool=" );
+ if ( !bHeaderIsOn )
+ aStrBuf.append( "true" );
+ else
+ aStrBuf.append( "false" );
+ OUString aCommand( aStrBuf.makeStringAndClear() );
+ rPopupMenu->insertItem(nId, aDisplayName, css::awt::MenuItemStyle::CHECKABLE, nCount);
+ if ( !bFirstItemInserted )
+ {
+ bFirstItemInserted = true;
+ bFirstChecked = bHeaderIsOn;
+ }
+
+ rPopupMenu->setCommand(nId, aCommand);
+ rPopupMenu->checkItem(nId, bHeaderIsOn);
+ ++nId;
+
+ // Check if all entries have the same state
+ if( bAllOneState && n && bHeaderIsOn != bLastCheck )
+ bAllOneState = false;
+ bLastCheck = bHeaderIsOn;
+ ++nCount;
+ }
+ }
+ }
+
+ if ( bAllOneState && ( nCount > 1 ))
+ {
+ // Insert special item for all command
+ rPopupMenu->insertItem(ALL_MENUITEM_ID, FwkResId(STR_MENU_HEADFOOTALL), 0, 0);
+
+ OUStringBuffer aStrBuf( aCmd );
+ aStrBuf.append( "?On:bool=" );
+
+ // Command depends on check state of first menu item entry
+ if ( !bFirstChecked )
+ aStrBuf.append( "true" );
+ else
+ aStrBuf.append( "false" );
+
+ rPopupMenu->setCommand(1, aStrBuf.makeStringAndClear());
+ rPopupMenu->insertSeparator(1);
+ }
+ }
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ }
+}
+
+// XEventListener
+void SAL_CALL HeaderMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(this);
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) );
+ m_xPopupMenu.clear();
+}
+
+// XStatusListener
+void SAL_CALL HeaderMenuController::statusChanged( const FeatureStateEvent& Event )
+{
+ Reference< css::frame::XModel > xModel;
+
+ if ( Event.State >>= xModel )
+ {
+ osl::MutexGuard aLock( m_aMutex );
+ m_xModel = xModel;
+ if ( m_xPopupMenu.is() )
+ fillPopupMenu( xModel, m_xPopupMenu );
+ }
+}
+
+// XMenuListener
+void SAL_CALL HeaderMenuController::updatePopupMenu()
+{
+ osl::ResettableMutexGuard aLock( m_aMutex );
+
+ throwIfDisposed();
+
+ Reference< css::frame::XModel > xModel( m_xModel );
+ aLock.clear();
+
+ if ( !xModel.is() )
+ svt::PopupMenuControllerBase::updatePopupMenu();
+
+ aLock.reset();
+ if ( m_xPopupMenu.is() && m_xModel.is() )
+ fillPopupMenu( m_xModel, m_xPopupMenu );
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_HeaderMenuController_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::HeaderMenuController(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/imagebuttontoolbarcontroller.cxx b/framework/source/uielement/imagebuttontoolbarcontroller.cxx
new file mode 100644
index 000000000..1ab3c0cf8
--- /dev/null
+++ b/framework/source/uielement/imagebuttontoolbarcontroller.cxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/imagebuttontoolbarcontroller.hxx>
+
+#include <framework/addonsoptions.hxx>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <comphelper/getexpandeduri.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/toolbox.hxx>
+#include <svtools/miscopt.hxx>
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::util;
+
+const ::Size aImageSizeSmall( 16, 16 );
+const ::Size aImageSizeBig( 26, 26 );
+
+namespace framework
+{
+
+static void SubstituteVariables( OUString& aURL )
+{
+ aURL = comphelper::getExpandedUri(
+ comphelper::getProcessComponentContext(), aURL);
+}
+
+ImageButtonToolbarController::ImageButtonToolbarController(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ ToolBox* pToolbar,
+ ToolBoxItemId nID,
+ const OUString& aCommand ) :
+ ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand )
+{
+ bool bBigImages( SvtMiscOptions().AreCurrentSymbolsLarge() );
+
+ Image aImage(AddonsOptions().GetImageFromURL(aCommand, bBigImages, true));
+
+ // Height will be controlled by scaling according to button height
+ m_xToolbar->SetItemImage( m_nID, aImage );
+}
+
+ImageButtonToolbarController::~ImageButtonToolbarController()
+{
+}
+
+void SAL_CALL ImageButtonToolbarController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+ ComplexToolbarController::dispose();
+}
+
+void ImageButtonToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand )
+{
+ SolarMutexGuard aSolarMutexGuard;
+ // i73486 to be downward compatible use old and "wrong" also!
+ if( rControlCommand.Command != "SetImag" &&
+ rControlCommand.Command != "SetImage" )
+ return;
+
+ for ( const NamedValue& rArg : rControlCommand.Arguments )
+ {
+ if ( rArg.Name == "URL" )
+ {
+ OUString aURL;
+ rArg.Value >>= aURL;
+
+ SubstituteVariables( aURL );
+
+ Image aImage;
+ if ( ReadImageFromURL( SvtMiscOptions().AreCurrentSymbolsLarge(),
+ aURL,
+ aImage ))
+ {
+ m_xToolbar->SetItemImage( m_nID, aImage );
+
+ // send notification
+ uno::Sequence< beans::NamedValue > aInfo { { "URL", css::uno::Any(aURL) } };
+ addNotifyInfo( "ImageChanged",
+ getDispatchFromCommand( m_aCommandURL ),
+ aInfo );
+ break;
+ }
+ }
+ }
+}
+
+bool ImageButtonToolbarController::ReadImageFromURL( bool bBigImage, const OUString& aImageURL, Image& aImage )
+{
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( aImageURL, StreamMode::STD_READ ));
+ if ( !pStream || ( pStream->GetErrorCode() != ERRCODE_NONE ))
+ return false;
+
+ // Use graphic class to also support more graphic formats (bmp,png,...)
+ Graphic aGraphic;
+
+ GraphicFilter& rGF = GraphicFilter::GetGraphicFilter();
+ rGF.ImportGraphic( aGraphic, u"", *pStream );
+
+ BitmapEx aBitmapEx = aGraphic.GetBitmapEx();
+
+ const ::Size aSize = bBigImage ? aImageSizeBig : aImageSizeSmall; // Sizes used for toolbar images
+
+ ::Size aBmpSize = aBitmapEx.GetSizePixel();
+ if ( !aBmpSize.IsEmpty() )
+ {
+ ::Size aNoScaleSize( aBmpSize.Width(), aSize.Height() );
+ if ( aBmpSize != aNoScaleSize )
+ aBitmapEx.Scale( aNoScaleSize, BmpScaleFlag::BestQuality );
+ aImage = Image( aBitmapEx );
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/langselectionmenucontroller.cxx b/framework/source/uielement/langselectionmenucontroller.cxx
new file mode 100644
index 000000000..94918c021
--- /dev/null
+++ b/framework/source/uielement/langselectionmenucontroller.cxx
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/langselectionmenucontroller.hxx>
+
+#include <services.h>
+
+#include <com/sun/star/awt/MenuItemStyle.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+#include <vcl/svapp.hxx>
+
+#include <svl/languageoptions.hxx>
+#include <svtools/langtab.hxx>
+#include <classes/fwkresid.hxx>
+
+#include <strings.hrc>
+
+#include <helper/mischelper.hxx>
+#include <osl/mutex.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <map>
+#include <set>
+
+// Defines
+
+using namespace ::com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+
+namespace framework
+{
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL LanguageSelectionMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.LanguageSelectionMenuController";
+}
+
+sal_Bool SAL_CALL LanguageSelectionMenuController::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL LanguageSelectionMenuController::getSupportedServiceNames()
+{
+ return { SERVICENAME_POPUPMENUCONTROLLER };
+}
+
+
+LanguageSelectionMenuController::LanguageSelectionMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext )
+ : svt::PopupMenuControllerBase(xContext)
+ , m_bShowMenu(true)
+ , m_nScriptType(SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX)
+ , m_aLangGuessHelper(xContext)
+{
+}
+
+LanguageSelectionMenuController::~LanguageSelectionMenuController()
+{
+}
+
+// XEventListener
+void SAL_CALL LanguageSelectionMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(this);
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+ m_xLanguageDispatch.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) );
+ m_xPopupMenu.clear();
+}
+
+// XStatusListener
+void SAL_CALL LanguageSelectionMenuController::statusChanged( const FeatureStateEvent& Event )
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ return;
+
+ m_bShowMenu = true;
+ m_nScriptType = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; //set the default value
+
+ Sequence< OUString > aSeq;
+
+ if ( Event.State >>= aSeq )
+ {
+ if ( aSeq.getLength() == 4 )
+ {
+ // Retrieve all other values from the sequence and
+ // store it members!
+ m_aCurLang = aSeq[0];
+ m_nScriptType = static_cast< SvtScriptType >(aSeq[1].toInt32());
+ m_aKeyboardLang = aSeq[2];
+ m_aGuessedTextLang = aSeq[3];
+ }
+ }
+ else if ( !Event.State.hasValue() )
+ {
+ m_bShowMenu = false; // no language -> no sub-menu entries -> disable menu
+ }
+}
+
+// XPopupMenuController
+void LanguageSelectionMenuController::impl_setPopupMenu()
+{
+ Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
+
+ css::util::URL aTargetURL;
+
+ // Register for language updates
+ aTargetURL.Complete = m_aLangStatusCommandURL;
+ m_xURLTransformer->parseStrict( aTargetURL );
+ m_xLanguageDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+
+ // Register for setting languages and opening language dialog
+ aTargetURL.Complete = m_aMenuCommandURL_Lang;
+ m_xURLTransformer->parseStrict( aTargetURL );
+ m_xMenuDispatch_Lang = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+
+ // Register for opening character dialog
+ aTargetURL.Complete = m_aMenuCommandURL_Font;
+ m_xURLTransformer->parseStrict( aTargetURL );
+ m_xMenuDispatch_Font = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+
+ // Register for opening character dialog with preselected paragraph
+ aTargetURL.Complete = m_aMenuCommandURL_CharDlgForParagraph;
+ m_xURLTransformer->parseStrict( aTargetURL );
+ m_xMenuDispatch_CharDlgForParagraph = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+}
+
+void LanguageSelectionMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu , const Mode eMode )
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu( rPopupMenu );
+ if (!m_bShowMenu)
+ return;
+
+ OUString aCmd_Dialog;
+ OUString aCmd_Language;
+ if( eMode == MODE_SetLanguageSelectionMenu )
+ {
+ aCmd_Dialog += ".uno:FontDialog?Page:string=font";
+ aCmd_Language += ".uno:LanguageStatus?Language:string=Current_";
+ }
+ else if ( eMode == MODE_SetLanguageParagraphMenu )
+ {
+ aCmd_Dialog += ".uno:FontDialogForParagraph";
+ aCmd_Language += ".uno:LanguageStatus?Language:string=Paragraph_";
+ }
+ else if ( eMode == MODE_SetLanguageAllTextMenu )
+ {
+ aCmd_Dialog += ".uno:LanguageStatus?Language:string=*";
+ aCmd_Language += ".uno:LanguageStatus?Language:string=Default_";
+ }
+
+ // get languages to be displayed in the menu
+ std::set< OUString > aLangItems;
+ FillLangItems( aLangItems, m_xFrame, m_aLangGuessHelper,
+ m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedTextLang );
+
+ // now add menu entries
+ // the different menus purpose will be handled by the different string
+ // for aCmd_Dialog and aCmd_Language
+ sal_Int16 nItemId = 0; // in this control the item id is not important for executing the command
+ static const OUStringLiteral sAsterisk(u"*"); // multiple languages in current selection
+ const OUString sNone( SvtLanguageTable::GetLanguageString( LANGUAGE_NONE ));
+ for (auto const& langItem : aLangItems)
+ {
+ if (langItem != sNone &&
+ langItem != sAsterisk &&
+ !langItem.isEmpty()) // 'no language found' from language guessing
+ {
+ ++nItemId;
+ rPopupMenu->insertItem(nItemId, langItem, css::awt::MenuItemStyle::CHECKABLE, nItemId - 1);
+ OUString aCmd = aCmd_Language + langItem;
+ rPopupMenu->setCommand(nItemId, aCmd);
+ bool bChecked = langItem == m_aCurLang && eMode == MODE_SetLanguageSelectionMenu;
+ //make a sign for the current language
+ rPopupMenu->checkItem(nItemId, bChecked);
+ }
+ }
+
+ // entry for LANGUAGE_NONE
+ ++nItemId;
+ rPopupMenu->insertItem(nItemId, FwkResId(STR_LANGSTATUS_NONE), 0, nItemId - 1);
+ OUString aCmd = aCmd_Language + "LANGUAGE_NONE";
+ rPopupMenu->setCommand(nItemId, aCmd);
+
+ // entry for 'Reset to default language'
+ ++nItemId;
+ rPopupMenu->insertItem(nItemId, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, nItemId - 1);
+ aCmd = aCmd_Language + "RESET_LANGUAGES";
+ rPopupMenu->setCommand(nItemId, aCmd);
+
+ // entry for opening the Format/Character dialog
+ ++nItemId;
+ rPopupMenu->insertItem(nItemId, FwkResId(STR_LANGSTATUS_MORE), 0, nItemId - 1);
+ rPopupMenu->setCommand(nItemId, aCmd_Dialog);
+}
+
+void SAL_CALL LanguageSelectionMenuController::updatePopupMenu()
+{
+ svt::PopupMenuControllerBase::updatePopupMenu();
+
+ // Force status update to get information about the current languages
+ osl::ClearableMutexGuard aLock( m_aMutex );
+ Reference< XDispatch > xDispatch( m_xLanguageDispatch );
+ css::util::URL aTargetURL;
+ aTargetURL.Complete = m_aLangStatusCommandURL;
+ m_xURLTransformer->parseStrict( aTargetURL );
+ aLock.clear();
+
+ if ( xDispatch.is() )
+ {
+ xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
+ xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
+ }
+
+ // TODO: Fill menu with the information retrieved by the status update
+
+ if ( m_aCommandURL == ".uno:SetLanguageSelectionMenu" )
+ {
+ fillPopupMenu(m_xPopupMenu, MODE_SetLanguageSelectionMenu );
+ }
+ else if ( m_aCommandURL == ".uno:SetLanguageParagraphMenu" )
+ {
+ fillPopupMenu(m_xPopupMenu, MODE_SetLanguageParagraphMenu );
+ }
+ else if ( m_aCommandURL == ".uno:SetLanguageAllTextMenu" )
+ {
+ fillPopupMenu(m_xPopupMenu, MODE_SetLanguageAllTextMenu );
+ }
+}
+
+// XInitialization
+void SAL_CALL LanguageSelectionMenuController::initialize( const Sequence< Any >& aArguments )
+{
+ osl::MutexGuard aLock( m_aMutex );
+
+ bool bInitialized( m_bInitialized );
+ if ( !bInitialized )
+ {
+ svt::PopupMenuControllerBase::initialize(aArguments);
+
+ if ( m_bInitialized )
+ {
+ m_aLangStatusCommandURL = ".uno:LanguageStatus";
+ m_aMenuCommandURL_Lang = m_aLangStatusCommandURL;
+ m_aMenuCommandURL_Font = ".uno:FontDialog";
+ m_aMenuCommandURL_CharDlgForParagraph = ".uno:FontDialogForParagraph";
+ }
+ }
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_LanguageSelectionMenuController_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::LanguageSelectionMenuController(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/langselectionstatusbarcontroller.cxx b/framework/source/uielement/langselectionstatusbarcontroller.cxx
new file mode 100644
index 000000000..807806364
--- /dev/null
+++ b/framework/source/uielement/langselectionstatusbarcontroller.cxx
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <classes/fwkresid.hxx>
+#include <services.h>
+#include <strings.hrc>
+#include <vcl/svapp.hxx>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/awt/PopupMenu.hpp>
+#include <com/sun/star/awt/PopupMenuDirection.hpp>
+#include <svtools/langtab.hxx>
+#include <svtools/statusbarcontroller.hxx>
+#include <sal/types.h>
+#include <sal/log.hxx>
+#include <com/sun/star/document/XDocumentLanguages.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/ui/XStatusbarItem.hpp>
+
+#include <com/sun/star/frame/XFrame.hpp>
+
+#include <com/sun/star/awt/Command.hpp>
+#include <svl/languageoptions.hxx>
+
+#include <helper/mischelper.hxx>
+
+#include <rtl/ustrbuf.hxx>
+
+#include <map>
+#include <set>
+
+using namespace ::cppu;
+using namespace ::com::sun::star;
+using namespace css::uno;
+using namespace css::lang;
+using namespace css::frame;
+using namespace css::i18n;
+using namespace css::document;
+using namespace framework;
+
+namespace {
+
+class LangSelectionStatusbarController:
+ public svt::StatusbarController
+{
+public:
+ explicit LangSelectionStatusbarController( const css::uno::Reference< css::uno::XComponentContext >& xContext );
+ LangSelectionStatusbarController(const LangSelectionStatusbarController&) = delete;
+ LangSelectionStatusbarController& operator=(const LangSelectionStatusbarController&) = delete;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override;
+
+ // XStatusbarController
+ virtual void SAL_CALL command( const css::awt::Point& aPos,
+ ::sal_Int32 nCommand,
+ sal_Bool bMouseEvent,
+ const css::uno::Any& aData ) override;
+ virtual void SAL_CALL click( const css::awt::Point& aPos ) override;
+
+private:
+ virtual ~LangSelectionStatusbarController() override {}
+
+ bool m_bShowMenu; // if the menu is to be displayed or not (depending on the selected object/text)
+ SvtScriptType m_nScriptType; // the flags for the different script types available in the selection, LATIN = 0x0001, ASIAN = 0x0002, COMPLEX = 0x0004
+ OUString m_aCurLang; // the language of the current selection, "*" if there are more than one languages
+ OUString m_aKeyboardLang; // the keyboard language
+ OUString m_aGuessedTextLang; // the 'guessed' language for the selection, "" if none could be guessed
+ LanguageGuessingHelper m_aLangGuessHelper;
+
+ /// @throws css::uno::RuntimeException
+ void LangMenu( const css::awt::Point& aPos );
+};
+
+LangSelectionStatusbarController::LangSelectionStatusbarController( const uno::Reference< uno::XComponentContext >& xContext ) :
+ svt::StatusbarController( xContext, uno::Reference< frame::XFrame >(), OUString(), 0 ),
+ m_bShowMenu( true ),
+ m_nScriptType( SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX ),
+ m_aLangGuessHelper( xContext )
+{
+}
+
+void SAL_CALL LangSelectionStatusbarController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ svt::StatusbarController::initialize( aArguments );
+
+ if ( m_xStatusbarItem.is() )
+ {
+ m_xStatusbarItem->setText( FwkResId(STR_LANGSTATUS_MULTIPLE_LANGUAGES) );
+ m_xStatusbarItem->setQuickHelpText(FwkResId(STR_LANGSTATUS_HINT));
+ }
+}
+
+void LangSelectionStatusbarController::LangMenu(
+ const css::awt::Point& aPos )
+{
+ if (!m_bShowMenu)
+ return;
+
+ const Reference<XServiceInfo> xService(m_xFrame->getController()->getModel(), UNO_QUERY);
+ bool bWriter = xService.is() && xService->supportsService("com.sun.star.text.GenericTextDocument");
+ //add context menu
+ Reference< awt::XPopupMenu > xPopupMenu( awt::PopupMenu::create( m_xContext ) );
+ //sub menu that contains all items except the last two items: Separator + Set Language for Paragraph
+ Reference< awt::XPopupMenu > subPopupMenu( awt::PopupMenu::create( m_xContext ) );
+
+ // get languages to be displayed in the menu
+ std::set< OUString > aLangItems;
+ FillLangItems( aLangItems, m_xFrame, m_aLangGuessHelper,
+ m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedTextLang );
+
+ // add first few entries to main menu
+ sal_Int16 nItemId = static_cast< sal_Int16 >(MID_LANG_SEL_1);
+ static const OUStringLiteral sAsterisk(u"*"); // multiple languages in current selection
+ const OUString sNone( SvtLanguageTable::GetLanguageString( LANGUAGE_NONE ));
+ std::map< sal_Int16, OUString > aLangMap;
+ for (auto const& langItem : aLangItems)
+ {
+ if ( langItem != sNone &&
+ langItem != sAsterisk &&
+ !langItem.isEmpty()) // 'no language found' from language guessing
+ {
+ SAL_WARN_IF( MID_LANG_SEL_1 > nItemId || nItemId > MID_LANG_SEL_9,
+ "fwk.uielement", "nItemId outside of expected range!" );
+ xPopupMenu->insertItem( nItemId, langItem, 0, nItemId );
+ if ( langItem == m_aCurLang )
+ {
+ //make a sign for the current language
+ xPopupMenu->checkItem( nItemId, true );
+ }
+ aLangMap[ nItemId ] = langItem;
+ ++nItemId;
+ }
+ }
+
+ if (bWriter)
+ {
+ xPopupMenu->insertItem( MID_LANG_SEL_NONE, FwkResId(STR_LANGSTATUS_NONE), 0, MID_LANG_SEL_NONE );
+ if ( sNone == m_aCurLang )
+ xPopupMenu->checkItem( MID_LANG_SEL_NONE, true );
+ xPopupMenu->insertItem( MID_LANG_SEL_RESET, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, MID_LANG_SEL_RESET );
+ xPopupMenu->insertItem( MID_LANG_SEL_MORE, FwkResId(STR_LANGSTATUS_MORE), 0, MID_LANG_SEL_MORE );
+
+ // add entries to submenu ('set language for paragraph')
+ nItemId = static_cast< sal_Int16 >(MID_LANG_PARA_1);
+ for (auto const& langItem : aLangItems)
+ {
+ if( langItem != sNone &&
+ langItem != sAsterisk &&
+ !langItem.isEmpty()) // 'no language found' from language guessing
+ {
+ SAL_WARN_IF( MID_LANG_PARA_1 > nItemId || nItemId > MID_LANG_PARA_9,
+ "fwk.uielement", "nItemId outside of expected range!" );
+ subPopupMenu->insertItem( nItemId, langItem, 0, nItemId );
+ aLangMap[nItemId] = langItem;
+ ++nItemId;
+ }
+ }
+ subPopupMenu->insertItem( MID_LANG_PARA_NONE, FwkResId(STR_LANGSTATUS_NONE), 0, MID_LANG_PARA_NONE );
+ subPopupMenu->insertItem( MID_LANG_PARA_RESET, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, MID_LANG_PARA_RESET );
+ subPopupMenu->insertItem( MID_LANG_PARA_MORE, FwkResId(STR_LANGSTATUS_MORE), 0, MID_LANG_PARA_MORE );
+
+ // add last two entries to main menu
+ xPopupMenu->insertSeparator( MID_LANG_PARA_SEPARATOR );
+ xPopupMenu->insertItem( MID_LANG_PARA_STRING, FwkResId(STR_SET_LANGUAGE_FOR_PARAGRAPH), 0, MID_LANG_PARA_STRING );
+ xPopupMenu->setPopupMenu( MID_LANG_PARA_STRING, subPopupMenu );
+ }
+ else
+ {
+ xPopupMenu->insertItem( MID_LANG_DEF_NONE, FwkResId(STR_LANGSTATUS_NONE), 0, MID_LANG_DEF_NONE );
+ if ( sNone == m_aCurLang )
+ xPopupMenu->checkItem( MID_LANG_DEF_NONE, true );
+ xPopupMenu->insertItem( MID_LANG_DEF_RESET, FwkResId(STR_RESET_TO_DEFAULT_LANGUAGE), 0, MID_LANG_DEF_RESET );
+ xPopupMenu->insertItem( MID_LANG_DEF_MORE, FwkResId(STR_LANGSTATUS_MORE), 0, MID_LANG_DEF_MORE );
+ }
+
+ // now display the popup menu and execute every command ...
+
+ Reference< awt::XWindowPeer > xParent( m_xParentWindow, UNO_QUERY );
+ css::awt::Rectangle aRect( aPos.X, aPos.Y, 0, 0 );
+ sal_Int16 nId = xPopupMenu->execute( xParent, aRect, css::awt::PopupMenuDirection::EXECUTE_UP+16 );
+ //click "More..."
+ if ( !(nId && m_xFrame.is()) )
+ return;
+
+ OUStringBuffer aBuff;
+ //set selected language as current language for selection
+ const OUString aSelectedLang = aLangMap[nId];
+
+ if (MID_LANG_SEL_1 <= nId && nId <= MID_LANG_SEL_9)
+ {
+ if (bWriter)
+ aBuff.append( ".uno:LanguageStatus?Language:string=Current_" );
+ else
+ aBuff.append( ".uno:LanguageStatus?Language:string=Default_" );
+
+ aBuff.append( aSelectedLang );
+ }
+ else if (nId == MID_LANG_SEL_NONE)
+ {
+ //set None as current language for selection
+ aBuff.append( ".uno:LanguageStatus?Language:string=Current_LANGUAGE_NONE" );
+ }
+ else if (nId == MID_LANG_SEL_RESET)
+ {
+ // reset language attributes for selection
+ aBuff.append( ".uno:LanguageStatus?Language:string=Current_RESET_LANGUAGES" );
+ }
+ else if (nId == MID_LANG_SEL_MORE)
+ {
+ //open the dialog "format/character" for current selection
+ aBuff.append( ".uno:FontDialog?Page:string=font" );
+ }
+ else if (nId == MID_LANG_DEF_NONE)
+ {
+ aBuff.append( ".uno:LanguageStatus?Language:string=Default_LANGUAGE_NONE" );
+ }
+ else if (nId == MID_LANG_DEF_RESET)
+ {
+ aBuff.append( ".uno:LanguageStatus?Language:string=Default_RESET_LANGUAGES" );
+ }
+ else if (nId == MID_LANG_DEF_MORE)
+ {
+ aBuff.append( ".uno:LanguageStatus?Language:string=*" );
+ }
+ else if (MID_LANG_PARA_1 <= nId && nId <= MID_LANG_PARA_9)
+ {
+ aBuff.append( ".uno:LanguageStatus?Language:string=Paragraph_" );
+ aBuff.append( aSelectedLang );
+ }
+ else if (nId == MID_LANG_PARA_NONE)
+ {
+ //set None as language for current paragraph
+ aBuff.append( ".uno:LanguageStatus?Language:string=Paragraph_LANGUAGE_NONE" );
+ }
+ else if (nId == MID_LANG_PARA_RESET)
+ {
+ // reset language attributes for paragraph
+ aBuff.append( ".uno:LanguageStatus?Language:string=Paragraph_RESET_LANGUAGES" );
+ }
+ else if (nId == MID_LANG_PARA_MORE)
+ {
+ //open the dialog "format/character" for current paragraph
+ aBuff.append( ".uno:FontDialogForParagraph" );
+ }
+
+ const Sequence< beans::PropertyValue > aDummyArgs;
+ execute( aBuff.makeStringAndClear(), aDummyArgs );
+}
+
+void SAL_CALL LangSelectionStatusbarController::command(
+ const css::awt::Point& aPos,
+ ::sal_Int32 nCommand,
+ sal_Bool /*bMouseEvent*/,
+ const css::uno::Any& /*aData*/ )
+{
+ if ( nCommand & ::awt::Command::CONTEXTMENU )
+ {
+ LangMenu( aPos );
+ }
+}
+
+void SAL_CALL LangSelectionStatusbarController::click(
+ const css::awt::Point& aPos )
+{
+ LangMenu( aPos );
+}
+
+// XStatusListener
+void SAL_CALL LangSelectionStatusbarController::statusChanged( const FeatureStateEvent& Event )
+{
+ // This function will be called when observed data changes,
+ // for example the selection or keyboard language.
+ // - It displays the language in use in the status bar
+ // - and it stores the relevant data for creating the menu
+ // at some later point in the member variables
+ // m_nScriptType, m_aCurLang, m_aKeyboardLang, m_aGuessedText
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ if ( m_bDisposed )
+ return;
+
+ m_bShowMenu = true;
+ m_nScriptType = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; //set the default value
+
+ if ( !m_xStatusbarItem.is() )
+ return;
+
+ OUString aStrValue;
+ Sequence< OUString > aSeq;
+
+ if ( Event.State >>= aStrValue )
+ {
+ m_xStatusbarItem->setText( aStrValue );
+ m_xStatusbarItem->setQuickHelpText(FwkResId(STR_LANGSTATUS_HINT));
+ m_aCurLang = aStrValue;
+ }
+ else if ( Event.State >>= aSeq )
+ {
+ if ( aSeq.getLength() == 4 )
+ {
+ OUString aStatusText = aSeq[0];
+ if (aStatusText == "*")
+ {
+ aStatusText = FwkResId(STR_LANGSTATUS_MULTIPLE_LANGUAGES);
+ }
+ m_xStatusbarItem->setText( aStatusText );
+ m_xStatusbarItem->setQuickHelpText(FwkResId(STR_LANGSTATUS_HINT));
+
+ // Retrieve all other values from the sequence and
+ // store it members!
+ m_aCurLang = aSeq[0];
+ m_nScriptType = static_cast< SvtScriptType >( aSeq[1].toInt32() );
+ m_aKeyboardLang = aSeq[2];
+ m_aGuessedTextLang = aSeq[3];
+ }
+ }
+ else if ( !Event.State.hasValue() )
+ {
+ m_xStatusbarItem->setText( OUString() );
+ m_xStatusbarItem->setQuickHelpText(u"");
+ m_bShowMenu = false; // no language -> no menu
+ }
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_LangSelectionStatusbarController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new LangSelectionStatusbarController(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/macrosmenucontroller.cxx b/framework/source/uielement/macrosmenucontroller.cxx
new file mode 100644
index 000000000..a0745b1e4
--- /dev/null
+++ b/framework/source/uielement/macrosmenucontroller.cxx
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/macrosmenucontroller.hxx>
+#include <services.h>
+#include <com/sun/star/container/XContentEnumerationAccess.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <officecfg/Office/Common.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <osl/mutex.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+using namespace com::sun::star::style;
+using namespace com::sun::star::container;
+
+namespace framework
+{
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL MacrosMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.MacrosMenuController";
+}
+
+sal_Bool SAL_CALL MacrosMenuController::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL MacrosMenuController::getSupportedServiceNames()
+{
+ return { SERVICENAME_POPUPMENUCONTROLLER };
+}
+
+MacrosMenuController::MacrosMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
+ svt::PopupMenuControllerBase( xContext ),
+ m_xContext( xContext)
+{
+}
+
+MacrosMenuController::~MacrosMenuController()
+{
+}
+
+// private function
+void MacrosMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ bool bMacrosDisabled = officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get();
+ if (bMacrosDisabled)
+ return;
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu(rPopupMenu);
+ assert(rPopupMenu->getItemCount() == 0);
+
+ // insert basic
+ OUString aCommand(".uno:MacroDialog");
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommand, m_aModuleName);
+ OUString aDisplayName = vcl::CommandInfoProvider::GetMenuLabelForCommand(aProperties);
+ rPopupMenu->insertItem(2, aDisplayName, 0, 0);
+ rPopupMenu->setCommand(2, aCommand);
+
+ // insert providers but not basic or java
+ addScriptItems(rPopupMenu, 4);
+}
+
+// XEventListener
+void SAL_CALL MacrosMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(this);
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+ m_xContext.clear();
+
+ if ( m_xPopupMenu.is() )
+ {
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) );
+ }
+ m_xPopupMenu.clear();
+}
+
+// XStatusListener
+void SAL_CALL MacrosMenuController::statusChanged( const FeatureStateEvent& )
+{
+ osl::MutexGuard aLock( m_aMutex );
+ if ( m_xPopupMenu.is() )
+ {
+ fillPopupMenu( m_xPopupMenu );
+ }
+}
+
+void MacrosMenuController::addScriptItems(const Reference<css::awt::XPopupMenu>& rPopupMenu, sal_uInt16 startItemId)
+{
+ static const OUStringLiteral aCmdBase(u".uno:ScriptOrganizer?ScriptOrganizer.Language:string=");
+ static const OUStringLiteral ellipsis( u"..." );
+ static const OUStringLiteral providerKey(u"com.sun.star.script.provider.ScriptProviderFor");
+ sal_uInt16 itemId = startItemId;
+ Reference< XContentEnumerationAccess > xEnumAccess( m_xContext->getServiceManager(), UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnum = xEnumAccess->createContentEnumeration ( "com.sun.star.script.provider.LanguageScriptProvider" );
+
+ sal_Int16 nPos = rPopupMenu->getItemCount();
+
+ while ( xEnum->hasMoreElements() )
+ {
+ Reference< XServiceInfo > xServiceInfo;
+ if ( !( xEnum->nextElement() >>= xServiceInfo ) )
+ {
+ break;
+ }
+ const Sequence< OUString > serviceNames = xServiceInfo->getSupportedServiceNames();
+
+ for ( OUString const & serviceName : serviceNames )
+ {
+ if ( serviceName.startsWith( providerKey ) )
+ {
+ OUString aCommand = aCmdBase;
+ OUString aDisplayName = serviceName.copy( providerKey.getLength() );
+ if( aDisplayName == "Java" || aDisplayName == "Basic" )
+ {
+ // no entries for Java & Basic added elsewhere
+ break;
+ }
+ aCommand += aDisplayName;
+ aDisplayName += ellipsis;
+ rPopupMenu->insertItem(itemId, aDisplayName, 0, nPos++);
+ rPopupMenu->setCommand(itemId, aCommand);
+ itemId++;
+ break;
+ }
+ }
+ }
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_MacrosMenuController_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::MacrosMenuController(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/menubarmanager.cxx b/framework/source/uielement/menubarmanager.cxx
new file mode 100644
index 000000000..c7efdb920
--- /dev/null
+++ b/framework/source/uielement/menubarmanager.cxx
@@ -0,0 +1,1572 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/menubarmanager.hxx>
+#include <uielement/styletoolbarcontroller.hxx>
+#include <menuconfiguration.hxx>
+#include <addonmenu.hxx>
+#include <framework/addonsoptions.hxx>
+#include <classes/fwkresid.hxx>
+#include <strings.hrc>
+
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/uno/XCurrentContext.hpp>
+#include <com/sun/star/frame/XPopupMenuController.hpp>
+#include <com/sun/star/frame/thePopupMenuControllerFactory.hpp>
+#include <com/sun/star/lang/SystemDependent.hpp>
+#include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/ItemStyle.hpp>
+#include <com/sun/star/frame/status/Visibility.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <svtools/javainteractionhandler.hxx>
+#include <uno/current_context.hxx>
+#include <unotools/cmdoptions.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <sal/log.hxx>
+#include <svtools/acceleratorexecute.hxx>
+#include <svtools/miscopt.hxx>
+#include <uielement/menubarmerger.hxx>
+#include <tools/urlobj.hxx>
+
+using namespace ::cppu;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::ui;
+
+const sal_uInt16 ADDONMENU_MERGE_ITEMID_START = 1500;
+const sal_uInt16 ITEMID_ADDONLIST = 6678; // used to be a SID in sfx2, now just a unique id...
+
+namespace framework
+{
+
+constexpr OUStringLiteral aCmdHelpIndex = u".uno:HelpIndex";
+constexpr OUStringLiteral aCmdToolsMenu = u".uno:ToolsMenu";
+constexpr OUStringLiteral aCmdHelpMenu = u".uno:HelpMenu";
+constexpr OUStringLiteral aSpecialWindowCommand = u".uno:WindowList";
+
+MenuBarManager::MenuBarManager(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ const Reference< XURLTransformer >& _xURLTransformer,
+ const Reference< XDispatchProvider >& rDispatchProvider,
+ const OUString& rModuleIdentifier,
+ Menu* pMenu, bool bDelete, bool bHasMenuBar ):
+ m_bRetrieveImages( false )
+ , m_bAcceleratorCfg( false )
+ , m_bHasMenuBar( bHasMenuBar )
+ , m_xContext(rxContext)
+ , m_xURLTransformer(_xURLTransformer)
+ , m_sIconTheme( SvtMiscOptions().GetIconTheme() )
+ , m_aAsyncSettingsTimer( "framework::MenuBarManager::Deactivate m_aAsyncSettingsTimer" )
+{
+ m_xPopupMenuControllerFactory = frame::thePopupMenuControllerFactory::get(m_xContext);
+ FillMenuManager( pMenu, rFrame, rDispatchProvider, rModuleIdentifier, bDelete );
+}
+
+Any SAL_CALL MenuBarManager::getMenuHandle( const Sequence< sal_Int8 >& /*ProcessId*/, sal_Int16 SystemType )
+{
+ SolarMutexGuard aSolarGuard;
+
+ if ( m_bDisposed )
+ throw css::lang::DisposedException();
+
+ Any a;
+
+ if ( m_pVCLMenu )
+ {
+ SystemMenuData aSystemMenuData;
+
+ m_pVCLMenu->GetSystemMenuData( &aSystemMenuData );
+#ifdef _WIN32
+ if( SystemType == SystemDependent::SYSTEM_WIN32 )
+ {
+ a <<= sal_Int64(
+ reinterpret_cast<sal_IntPtr>(aSystemMenuData.hMenu));
+ }
+#else
+ (void) SystemType;
+#endif
+ }
+
+ return a;
+}
+
+MenuBarManager::~MenuBarManager()
+{
+ // stop asynchronous settings timer
+ m_xDeferredItemContainer.clear();
+ m_aAsyncSettingsTimer.Stop();
+
+ SAL_WARN_IF( OWeakObject::m_refCount != 0, "fwk.uielement", "Who wants to delete an object with refcount > 0!" );
+}
+
+// XComponent
+void MenuBarManager::disposing(std::unique_lock<std::mutex>& )
+{
+ Reference< XComponent > xThis( this );
+
+ SolarMutexGuard g;
+
+ // stop asynchronous settings timer and
+ // release deferred item container reference
+ m_aAsyncSettingsTimer.Stop();
+ m_xDeferredItemContainer.clear();
+ RemoveListener();
+
+ m_aMenuItemHandlerVector.clear();
+
+ if ( m_bDeleteMenu )
+ {
+ m_pVCLMenu.disposeAndClear();
+ }
+
+ if ( m_xDocImageManager.is() )
+ {
+ try
+ {
+ m_xDocImageManager->removeConfigurationListener(
+ Reference< XUIConfigurationListener >(this) );
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+ if ( m_xModuleImageManager.is() )
+ {
+ try
+ {
+ m_xModuleImageManager->removeConfigurationListener(
+ Reference< XUIConfigurationListener >(this) );
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+ m_xDocImageManager.clear();
+ m_xModuleImageManager.clear();
+ m_xGlobalAcceleratorManager.clear();
+ m_xModuleAcceleratorManager.clear();
+ m_xDocAcceleratorManager.clear();
+ m_xPopupMenuControllerFactory.clear();
+ m_xContext.clear();
+}
+
+void SAL_CALL MenuBarManager::elementInserted( const css::ui::ConfigurationEvent& Event )
+{
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ return;
+
+ sal_Int16 nImageType = sal_Int16();
+ if (( Event.aInfo >>= nImageType ) && nImageType == 0 )
+ RequestImages();
+}
+
+void SAL_CALL MenuBarManager::elementRemoved( const css::ui::ConfigurationEvent& Event )
+{
+ elementInserted(Event);
+}
+
+void SAL_CALL MenuBarManager::elementReplaced( const css::ui::ConfigurationEvent& Event )
+{
+ elementInserted(Event);
+}
+
+// XFrameActionListener
+void SAL_CALL MenuBarManager::frameAction( const FrameActionEvent& Action )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw css::lang::DisposedException();
+
+ if ( Action.Action != FrameAction_CONTEXT_CHANGED )
+ return;
+
+ for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
+ {
+ // Clear dispatch reference as we will requery it later
+ if ( menuItemHandler->xMenuItemDispatch.is() )
+ {
+ URL aTargetURL;
+ aTargetURL.Complete = menuItemHandler->aMenuItemURL;
+ m_xURLTransformer->parseStrict( aTargetURL );
+
+ menuItemHandler->xMenuItemDispatch->removeStatusListener( this, aTargetURL );
+ }
+ menuItemHandler->xMenuItemDispatch.clear();
+ }
+}
+
+// XStatusListener
+void SAL_CALL MenuBarManager::statusChanged( const FeatureStateEvent& Event )
+{
+ OUString aFeatureURL = Event.FeatureURL.Complete;
+
+ SolarMutexGuard aSolarGuard;
+ {
+ if ( m_bDisposed )
+ return;
+
+ // We have to check all menu entries as there can be identical entries in a popup menu.
+ for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
+ {
+ if ( menuItemHandler->aParsedItemURL == aFeatureURL )
+ {
+ bool bCheckmark( false );
+ bool bMenuItemEnabled( m_pVCLMenu->IsItemEnabled( menuItemHandler->nItemId ));
+ bool bEnabledItem( Event.IsEnabled );
+ OUString aItemText;
+ status::Visibility aVisibilityStatus;
+
+ #ifdef UNIX
+ //enable some slots hardly, because UNIX clipboard does not notify all changes
+ // Can be removed if follow up task will be fixed directly within applications.
+ // Note: PasteSpecial is handled specifically by calc
+ // Calc also disables Paste under some circumstances, do not override.
+ /* TODO: is this workaround even needed anymore? Was introduced
+ * in 2009 with commit 426ab2c0e8f6e3fe2b766f74f6b8da873d860260
+ * as some "metropatch" and the other places it touched seem to
+ * be gone. */
+ if ( (menuItemHandler->aMenuItemURL == ".uno:Paste" &&
+ m_aModuleIdentifier != "com.sun.star.sheet.SpreadsheetDocument")
+ || menuItemHandler->aMenuItemURL == ".uno:PasteClipboard" ) // special for draw/impress
+ bEnabledItem = true;
+ #endif
+
+ // Enable/disable item
+ if ( bEnabledItem != bMenuItemEnabled )
+ {
+ m_pVCLMenu->EnableItem( menuItemHandler->nItemId, bEnabledItem );
+
+ // Remove "checked" mark for disabled menu items.
+ // Initially disabled but checkable menu items do not receive
+ // checked/unchecked state, so can appear inconsistently after
+ // enabling/disabling. Since we can not pass checked state for disabled
+ // items, we will just reset checked state for them, anyway correct state
+ // will be transferred from controller once item enabled.
+ if ( !bEnabledItem && m_pVCLMenu->IsItemChecked( menuItemHandler->nItemId ) )
+ m_pVCLMenu->CheckItem( menuItemHandler->nItemId, false );
+ }
+
+ if ( Event.State >>= bCheckmark )
+ {
+ // Checkmark or RadioButton
+ m_pVCLMenu->CheckItem( menuItemHandler->nItemId, bCheckmark );
+ // If not already designated RadioButton set as CheckMark
+ MenuItemBits nBits = m_pVCLMenu->GetItemBits( menuItemHandler->nItemId );
+ if (!(nBits & MenuItemBits::RADIOCHECK))
+ m_pVCLMenu->SetItemBits( menuItemHandler->nItemId, nBits | MenuItemBits::CHECKABLE );
+
+ if ( menuItemHandler->bMadeInvisible )
+ m_pVCLMenu->ShowItem( menuItemHandler->nItemId );
+ }
+ else if ( Event.State >>= aItemText )
+ {
+ INetURLObject aURL( aFeatureURL );
+ OUString aEnumPart = aURL.GetURLPath().getToken( 1, '.' );
+ if ( !aEnumPart.isEmpty() && aURL.GetProtocol() == INetProtocol::Uno )
+ {
+ // Checkmark or RadioButton
+ m_pVCLMenu->CheckItem( menuItemHandler->nItemId, aItemText == aEnumPart );
+ // If not already designated RadioButton set as CheckMark
+ MenuItemBits nBits = m_pVCLMenu->GetItemBits( menuItemHandler->nItemId );
+ if (!(nBits & MenuItemBits::RADIOCHECK))
+ m_pVCLMenu->SetItemBits( menuItemHandler->nItemId, nBits | MenuItemBits::CHECKABLE );
+ }
+ else
+ {
+ // Replacement for place holders
+ if ( aItemText.startsWith("($1)") )
+ {
+ aItemText = FwkResId(STR_UPDATEDOC) + " " + aItemText.subView( 4 );
+ }
+ else if ( aItemText.startsWith("($2)") )
+ {
+ aItemText = FwkResId(STR_CLOSEDOC_ANDRETURN) + aItemText.subView( 4 );
+ }
+ else if ( aItemText.startsWith("($3)") )
+ {
+ aItemText = FwkResId(STR_SAVECOPYDOC) + aItemText.subView( 4 );
+ }
+
+ m_pVCLMenu->SetItemText( menuItemHandler->nItemId, aItemText );
+ }
+
+ if ( menuItemHandler->bMadeInvisible )
+ m_pVCLMenu->ShowItem( menuItemHandler->nItemId );
+ }
+ else if ( Event.State >>= aVisibilityStatus )
+ {
+ // Visibility
+ m_pVCLMenu->ShowItem( menuItemHandler->nItemId, aVisibilityStatus.bVisible );
+ menuItemHandler->bMadeInvisible = !aVisibilityStatus.bVisible;
+ }
+ else if ( menuItemHandler->bMadeInvisible )
+ m_pVCLMenu->ShowItem( menuItemHandler->nItemId );
+ }
+
+ if ( Event.Requery )
+ {
+ // Release dispatch object - will be required on the next activate!
+ menuItemHandler->xMenuItemDispatch.clear();
+ }
+ }
+ }
+}
+
+// Helper to retrieve own structure from item ID
+MenuBarManager::MenuItemHandler* MenuBarManager::GetMenuItemHandler( sal_uInt16 nItemId )
+{
+ SolarMutexGuard g;
+
+ for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
+ {
+ if ( menuItemHandler->nItemId == nItemId )
+ return menuItemHandler.get();
+ }
+
+ return nullptr;
+}
+
+// Helper to set request images flag
+void MenuBarManager::RequestImages()
+{
+
+ m_bRetrieveImages = true;
+ for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
+ {
+ if ( menuItemHandler->xSubMenuManager.is() )
+ {
+ MenuBarManager* pMenuBarManager = static_cast<MenuBarManager*>(menuItemHandler->xSubMenuManager.get());
+ pMenuBarManager->RequestImages();
+ }
+ }
+}
+
+// Helper to reset objects to prepare shutdown
+void MenuBarManager::RemoveListener()
+{
+ SolarMutexGuard g;
+
+ for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
+ {
+ if ( menuItemHandler->xMenuItemDispatch.is() )
+ {
+ URL aTargetURL;
+ aTargetURL.Complete = menuItemHandler->aMenuItemURL;
+ m_xURLTransformer->parseStrict( aTargetURL );
+
+ menuItemHandler->xMenuItemDispatch->removeStatusListener(
+ static_cast< XStatusListener* >( this ), aTargetURL );
+ }
+
+ menuItemHandler->xMenuItemDispatch.clear();
+
+ if ( menuItemHandler->xPopupMenu.is() )
+ {
+ {
+ // Remove popup menu from menu structure
+ m_pVCLMenu->SetPopupMenu( menuItemHandler->nItemId, nullptr );
+ }
+
+ Reference< css::lang::XEventListener > xEventListener( menuItemHandler->xPopupMenuController, UNO_QUERY );
+ if ( xEventListener.is() )
+ {
+ EventObject aEventObject;
+ aEventObject.Source = static_cast<OWeakObject *>(this);
+ xEventListener->disposing( aEventObject );
+ }
+
+ // We now provide a popup menu controller to external code.
+ // Therefore the life-time must be explicitly handled via
+ // dispose!!
+ try
+ {
+ Reference< XComponent > xComponent( menuItemHandler->xPopupMenuController, UNO_QUERY );
+ if ( xComponent.is() )
+ xComponent->dispose();
+ }
+ catch ( const RuntimeException& )
+ {
+ throw;
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ // Release references to controller and popup menu
+ menuItemHandler->xPopupMenuController.clear();
+ menuItemHandler->xPopupMenu.clear();
+ }
+
+ Reference< XComponent > xComponent( menuItemHandler->xSubMenuManager, UNO_QUERY );
+ if ( xComponent.is() )
+ xComponent->dispose();
+ }
+
+ try
+ {
+ if ( m_xFrame.is() )
+ m_xFrame->removeFrameActionListener( Reference< XFrameActionListener >(this) );
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ m_xFrame = nullptr;
+}
+
+void SAL_CALL MenuBarManager::disposing( const EventObject& Source )
+{
+ MenuItemHandler* pMenuItemDisposing = nullptr;
+
+ SolarMutexGuard g;
+
+ for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
+ {
+ if ( menuItemHandler->xMenuItemDispatch.is() &&
+ menuItemHandler->xMenuItemDispatch == Source.Source )
+ {
+ // disposing called from menu item dispatcher, remove listener
+ pMenuItemDisposing = menuItemHandler.get();
+ break;
+ }
+ }
+
+ if ( pMenuItemDisposing )
+ {
+ // Release references to the dispatch object
+ URL aTargetURL;
+ aTargetURL.Complete = pMenuItemDisposing->aMenuItemURL;
+
+ m_xURLTransformer->parseStrict( aTargetURL );
+
+ pMenuItemDisposing->xMenuItemDispatch->removeStatusListener(
+ static_cast< XStatusListener* >( this ), aTargetURL );
+ pMenuItemDisposing->xMenuItemDispatch.clear();
+ if ( pMenuItemDisposing->xPopupMenu.is() )
+ {
+ Reference< css::lang::XEventListener > xEventListener( pMenuItemDisposing->xPopupMenuController, UNO_QUERY );
+ if ( xEventListener.is() )
+ xEventListener->disposing( Source );
+
+ {
+ // Remove popup menu from menu structure as we release our reference to
+ // the controller.
+ m_pVCLMenu->SetPopupMenu( pMenuItemDisposing->nItemId, nullptr );
+ }
+
+ pMenuItemDisposing->xPopupMenuController.clear();
+ pMenuItemDisposing->xPopupMenu.clear();
+ }
+ return;
+ }
+ else if ( Source.Source == m_xFrame )
+ {
+ // Our frame gets disposed. We have to remove all our listeners
+ RemoveListener();
+ }
+ else if ( Source.Source == Reference< XInterface >( m_xDocImageManager, UNO_QUERY ))
+ m_xDocImageManager.clear();
+ else if ( Source.Source == Reference< XInterface >( m_xModuleImageManager, UNO_QUERY ))
+ m_xModuleImageManager.clear();
+}
+
+static void lcl_CheckForChildren(Menu* pMenu, sal_uInt16 nItemId)
+{
+ if (PopupMenu* pThisPopup = pMenu->GetPopupMenu( nItemId ))
+ pMenu->EnableItem( nItemId, pThisPopup->GetItemCount() != 0 );
+}
+
+// vcl handler
+
+namespace {
+
+class QuietInteractionContext:
+ public cppu::WeakImplHelper< css::uno::XCurrentContext >
+{
+public:
+ explicit QuietInteractionContext(
+ css::uno::Reference< css::uno::XCurrentContext > context):
+ context_(std::move(context)) {}
+ QuietInteractionContext(const QuietInteractionContext&) = delete;
+ QuietInteractionContext& operator=(const QuietInteractionContext&) = delete;
+
+private:
+ virtual ~QuietInteractionContext() override {}
+
+ virtual css::uno::Any SAL_CALL getValueByName(
+ OUString const & Name) override
+ {
+ return Name != JAVA_INTERACTION_HANDLER_NAME && context_.is()
+ ? context_->getValueByName(Name)
+ : css::uno::Any();
+ }
+
+ css::uno::Reference< css::uno::XCurrentContext >
+ context_;
+};
+
+}
+
+IMPL_LINK( MenuBarManager, Activate, Menu *, pMenu, bool )
+{
+ if ( pMenu != m_pVCLMenu )
+ return true;
+
+ css::uno::ContextLayer layer(
+ new QuietInteractionContext(
+ css::uno::getCurrentContext()));
+
+ // set/unset hiding disabled menu entries
+ bool bDontHide = officecfg::Office::Common::View::Menu::DontHideDisabledEntry::get();
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ bool bShowMenuImages = rSettings.GetUseImagesInMenus();
+ bool bShowShortcuts = m_bHasMenuBar || rSettings.GetContextMenuShortcuts();
+ bool bHasDisabledEntries = SvtCommandOptions().HasEntries( SvtCommandOptions::CMDOPTION_DISABLED );
+
+ SolarMutexGuard g;
+
+ MenuFlags nFlag = pMenu->GetMenuFlags();
+ if ( bDontHide )
+ nFlag &= ~MenuFlags::HideDisabledEntries;
+ else
+ nFlag |= MenuFlags::HideDisabledEntries;
+ pMenu->SetMenuFlags( nFlag );
+
+ if ( m_bActive )
+ return false;
+
+ m_bActive = true;
+
+ // Check if some modes have changed so we have to update our menu images
+ OUString sIconTheme = SvtMiscOptions().GetIconTheme();
+
+ if ( m_bRetrieveImages ||
+ bShowMenuImages != m_bShowMenuImages ||
+ sIconTheme != m_sIconTheme )
+ {
+ m_bShowMenuImages = bShowMenuImages;
+ m_bRetrieveImages = false;
+ m_sIconTheme = sIconTheme;
+ FillMenuImages( m_xFrame, pMenu, bShowMenuImages );
+ }
+
+ // Try to map commands to labels
+ for ( sal_uInt16 nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
+ {
+ sal_uInt16 nItemId = pMenu->GetItemId( nPos );
+ if (( pMenu->GetItemType( nPos ) != MenuItemType::SEPARATOR ) &&
+ ( pMenu->GetItemText( nItemId ).isEmpty() ))
+ {
+ OUString aCommand = pMenu->GetItemCommand( nItemId );
+ if ( !aCommand.isEmpty() ) {
+ pMenu->SetItemText( nItemId, RetrieveLabelFromCommand( aCommand ));
+ }
+ }
+ }
+
+ // Try to set accelerator keys
+ {
+ if ( bShowShortcuts )
+ RetrieveShortcuts( m_aMenuItemHandlerVector );
+
+ for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
+ {
+ if ( !bShowShortcuts )
+ {
+ pMenu->SetAccelKey( menuItemHandler->nItemId, vcl::KeyCode() );
+ }
+ else if ( menuItemHandler->aMenuItemURL == aCmdHelpIndex )
+ {
+ // Set key code, workaround for hard-coded shortcut F1 mapped to .uno:HelpIndex
+ // Only non-popup menu items can have a short-cut
+ vcl::KeyCode aKeyCode( KEY_F1 );
+ pMenu->SetAccelKey( menuItemHandler->nItemId, aKeyCode );
+ }
+ else if ( pMenu->GetPopupMenu( menuItemHandler->nItemId ) == nullptr )
+ pMenu->SetAccelKey( menuItemHandler->nItemId, menuItemHandler->aKeyCode );
+ }
+ }
+
+ URL aTargetURL;
+
+ // Use provided dispatch provider => fallback to frame as dispatch provider
+ Reference< XDispatchProvider > xDispatchProvider;
+ if ( m_xDispatchProvider.is() )
+ xDispatchProvider = m_xDispatchProvider;
+ else
+ xDispatchProvider.set( m_xFrame, UNO_QUERY );
+
+ if ( !xDispatchProvider.is() )
+ return true;
+
+ SvtCommandOptions aCmdOptions;
+ for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
+ {
+ if (menuItemHandler)
+ {
+ if ( !menuItemHandler->xMenuItemDispatch.is() &&
+ !menuItemHandler->xSubMenuManager.is() )
+ {
+ Reference< XDispatch > xMenuItemDispatch;
+
+ aTargetURL.Complete = menuItemHandler->aMenuItemURL;
+
+ m_xURLTransformer->parseStrict( aTargetURL );
+
+ if ( bHasDisabledEntries )
+ {
+ if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, aTargetURL.Path ))
+ pMenu->HideItem( menuItemHandler->nItemId );
+ }
+
+ if ( aTargetURL.Complete.startsWith( ".uno:StyleApply?" ) )
+ xMenuItemDispatch = new StyleDispatcher( m_xFrame, m_xURLTransformer, aTargetURL );
+ else
+ xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, menuItemHandler->aTargetFrame, 0 );
+
+ bool bPopupMenu( false );
+ if ( !menuItemHandler->xPopupMenuController.is() &&
+ m_xPopupMenuControllerFactory->hasController( menuItemHandler->aMenuItemURL, m_aModuleIdentifier ) )
+ {
+ if( xMenuItemDispatch.is() || menuItemHandler->aMenuItemURL != ".uno:RecentFileList" )
+ bPopupMenu = CreatePopupMenuController(menuItemHandler.get(), m_xDispatchProvider, m_aModuleIdentifier);
+ }
+ else if ( menuItemHandler->xPopupMenuController.is() )
+ {
+ // Force update of popup menu
+ menuItemHandler->xPopupMenuController->updatePopupMenu();
+ bPopupMenu = true;
+ if (PopupMenu* pThisPopup = pMenu->GetPopupMenu( menuItemHandler->nItemId ))
+ pMenu->EnableItem( menuItemHandler->nItemId, pThisPopup->GetItemCount() != 0 );
+ }
+ lcl_CheckForChildren(pMenu, menuItemHandler->nItemId);
+
+ if ( xMenuItemDispatch.is() )
+ {
+ menuItemHandler->xMenuItemDispatch = xMenuItemDispatch;
+ menuItemHandler->aParsedItemURL = aTargetURL.Complete;
+
+ if ( !bPopupMenu )
+ {
+ xMenuItemDispatch->addStatusListener( static_cast< XStatusListener* >( this ), aTargetURL );
+ // For the menubar, we have to keep status listening to support Ubuntu's HUD.
+ if ( !m_bHasMenuBar )
+ xMenuItemDispatch->removeStatusListener( static_cast< XStatusListener* >( this ), aTargetURL );
+ }
+ }
+ else if ( !bPopupMenu )
+ pMenu->EnableItem( menuItemHandler->nItemId, false );
+ }
+ else if ( menuItemHandler->xPopupMenuController.is() )
+ {
+ // Force update of popup menu
+ menuItemHandler->xPopupMenuController->updatePopupMenu();
+ lcl_CheckForChildren(pMenu, menuItemHandler->nItemId);
+ }
+ else if ( menuItemHandler->xMenuItemDispatch.is() )
+ {
+ // We need an update to reflect the current state
+ try
+ {
+ aTargetURL.Complete = menuItemHandler->aMenuItemURL;
+ m_xURLTransformer->parseStrict( aTargetURL );
+
+ menuItemHandler->xMenuItemDispatch->addStatusListener(
+ static_cast< XStatusListener* >( this ), aTargetURL );
+ menuItemHandler->xMenuItemDispatch->removeStatusListener(
+ static_cast< XStatusListener* >( this ), aTargetURL );
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+ else if ( menuItemHandler->xSubMenuManager.is() )
+ lcl_CheckForChildren(pMenu, menuItemHandler->nItemId);
+ }
+ }
+
+ return true;
+}
+
+IMPL_LINK( MenuBarManager, Deactivate, Menu *, pMenu, bool )
+{
+ if ( pMenu == m_pVCLMenu )
+ {
+ m_bActive = false;
+ if ( pMenu->IsMenuBar() && m_xDeferredItemContainer.is() )
+ {
+ // Start timer to handle settings asynchronous
+ // Changing the menu inside this handler leads to
+ // a crash under X!
+ m_aAsyncSettingsTimer.SetInvokeHandler(LINK(this, MenuBarManager, AsyncSettingsHdl));
+ m_aAsyncSettingsTimer.SetTimeout(10);
+ m_aAsyncSettingsTimer.Start();
+ }
+ }
+
+ return true;
+}
+
+IMPL_LINK_NOARG( MenuBarManager, AsyncSettingsHdl, Timer*, void)
+{
+ SolarMutexGuard g;
+ Reference< XInterface > xSelfHold(
+ static_cast< ::cppu::OWeakObject* >( this ), UNO_QUERY_THROW );
+
+ m_aAsyncSettingsTimer.Stop();
+ if ( !m_bActive && m_xDeferredItemContainer.is() )
+ {
+ SetItemContainer( m_xDeferredItemContainer );
+ m_xDeferredItemContainer.clear();
+ }
+}
+
+IMPL_LINK( MenuBarManager, Select, Menu *, pMenu, bool )
+{
+ URL aTargetURL;
+ Sequence<PropertyValue> aArgs;
+ Reference< XDispatch > xDispatch;
+
+ {
+ SolarMutexGuard g;
+
+ sal_uInt16 nCurItemId = pMenu->GetCurItemId();
+ sal_uInt16 nCurPos = pMenu->GetItemPos( nCurItemId );
+ if ( pMenu == m_pVCLMenu &&
+ pMenu->GetItemType( nCurPos ) != MenuItemType::SEPARATOR )
+ {
+ MenuItemHandler* pMenuItemHandler = GetMenuItemHandler( nCurItemId );
+ if ( pMenuItemHandler && pMenuItemHandler->xMenuItemDispatch.is() )
+ {
+ aTargetURL.Complete = pMenuItemHandler->aMenuItemURL;
+ m_xURLTransformer->parseStrict( aTargetURL );
+
+ if ( pMenu->GetUserValue( nCurItemId ) )
+ {
+ // addon menu item selected
+ aArgs = { comphelper::makePropertyValue("Referer", OUString("private:user")) };
+ }
+
+ xDispatch = pMenuItemHandler->xMenuItemDispatch;
+ }
+ }
+ }
+
+ // tdf#126054 don't let dispatch destroy this until after function completes
+ rtl::Reference<MenuBarManager> xKeepAlive(this);
+ if (xDispatch.is())
+ {
+ SolarMutexReleaser aReleaser;
+ xDispatch->dispatch( aTargetURL, aArgs );
+ }
+
+ if ( !m_bHasMenuBar )
+ // Standalone (non-native) popup menu doesn't fire deactivate event
+ // in this case, so we have to reset the active flag here.
+ m_bActive = false;
+
+ return true;
+}
+
+bool MenuBarManager::MustBeHidden( PopupMenu* pPopupMenu, const Reference< XURLTransformer >& rTransformer )
+{
+ if ( !pPopupMenu )
+ return true;
+
+ URL aTargetURL;
+ SvtCommandOptions aCmdOptions;
+
+ sal_uInt16 nCount = pPopupMenu->GetItemCount();
+ sal_uInt16 nHideCount( 0 );
+
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ sal_uInt16 nId = pPopupMenu->GetItemId( i );
+ if ( nId > 0 )
+ {
+ PopupMenu* pSubPopupMenu = pPopupMenu->GetPopupMenu( nId );
+ if ( pSubPopupMenu )
+ {
+ if ( MustBeHidden( pSubPopupMenu, rTransformer ))
+ {
+ pPopupMenu->HideItem( nId );
+ ++nHideCount;
+ }
+ }
+ else
+ {
+ aTargetURL.Complete = pPopupMenu->GetItemCommand( nId );
+ rTransformer->parseStrict( aTargetURL );
+
+ if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, aTargetURL.Path ))
+ ++nHideCount;
+ }
+ }
+ else
+ ++nHideCount;
+ }
+
+ return ( nCount == nHideCount );
+}
+
+OUString MenuBarManager::RetrieveLabelFromCommand(const OUString& rCmdURL)
+{
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCmdURL, m_aModuleIdentifier);
+ if ( !m_bHasMenuBar )
+ {
+ // This is a context menu, prefer "PopupLabel" over "Label".
+ return vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties);
+ }
+ return vcl::CommandInfoProvider::GetMenuLabelForCommand(aProperties);
+}
+
+bool MenuBarManager::CreatePopupMenuController( MenuItemHandler* pMenuItemHandler,
+ const css::uno::Reference< css::frame::XDispatchProvider >& rDispatchProvider,
+ const OUString& rModuleIdentifier )
+{
+ OUString aItemCommand( pMenuItemHandler->aMenuItemURL );
+
+ // Try instantiate a popup menu controller. It is stored in the menu item handler.
+ if ( !m_xPopupMenuControllerFactory.is() )
+ return false;
+
+ auto aSeq( comphelper::InitAnyPropertySequence( {
+ { "DispatchProvider", Any(rDispatchProvider) },
+ { "ModuleIdentifier", Any(rModuleIdentifier) },
+ { "Frame", Any(m_xFrame) },
+ { "InToolbar", Any(!m_bHasMenuBar) }
+ } ) );
+
+ Reference< XPopupMenuController > xPopupMenuController(
+ m_xPopupMenuControllerFactory->createInstanceWithArgumentsAndContext(
+ aItemCommand,
+ aSeq,
+ m_xContext ),
+ UNO_QUERY );
+
+ if ( xPopupMenuController.is() )
+ {
+ // Provide our awt popup menu to the popup menu controller
+ pMenuItemHandler->xPopupMenuController = xPopupMenuController;
+ xPopupMenuController->setPopupMenu( pMenuItemHandler->xPopupMenu );
+ return true;
+ }
+
+ return false;
+}
+
+void MenuBarManager::FillMenuManager( Menu* pMenu, const Reference< XFrame >& rFrame,
+ const Reference< XDispatchProvider >& rDispatchProvider,
+ const OUString& rModuleIdentifier, bool bDelete )
+{
+ m_xFrame = rFrame;
+ m_bActive = false;
+ m_bDeleteMenu = bDelete;
+ m_pVCLMenu = pMenu;
+ m_xDispatchProvider = rDispatchProvider;
+
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ m_bShowMenuImages = rSettings.GetUseImagesInMenus();
+ m_bRetrieveImages = false;
+
+ // Set module identifier when provided from outside
+ if (!rModuleIdentifier.isEmpty())
+ m_aModuleIdentifier = rModuleIdentifier;
+ else
+ m_aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier(m_xFrame);
+
+ // Add root as ui configuration listener
+ RetrieveImageManagers();
+
+ if ( pMenu->IsMenuBar() && rFrame.is() )
+ {
+ // First merge all addon popup menus into our structure
+ sal_uInt16 nPos = 0;
+ for ( nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
+ {
+ sal_uInt16 nItemId = pMenu->GetItemId( nPos );
+ OUString aCommand = pMenu->GetItemCommand( nItemId );
+ if ( aCommand == aSpecialWindowCommand || aCommand == aCmdHelpMenu )
+ {
+ // Retrieve addon popup menus and add them to our menu bar
+ framework::AddonMenuManager::MergeAddonPopupMenus( rFrame, nPos, static_cast<MenuBar *>(pMenu) );
+ break;
+ }
+ }
+
+ // Merge the Add-Ons help menu items into the Office help menu
+ framework::AddonMenuManager::MergeAddonHelpMenu( rFrame, static_cast<MenuBar *>(pMenu) );
+ }
+
+ bool bAccessibilityEnabled( Application::GetSettings().GetMiscSettings().GetEnableATToolSupport() );
+ sal_uInt16 nItemCount = pMenu->GetItemCount();
+ OUString aItemCommand;
+ m_aMenuItemHandlerVector.reserve(nItemCount);
+ for ( sal_uInt16 i = 0; i < nItemCount; i++ )
+ {
+ sal_uInt16 nItemId = FillItemCommand(aItemCommand,pMenu, i );
+
+ if (( pMenu->IsMenuBar() || bAccessibilityEnabled ) &&
+ ( pMenu->GetItemText( nItemId ).isEmpty() ))
+ {
+ if ( !aItemCommand.isEmpty() )
+ pMenu->SetItemText( nItemId, RetrieveLabelFromCommand( aItemCommand ));
+ }
+
+ // Command can be just an alias to another command.
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aItemCommand, m_aModuleIdentifier);
+ OUString aRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties);
+ if ( !aRealCommand.isEmpty() )
+ aItemCommand = aRealCommand;
+
+ Reference< XDispatch > xDispatch;
+ Reference< XStatusListener > xStatusListener;
+ VclPtr<PopupMenu> pPopup = pMenu->GetPopupMenu( nItemId );
+ // overwrite the show icons on menu option?
+ MenuItemBits nBits = pMenu->GetItemBits( nItemId ) & ( MenuItemBits::ICON | MenuItemBits::TEXT );
+ bool bItemShowMenuImages = ( m_bShowMenuImages && nBits != MenuItemBits::TEXT ) || nBits & MenuItemBits::ICON;
+
+ if ( pPopup )
+ {
+ // Retrieve module identifier from Help Command entry
+ OUString aModuleIdentifier( rModuleIdentifier );
+ if (!pMenu->GetHelpCommand(nItemId).isEmpty())
+ {
+ aModuleIdentifier = pMenu->GetHelpCommand( nItemId );
+ pMenu->SetHelpCommand( nItemId, "" );
+ }
+
+ // Retrieve possible attributes struct
+ Reference< XDispatchProvider > xPopupMenuDispatchProvider( rDispatchProvider );
+ MenuAttributes* pAttributes = static_cast<MenuAttributes *>(pMenu->GetUserValue( nItemId ));
+ if ( pAttributes )
+ xPopupMenuDispatchProvider = pAttributes->xDispatchProvider;
+
+ if ( m_xPopupMenuControllerFactory.is() &&
+ m_xPopupMenuControllerFactory->hasController( aItemCommand, aModuleIdentifier )
+ )
+ {
+ // Check if we have to create a popup menu for a uno based popup menu controller.
+ // We have to set an empty popup menu into our menu structure so the controller also
+ // works with inplace OLE.
+ MenuItemHandler* pItemHandler = new MenuItemHandler( nItemId, xStatusListener, xDispatch );
+ rtl::Reference<VCLXPopupMenu> pVCLXPopupMenu = new VCLXPopupMenu(pPopup);
+ pItemHandler->xPopupMenu = pVCLXPopupMenu;
+ pItemHandler->aMenuItemURL = aItemCommand;
+ m_aMenuItemHandlerVector.push_back( std::unique_ptr<MenuItemHandler>(pItemHandler) );
+
+ if ( bAccessibilityEnabled || pMenu->IsMenuBar())
+ {
+ if ( CreatePopupMenuController( pItemHandler, xPopupMenuDispatchProvider, aModuleIdentifier ))
+ pItemHandler->xPopupMenuController->updatePopupMenu();
+ }
+ lcl_CheckForChildren(pMenu, nItemId);
+ }
+ else
+ {
+ // Check if this is the tools menu. Add menu item if needed
+ if ( aItemCommand == aCmdToolsMenu && AddonMenuManager::HasAddonMenuElements() )
+ {
+ // Create addon popup menu if there exist elements and this is the tools popup menu
+ VclPtr<PopupMenu> pSubMenu = AddonMenuManager::CreateAddonMenu(rFrame);
+ if ( pSubMenu && ( pSubMenu->GetItemCount() > 0 ))
+ {
+ if ( pPopup->GetItemType( pPopup->GetItemCount() - 1 ) != MenuItemType::SEPARATOR )
+ pPopup->InsertSeparator();
+
+ pPopup->InsertItem( ITEMID_ADDONLIST, OUString() );
+ pPopup->SetPopupMenu( ITEMID_ADDONLIST, pSubMenu );
+ pPopup->SetItemCommand( ITEMID_ADDONLIST, ".uno:Addons" );
+ }
+ else
+ pSubMenu.disposeAndClear();
+ }
+
+ rtl::Reference<MenuBarManager> pSubMenuManager = new MenuBarManager( m_xContext, rFrame, m_xURLTransformer,
+ xPopupMenuDispatchProvider, aModuleIdentifier, pPopup, false, m_bHasMenuBar );
+
+ AddMenu(pSubMenuManager.get(), aItemCommand, nItemId);
+ }
+ }
+ else if ( pMenu->GetItemType( i ) != MenuItemType::SEPARATOR )
+ {
+ if ( bItemShowMenuImages )
+ m_bRetrieveImages = true;
+
+ std::unique_ptr<MenuItemHandler> pItemHandler(new MenuItemHandler( nItemId, xStatusListener, xDispatch ));
+ // Retrieve possible attributes struct
+ MenuAttributes* pAttributes = static_cast<MenuAttributes *>(pMenu->GetUserValue( nItemId ));
+ if ( pAttributes )
+ pItemHandler->aTargetFrame = pAttributes->aTargetFrame;
+ pItemHandler->aMenuItemURL = aItemCommand;
+
+ if ( m_xPopupMenuControllerFactory.is() &&
+ m_xPopupMenuControllerFactory->hasController( aItemCommand, m_aModuleIdentifier ) )
+ {
+ // Check if we have to create a popup menu for a uno based popup menu controller.
+ // We have to set an empty popup menu into our menu structure so the controller also
+ // works with inplace OLE.
+ rtl::Reference<VCLXPopupMenu> pVCLXPopupMenu = new VCLXPopupMenu;
+ PopupMenu* pPopupMenu = static_cast<PopupMenu *>(pVCLXPopupMenu->GetMenu());
+ pMenu->SetPopupMenu( pItemHandler->nItemId, pPopupMenu );
+ pItemHandler->xPopupMenu = pVCLXPopupMenu;
+
+ if ( bAccessibilityEnabled && CreatePopupMenuController( pItemHandler.get(), m_xDispatchProvider, m_aModuleIdentifier ) )
+ {
+ pItemHandler->xPopupMenuController->updatePopupMenu();
+ }
+
+ lcl_CheckForChildren(pMenu, pItemHandler->nItemId);
+ }
+
+ m_aMenuItemHandlerVector.push_back( std::move(pItemHandler) );
+ }
+ }
+
+ if ( m_bHasMenuBar && bAccessibilityEnabled )
+ {
+ RetrieveShortcuts( m_aMenuItemHandlerVector );
+ for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
+ {
+ // Set key code, workaround for hard-coded shortcut F1 mapped to .uno:HelpIndex
+ // Only non-popup menu items can have a short-cut
+ if ( menuItemHandler->aMenuItemURL == aCmdHelpIndex )
+ {
+ vcl::KeyCode aKeyCode( KEY_F1 );
+ pMenu->SetAccelKey( menuItemHandler->nItemId, aKeyCode );
+ }
+ else if ( pMenu->GetPopupMenu( menuItemHandler->nItemId ) == nullptr )
+ pMenu->SetAccelKey( menuItemHandler->nItemId, menuItemHandler->aKeyCode );
+ }
+ }
+
+ SetHdl();
+}
+
+void MenuBarManager::impl_RetrieveShortcutsFromConfiguration(
+ const Reference< XAcceleratorConfiguration >& rAccelCfg,
+ const Sequence< OUString >& rCommands,
+ std::vector< std::unique_ptr<MenuItemHandler> >& aMenuShortCuts )
+{
+ if ( !rAccelCfg.is() )
+ return;
+
+ try
+ {
+ css::awt::KeyEvent aKeyEvent;
+ Sequence< Any > aSeqKeyCode = rAccelCfg->getPreferredKeyEventsForCommandList( rCommands );
+ for ( sal_Int32 i = 0; i < aSeqKeyCode.getLength(); i++ )
+ {
+ if ( aSeqKeyCode[i] >>= aKeyEvent )
+ aMenuShortCuts[i]->aKeyCode = svt::AcceleratorExecute::st_AWTKey2VCLKey( aKeyEvent );
+ }
+ }
+ catch ( const IllegalArgumentException& )
+ {
+ }
+}
+
+void MenuBarManager::RetrieveShortcuts( std::vector< std::unique_ptr<MenuItemHandler> >& aMenuShortCuts )
+{
+ Reference< XAcceleratorConfiguration > xDocAccelCfg( m_xDocAcceleratorManager );
+ Reference< XAcceleratorConfiguration > xModuleAccelCfg( m_xModuleAcceleratorManager );
+ Reference< XAcceleratorConfiguration > xGlobalAccelCfg( m_xGlobalAcceleratorManager );
+
+ if ( !m_bAcceleratorCfg )
+ {
+ // Retrieve references on demand
+ m_bAcceleratorCfg = true;
+ if ( !xDocAccelCfg.is() )
+ {
+ Reference< XController > xController = m_xFrame->getController();
+ Reference< XModel > xModel;
+ if ( xController.is() )
+ {
+ xModel = xController->getModel();
+ if ( xModel.is() )
+ {
+ Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY );
+ if ( xSupplier.is() )
+ {
+ Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager();
+ if ( xDocUICfgMgr.is() )
+ {
+ xDocAccelCfg = xDocUICfgMgr->getShortCutManager();
+ m_xDocAcceleratorManager = xDocAccelCfg;
+ }
+ }
+ }
+ }
+ }
+
+ if ( !xModuleAccelCfg.is() )
+ {
+ Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier =
+ theModuleUIConfigurationManagerSupplier::get( m_xContext );
+ try
+ {
+ Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier );
+ if ( xUICfgMgr.is() )
+ {
+ xModuleAccelCfg = xUICfgMgr->getShortCutManager();
+ m_xModuleAcceleratorManager = xModuleAccelCfg;
+ }
+ }
+ catch ( const RuntimeException& )
+ {
+ throw;
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+
+ if ( !xGlobalAccelCfg.is() ) try
+ {
+ xGlobalAccelCfg = GlobalAcceleratorConfiguration::create( m_xContext );
+ m_xGlobalAcceleratorManager = xGlobalAccelCfg;
+ }
+ catch ( const css::uno::DeploymentException& )
+ {
+ SAL_WARN("fwk.uielement", "GlobalAcceleratorConfiguration"
+ " not available. This should happen only on mobile platforms.");
+ }
+ }
+
+ vcl::KeyCode aEmptyKeyCode;
+ Sequence< OUString > aSeq( aMenuShortCuts.size() );
+ auto aSeqRange = asNonConstRange(aSeq);
+ const sal_uInt32 nCount = aMenuShortCuts.size();
+ for ( sal_uInt32 i = 0; i < nCount; ++i )
+ {
+ aSeqRange[i] = aMenuShortCuts[i]->aMenuItemURL;
+ aMenuShortCuts[i]->aKeyCode = aEmptyKeyCode;
+ }
+
+ if ( m_xGlobalAcceleratorManager.is() )
+ impl_RetrieveShortcutsFromConfiguration( xGlobalAccelCfg, aSeq, aMenuShortCuts );
+ if ( m_xModuleAcceleratorManager.is() )
+ impl_RetrieveShortcutsFromConfiguration( xModuleAccelCfg, aSeq, aMenuShortCuts );
+ if ( m_xDocAcceleratorManager.is() )
+ impl_RetrieveShortcutsFromConfiguration( xDocAccelCfg, aSeq, aMenuShortCuts );
+}
+
+void MenuBarManager::RetrieveImageManagers()
+{
+ if ( !m_xDocImageManager.is() )
+ {
+ Reference< XController > xController = m_xFrame->getController();
+ Reference< XModel > xModel;
+ if ( xController.is() )
+ {
+ xModel = xController->getModel();
+ if ( xModel.is() )
+ {
+ Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY );
+ if ( xSupplier.is() )
+ {
+ Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager();
+ m_xDocImageManager.set( xDocUICfgMgr->getImageManager(), UNO_QUERY );
+ m_xDocImageManager->addConfigurationListener(
+ Reference< XUIConfigurationListener >(this) );
+ }
+ }
+ }
+ }
+
+ if ( !m_xModuleImageManager.is() )
+ {
+ Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier =
+ theModuleUIConfigurationManagerSupplier::get( m_xContext );
+ Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier );
+ m_xModuleImageManager.set( xUICfgMgr->getImageManager(), UNO_QUERY );
+ m_xModuleImageManager->addConfigurationListener( Reference< XUIConfigurationListener >(this) );
+ }
+}
+
+void MenuBarManager::FillMenuWithConfiguration(
+ sal_uInt16& nId,
+ Menu* pMenu,
+ const OUString& rModuleIdentifier,
+ const Reference< XIndexAccess >& rItemContainer,
+ const Reference< XURLTransformer >& rTransformer )
+{
+ Reference< XDispatchProvider > xEmptyDispatchProvider;
+ MenuBarManager::FillMenu( nId, pMenu, rModuleIdentifier, rItemContainer, xEmptyDispatchProvider );
+
+ // Merge add-on menu entries into the menu bar
+ MenuBarManager::MergeAddonMenus( pMenu,
+ AddonsOptions().GetMergeMenuInstructions(),
+ rModuleIdentifier );
+
+ bool bHasDisabledEntries = SvtCommandOptions().HasEntries( SvtCommandOptions::CMDOPTION_DISABLED );
+ if ( !bHasDisabledEntries )
+ return;
+
+ sal_uInt16 nCount = pMenu->GetItemCount();
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ sal_uInt16 nID = pMenu->GetItemId( i );
+ if ( nID > 0 )
+ {
+ PopupMenu* pPopupMenu = pMenu->GetPopupMenu( nID );
+ if ( pPopupMenu )
+ {
+ if ( MustBeHidden( pPopupMenu, rTransformer ))
+ pMenu->HideItem( nId );
+ }
+ }
+ }
+}
+
+void MenuBarManager::FillMenu(
+ sal_uInt16& nId,
+ Menu* pMenu,
+ const OUString& rModuleIdentifier,
+ const Reference< XIndexAccess >& rItemContainer,
+ const Reference< XDispatchProvider >& rDispatchProvider )
+{
+ // Fill menu bar with container contents
+ for ( sal_Int32 n = 0; n < rItemContainer->getCount(); n++ )
+ {
+ Sequence< PropertyValue > aProps;
+ OUString aCommandURL;
+ OUString aLabel;
+ OUString aModuleIdentifier( rModuleIdentifier );
+ sal_uInt16 nType = 0;
+ Reference< XIndexAccess > xIndexContainer;
+ Reference< XDispatchProvider > xDispatchProvider( rDispatchProvider );
+ sal_Int16 nStyle = 0;
+ try
+ {
+ if ( rItemContainer->getByIndex( n ) >>= aProps )
+ {
+ bool bShow = true;
+ bool bEnabled = true;
+
+ for ( beans::PropertyValue const & rProp : std::as_const(aProps) )
+ {
+ OUString aPropName = rProp.Name;
+ if ( aPropName == "CommandURL" )
+ rProp.Value >>= aCommandURL;
+ else if ( aPropName == "ItemDescriptorContainer" )
+ rProp.Value >>= xIndexContainer;
+ else if ( aPropName == "Label" )
+ rProp.Value >>= aLabel;
+ else if ( aPropName == "Type" )
+ rProp.Value >>= nType;
+ else if ( aPropName == "ModuleIdentifier" )
+ rProp.Value >>= aModuleIdentifier;
+ else if ( aPropName == "DispatchProvider" )
+ rProp.Value >>= xDispatchProvider;
+ else if ( aPropName == "Style" )
+ rProp.Value >>= nStyle;
+ else if ( aPropName == "IsVisible" )
+ rProp.Value >>= bShow;
+ else if ( aPropName == "Enabled" )
+ rProp.Value >>= bEnabled;
+ }
+
+ if (!aCommandURL.isEmpty() && vcl::CommandInfoProvider::IsExperimental(aCommandURL, rModuleIdentifier) &&
+ !officecfg::Office::Common::Misc::ExperimentalMode::get())
+ {
+ continue;
+ }
+
+ if ( nType == css::ui::ItemType::DEFAULT )
+ {
+ pMenu->InsertItem( nId, aLabel );
+ pMenu->SetItemCommand( nId, aCommandURL );
+
+ if ( nStyle )
+ {
+ MenuItemBits nBits = pMenu->GetItemBits( nId );
+ if ( nStyle & css::ui::ItemStyle::ICON )
+ nBits |= MenuItemBits::ICON;
+ if ( nStyle & css::ui::ItemStyle::TEXT )
+ nBits |= MenuItemBits::TEXT;
+ if ( nStyle & css::ui::ItemStyle::RADIO_CHECK )
+ nBits |= MenuItemBits::RADIOCHECK;
+ pMenu->SetItemBits( nId, nBits );
+ }
+
+ if ( !bShow )
+ pMenu->HideItem( nId );
+
+ if ( !bEnabled)
+ pMenu->EnableItem( nId, false );
+
+ if ( xIndexContainer.is() )
+ {
+ VclPtr<PopupMenu> pNewPopupMenu = VclPtr<PopupMenu>::Create();
+ pMenu->SetPopupMenu( nId, pNewPopupMenu );
+
+ if ( xDispatchProvider.is() )
+ {
+ // Use attributes struct to transport special dispatch provider
+ void* nAttributePtr = MenuAttributes::CreateAttribute(xDispatchProvider);
+ pMenu->SetUserValue(nId, nAttributePtr, MenuAttributes::ReleaseAttribute);
+ }
+
+ // Use help command to transport module identifier
+ if ( !aModuleIdentifier.isEmpty() )
+ pMenu->SetHelpCommand( nId, aModuleIdentifier );
+
+ ++nId;
+ FillMenu( nId, pNewPopupMenu, aModuleIdentifier, xIndexContainer, xDispatchProvider );
+ }
+ else
+ ++nId;
+ }
+ else
+ {
+ pMenu->InsertSeparator();
+ ++nId;
+ }
+ }
+ }
+ catch ( const IndexOutOfBoundsException& )
+ {
+ break;
+ }
+ }
+}
+
+void MenuBarManager::MergeAddonMenus(
+ Menu* pMenuBar,
+ const MergeMenuInstructionContainer& aMergeInstructionContainer,
+ const OUString& rModuleIdentifier )
+{
+ // set start value for the item ID for the new addon menu items
+ sal_uInt16 nItemId = ADDONMENU_MERGE_ITEMID_START;
+
+ const sal_uInt32 nCount = aMergeInstructionContainer.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ const MergeMenuInstruction& rMergeInstruction = aMergeInstructionContainer[i];
+
+ if ( MenuBarMerger::IsCorrectContext( rMergeInstruction.aMergeContext, rModuleIdentifier ))
+ {
+ ::std::vector< OUString > aMergePath;
+
+ // retrieve the merge path from the merge point string
+ MenuBarMerger::RetrieveReferencePath( rMergeInstruction.aMergePoint, aMergePath );
+
+ // convert the sequence/sequence property value to a more convenient vector<>
+ AddonMenuContainer aMergeMenuItems;
+ MenuBarMerger::GetSubMenu( rMergeInstruction.aMergeMenu, aMergeMenuItems );
+
+ // try to find the reference point for our merge operation
+ Menu* pMenu = pMenuBar;
+ ReferencePathInfo aResult = MenuBarMerger::FindReferencePath( aMergePath, pMenu );
+
+ if ( aResult.eResult == RP_OK )
+ {
+ // normal merge operation
+ MenuBarMerger::ProcessMergeOperation( aResult.pPopupMenu,
+ aResult.nPos,
+ nItemId,
+ rMergeInstruction.aMergeCommand,
+ rMergeInstruction.aMergeCommandParameter,
+ rModuleIdentifier,
+ aMergeMenuItems );
+ }
+ else
+ {
+ // fallback
+ MenuBarMerger::ProcessFallbackOperation( aResult,
+ nItemId,
+ rMergeInstruction.aMergeCommand,
+ rMergeInstruction.aMergeFallback,
+ aMergePath,
+ rModuleIdentifier,
+ aMergeMenuItems );
+ }
+ }
+ }
+}
+
+void MenuBarManager::SetItemContainer( const Reference< XIndexAccess >& rItemContainer )
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ Reference< XFrame > xFrame = m_xFrame;
+
+ // Clear MenuBarManager structures
+ {
+ // Check active state as we cannot change our VCL menu during activation by the user
+ if ( m_bActive )
+ {
+ m_xDeferredItemContainer = rItemContainer;
+ return;
+ }
+
+ RemoveListener();
+ m_aMenuItemHandlerVector.clear();
+ m_pVCLMenu->Clear();
+
+ sal_uInt16 nId = 1;
+
+ // Fill menu bar with container contents
+ FillMenuWithConfiguration( nId, m_pVCLMenu, m_aModuleIdentifier, rItemContainer, m_xURLTransformer );
+
+ // Refill menu manager again
+ Reference< XDispatchProvider > xDispatchProvider;
+ FillMenuManager( m_pVCLMenu, xFrame, xDispatchProvider, m_aModuleIdentifier, false );
+
+ // add itself as frame action listener
+ m_xFrame->addFrameActionListener( Reference< XFrameActionListener >(this) );
+ }
+}
+
+void MenuBarManager::GetPopupController( PopupControllerCache& rPopupController )
+{
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
+ {
+ if ( menuItemHandler->xPopupMenuController.is() )
+ {
+ Reference< XDispatchProvider > xDispatchProvider( menuItemHandler->xPopupMenuController, UNO_QUERY );
+
+ PopupControllerEntry aPopupControllerEntry;
+ aPopupControllerEntry.m_xDispatchProvider = xDispatchProvider;
+
+ // Just use the main part of the URL for popup menu controllers
+ sal_Int32 nSchemePart( 0 );
+ OUString aMenuURL( menuItemHandler->aMenuItemURL );
+
+ nSchemePart = aMenuURL.indexOf( ':' );
+ if (( nSchemePart > 0 ) &&
+ ( aMenuURL.getLength() > ( nSchemePart+1 )))
+ {
+ OUString aMainURL( "vnd.sun.star.popup:" );
+ sal_Int32 nQueryPart = aMenuURL.indexOf( '?', nSchemePart );
+ if ( nQueryPart > 0 )
+ aMainURL += aMenuURL.subView( nSchemePart, nQueryPart-nSchemePart );
+ else if ( nQueryPart == -1 )
+ aMainURL += aMenuURL.subView( nSchemePart+1 );
+
+ rPopupController.emplace( aMainURL, aPopupControllerEntry );
+ }
+ }
+ if ( menuItemHandler->xSubMenuManager.is() )
+ {
+ MenuBarManager* pMenuBarManager = static_cast<MenuBarManager*>(menuItemHandler->xSubMenuManager.get());
+ if ( pMenuBarManager )
+ pMenuBarManager->GetPopupController( rPopupController );
+ }
+ }
+}
+
+void MenuBarManager::AddMenu(MenuBarManager* pSubMenuManager,const OUString& _sItemCommand,sal_uInt16 _nItemId)
+{
+ Reference< XStatusListener > xSubMenuManager( pSubMenuManager );
+ m_xFrame->addFrameActionListener( Reference< XFrameActionListener >( xSubMenuManager, UNO_QUERY ));
+
+ Reference< XDispatch > xDispatch;
+ std::unique_ptr<MenuItemHandler> pMenuItemHandler(new MenuItemHandler(
+ _nItemId,
+ xSubMenuManager,
+ xDispatch ));
+ pMenuItemHandler->aMenuItemURL = _sItemCommand;
+ m_aMenuItemHandlerVector.push_back( std::move(pMenuItemHandler) );
+}
+
+sal_uInt16 MenuBarManager::FillItemCommand(OUString& _rItemCommand, Menu* _pMenu,sal_uInt16 _nIndex) const
+{
+ sal_uInt16 nItemId = _pMenu->GetItemId( _nIndex );
+
+ _rItemCommand = _pMenu->GetItemCommand( nItemId );
+ if ( _rItemCommand.isEmpty() )
+ {
+ _rItemCommand = "slot:" + OUString::number( nItemId );
+ _pMenu->SetItemCommand( nItemId, _rItemCommand );
+ }
+ return nItemId;
+}
+
+void MenuBarManager::SetHdl()
+{
+ m_pVCLMenu->SetActivateHdl( LINK( this, MenuBarManager, Activate ));
+ m_pVCLMenu->SetDeactivateHdl( LINK( this, MenuBarManager, Deactivate ));
+ m_pVCLMenu->SetSelectHdl( LINK( this, MenuBarManager, Select ));
+
+ if ( !m_xURLTransformer.is() && m_xContext.is() )
+ m_xURLTransformer.set( URLTransformer::create( m_xContext) );
+}
+
+void MenuBarManager::FillMenuImages(Reference< XFrame > const & _xFrame, Menu* _pMenu,bool bShowMenuImages)
+{
+ AddonsOptions aAddonOptions;
+
+ for ( sal_uInt16 nPos = 0; nPos < _pMenu->GetItemCount(); nPos++ )
+ {
+ sal_uInt16 nId = _pMenu->GetItemId( nPos );
+ if ( _pMenu->GetItemType( nPos ) != MenuItemType::SEPARATOR )
+ {
+ // overwrite the show icons on menu option?
+ MenuItemBits nBits = _pMenu->GetItemBits( nId ) & ( MenuItemBits::ICON | MenuItemBits::TEXT );
+ bool bTmpShowMenuImages = ( bShowMenuImages && nBits != MenuItemBits::TEXT ) || nBits & MenuItemBits::ICON;
+
+ if ( bTmpShowMenuImages )
+ {
+ OUString aMenuItemCommand = _pMenu->GetItemCommand( nId );
+ Image aImage = vcl::CommandInfoProvider::GetImageForCommand(aMenuItemCommand, _xFrame);
+ if ( !aImage )
+ aImage = Image(aAddonOptions.GetImageFromURL(aMenuItemCommand, false));
+
+ _pMenu->SetItemImage( nId, aImage );
+ }
+ else
+ _pMenu->SetItemImage( nId, Image() );
+ }
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/menubarmerger.cxx b/framework/source/uielement/menubarmerger.cxx
new file mode 100644
index 000000000..c5730d2ef
--- /dev/null
+++ b/framework/source/uielement/menubarmerger.cxx
@@ -0,0 +1,430 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/menubarmerger.hxx>
+#include <framework/addonsoptions.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::com::sun::star;
+
+const char SEPARATOR_STRING[] = "private:separator";
+
+const char16_t MERGECOMMAND_ADDAFTER[] = u"AddAfter";
+const char16_t MERGECOMMAND_ADDBEFORE[] = u"AddBefore";
+const char16_t MERGECOMMAND_REPLACE[] = u"Replace";
+const char16_t MERGECOMMAND_REMOVE[] = u"Remove";
+
+const char16_t MERGEFALLBACK_ADDPATH[] = u"AddPath";
+const char16_t MERGEFALLBACK_IGNORE[] = u"Ignore";
+
+namespace framework
+{
+
+/**
+ Check whether a module identifier is part of a context
+ defined by a colon separated list of module identifier.
+
+ @param
+ rContext
+
+ Describes a context string list where all contexts
+ are delimited by a colon. For more information about
+ the module identifier used as context strings see the
+ IDL description of css::frame::XModuleManager
+
+ @param
+ rModuleIdentifier
+
+ A string describing a module identifier. See IDL
+ description of css::frame::XModuleManager.
+
+*/
+bool MenuBarMerger::IsCorrectContext(
+ std::u16string_view rContext, std::u16string_view rModuleIdentifier )
+{
+ return ( rContext.empty() || ( rContext.find( rModuleIdentifier ) != std::u16string_view::npos ));
+}
+
+void MenuBarMerger::RetrieveReferencePath(
+ std::u16string_view rReferencePathString,
+ ::std::vector< OUString >& rReferencePath )
+{
+ const char aDelimiter = '\\';
+
+ rReferencePath.clear();
+ sal_Int32 nIndex( 0 );
+ do
+ {
+ OUString aToken( o3tl::getToken(rReferencePathString, 0, aDelimiter, nIndex ) );
+ if ( !aToken.isEmpty() )
+ rReferencePath.push_back( aToken );
+ }
+ while ( nIndex >= 0 );
+}
+
+ReferencePathInfo MenuBarMerger::FindReferencePath(
+ const ::std::vector< OUString >& rReferencePath,
+ Menu* pMenu )
+{
+ sal_uInt32 i( 0 );
+ const sal_uInt32 nCount( rReferencePath.size() );
+
+ ReferencePathInfo aResult;
+ if ( !nCount )
+ {
+ aResult.pPopupMenu = nullptr;
+ aResult.nPos = 0;
+ aResult.nLevel = -1;
+ aResult.eResult = RP_MENUITEM_NOT_FOUND;
+ return aResult;
+ }
+
+ Menu* pCurrMenu( pMenu );
+ RPResultInfo eResult( RP_OK );
+
+ sal_Int32 nLevel( - 1 );
+ sal_uInt16 nPos( MENU_ITEM_NOTFOUND );
+ do
+ {
+ ++nLevel;
+ OUString aCmd( rReferencePath[i] );
+
+ if ( i == nCount-1 )
+ {
+ // Check last reference path element. Must be a leave (menu item).
+ sal_uInt16 nTmpPos = FindMenuItem( aCmd, pCurrMenu );
+ if ( nTmpPos != MENU_ITEM_NOTFOUND )
+ nPos = nTmpPos;
+ eResult = ( nTmpPos != MENU_ITEM_NOTFOUND ) ? RP_OK : RP_MENUITEM_NOT_FOUND;
+ }
+ else
+ {
+ // Check reference path element. Must be a node (popup menu)!
+ sal_uInt16 nTmpPos = FindMenuItem( aCmd, pCurrMenu );
+ if ( nTmpPos != MENU_ITEM_NOTFOUND )
+ {
+ sal_uInt16 nItemId = pCurrMenu->GetItemId( nTmpPos );
+ Menu* pTmpMenu = pCurrMenu->GetPopupMenu( nItemId );
+ if ( pTmpMenu != nullptr )
+ pCurrMenu = pTmpMenu;
+ else
+ {
+ nPos = nTmpPos;
+ eResult = RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND;
+ }
+ }
+ else
+ eResult = RP_POPUPMENU_NOT_FOUND;
+ }
+ i++;
+ }
+ while ((i < nCount) && (eResult == RP_OK));
+
+ aResult.pPopupMenu = pCurrMenu;
+ aResult.nPos = nPos;
+ aResult.nLevel = nLevel;
+ aResult.eResult = eResult;
+
+ return aResult;
+}
+
+sal_uInt16 MenuBarMerger::FindMenuItem( std::u16string_view rCmd, Menu const * pCurrMenu )
+{
+ for ( sal_uInt16 i = 0; i < pCurrMenu->GetItemCount(); i++ )
+ {
+ const sal_uInt16 nItemId = pCurrMenu->GetItemId( i );
+ if ( nItemId > 0 )
+ {
+ if ( rCmd == pCurrMenu->GetItemCommand( nItemId ) )
+ return i;
+ }
+ }
+
+ return MENU_ITEM_NOTFOUND;
+}
+
+bool MenuBarMerger::CreateSubMenu(
+ Menu* pSubMenu,
+ sal_uInt16& nItemId,
+ const OUString& rModuleIdentifier,
+ const AddonMenuContainer& rAddonSubMenu )
+{
+ const sal_uInt32 nSize = rAddonSubMenu.size();
+ for ( sal_uInt32 i = 0; i < nSize; i++ )
+ {
+ const AddonMenuItem& rMenuItem = rAddonSubMenu[i];
+
+ if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier ))
+ {
+ if ( rMenuItem.aURL == SEPARATOR_STRING )
+ {
+ pSubMenu->InsertSeparator();
+ }
+ else
+ {
+ pSubMenu->InsertItem(nItemId, rMenuItem.aTitle);
+ pSubMenu->SetItemCommand( nItemId, rMenuItem.aURL );
+ if ( !rMenuItem.aSubMenu.empty() )
+ {
+ VclPtr<PopupMenu> pPopupMenu = VclPtr<PopupMenu>::Create();
+ pSubMenu->SetPopupMenu( nItemId, pPopupMenu );
+ ++nItemId;
+
+ CreateSubMenu( pPopupMenu, nItemId, rModuleIdentifier, rMenuItem.aSubMenu );
+ }
+ else
+ ++nItemId;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool MenuBarMerger::MergeMenuItems(
+ Menu* pMenu,
+ sal_uInt16 nPos,
+ sal_uInt16 nModIndex,
+ sal_uInt16& nItemId,
+ const OUString& rModuleIdentifier,
+ const AddonMenuContainer& rAddonMenuItems )
+{
+ sal_uInt16 nIndex( 0 );
+ const sal_uInt32 nSize = rAddonMenuItems.size();
+ for ( sal_uInt32 i = 0; i < nSize; i++ )
+ {
+ const AddonMenuItem& rMenuItem = rAddonMenuItems[i];
+
+ if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier ))
+ {
+ if ( rMenuItem.aURL == SEPARATOR_STRING )
+ {
+ pMenu->InsertSeparator(OString(), nPos+nModIndex+nIndex);
+ }
+ else
+ {
+ pMenu->InsertItem(nItemId, rMenuItem.aTitle, MenuItemBits::NONE, OString(), nPos+nModIndex+nIndex);
+ pMenu->SetItemCommand( nItemId, rMenuItem.aURL );
+ if ( !rMenuItem.aSubMenu.empty() )
+ {
+ VclPtr<PopupMenu> pSubMenu = VclPtr<PopupMenu>::Create();
+ pMenu->SetPopupMenu( nItemId, pSubMenu );
+ ++nItemId;
+
+ CreateSubMenu( pSubMenu, nItemId, rModuleIdentifier, rMenuItem.aSubMenu );
+ }
+ else
+ ++nItemId;
+ }
+ ++nIndex;
+ }
+ }
+
+ return true;
+}
+
+bool MenuBarMerger::ReplaceMenuItem(
+ Menu* pMenu,
+ sal_uInt16 nPos,
+ sal_uInt16& rItemId,
+ const OUString& rModuleIdentifier,
+ const AddonMenuContainer& rAddonMenuItems )
+{
+ // There is no replace available. Therefore we first have to
+ // remove the old menu entry,
+ pMenu->RemoveItem( nPos );
+
+ return MergeMenuItems( pMenu, nPos, 0, rItemId, rModuleIdentifier, rAddonMenuItems );
+}
+
+bool MenuBarMerger::RemoveMenuItems(
+ Menu* pMenu,
+ sal_uInt16 nPos,
+ std::u16string_view rMergeCommandParameter )
+{
+ const sal_uInt16 nParam( sal_uInt16( o3tl::toInt32(rMergeCommandParameter) ));
+ sal_uInt16 nCount = std::max( nParam, sal_uInt16(1) );
+
+ sal_uInt16 i = 0;
+ while (( nPos < pMenu->GetItemCount() ) && ( i < nCount ))
+ {
+ pMenu->RemoveItem( nPos );
+ ++i;
+ }
+
+ return true;
+}
+
+bool MenuBarMerger::ProcessMergeOperation(
+ Menu* pMenu,
+ sal_uInt16 nPos,
+ sal_uInt16& nItemId,
+ std::u16string_view rMergeCommand,
+ std::u16string_view rMergeCommandParameter,
+ const OUString& rModuleIdentifier,
+ const AddonMenuContainer& rAddonMenuItems )
+{
+ sal_uInt16 nModIndex( 0 );
+
+ if ( rMergeCommand == MERGECOMMAND_ADDBEFORE )
+ {
+ nModIndex = 0;
+ return MergeMenuItems( pMenu, nPos, nModIndex, nItemId, rModuleIdentifier, rAddonMenuItems );
+ }
+ else if ( rMergeCommand == MERGECOMMAND_ADDAFTER )
+ {
+ nModIndex = 1;
+ return MergeMenuItems( pMenu, nPos, nModIndex, nItemId, rModuleIdentifier, rAddonMenuItems );
+ }
+ else if ( rMergeCommand == MERGECOMMAND_REPLACE )
+ {
+ return ReplaceMenuItem( pMenu, nPos, nItemId, rModuleIdentifier, rAddonMenuItems );
+ }
+ else if ( rMergeCommand == MERGECOMMAND_REMOVE )
+ {
+ return RemoveMenuItems( pMenu, nPos, rMergeCommandParameter );
+ }
+
+ return false;
+}
+
+bool MenuBarMerger::ProcessFallbackOperation(
+ const ReferencePathInfo& aRefPathInfo,
+ sal_uInt16& rItemId,
+ std::u16string_view rMergeCommand,
+ std::u16string_view rMergeFallback,
+ const ::std::vector< OUString >& rReferencePath,
+ const std::u16string_view rModuleIdentifier,
+ const AddonMenuContainer& rAddonMenuItems )
+{
+ if (( rMergeFallback == MERGEFALLBACK_IGNORE ) ||
+ ( rMergeCommand == MERGECOMMAND_REPLACE ) ||
+ ( rMergeCommand == MERGECOMMAND_REMOVE ) )
+ {
+ return true;
+ }
+ else if ( rMergeFallback == MERGEFALLBACK_ADDPATH )
+ {
+ Menu* pCurrMenu( aRefPathInfo.pPopupMenu );
+ sal_Int32 nLevel( aRefPathInfo.nLevel );
+ const sal_Int32 nSize( rReferencePath.size() );
+ bool bFirstLevel( true );
+
+ while ( nLevel < nSize )
+ {
+ if ( nLevel == nSize-1 )
+ {
+ const sal_uInt32 nCount = rAddonMenuItems.size();
+ for ( sal_uInt32 i = 0; i < nCount; ++i )
+ {
+ const AddonMenuItem& rMenuItem = rAddonMenuItems[i];
+ if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier ))
+ {
+ if ( rMenuItem.aURL == SEPARATOR_STRING )
+ pCurrMenu->InsertSeparator();
+ else
+ {
+ pCurrMenu->InsertItem(rItemId, rMenuItem.aTitle);
+ pCurrMenu->SetItemCommand( rItemId, rMenuItem.aURL );
+ ++rItemId;
+ }
+ }
+ }
+ }
+ else
+ {
+ const OUString aCmd( rReferencePath[nLevel] );
+
+ VclPtr<PopupMenu> pPopupMenu = VclPtr<PopupMenu>::Create();
+
+ if ( bFirstLevel && ( aRefPathInfo.eResult == RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND ))
+ {
+ // special case: menu item without popup
+ sal_uInt16 nInsPos = aRefPathInfo.nPos;
+ sal_uInt16 nSetItemId = pCurrMenu->GetItemId( nInsPos );
+ pCurrMenu->SetItemCommand( nSetItemId, aCmd );
+ pCurrMenu->SetPopupMenu( nSetItemId, pPopupMenu );
+ }
+ else
+ {
+ // normal case: insert a new item with popup
+ pCurrMenu->InsertItem(rItemId, OUString());
+ pCurrMenu->SetItemCommand( rItemId, aCmd );
+ pCurrMenu->SetPopupMenu( rItemId, pPopupMenu );
+ }
+
+ pCurrMenu = pPopupMenu;
+ ++rItemId;
+ bFirstLevel = false;
+ }
+ ++nLevel;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void MenuBarMerger::GetMenuEntry(
+ const uno::Sequence< beans::PropertyValue >& rAddonMenuEntry,
+ AddonMenuItem& rAddonMenuItem )
+{
+ // Reset submenu member
+ rAddonMenuItem.aSubMenu.clear();
+
+ for ( const beans::PropertyValue& rProp : rAddonMenuEntry )
+ {
+ OUString aMenuEntryPropName = rProp.Name;
+ if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_URL )
+ rProp.Value >>= rAddonMenuItem.aURL;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_TITLE )
+ rProp.Value >>= rAddonMenuItem.aTitle;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_SUBMENU )
+ {
+ uno::Sequence< uno::Sequence< beans::PropertyValue > > aSubMenu;
+ rProp.Value >>= aSubMenu;
+ GetSubMenu( aSubMenu, rAddonMenuItem.aSubMenu );
+ }
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_CONTEXT )
+ rProp.Value >>= rAddonMenuItem.aContext;
+ }
+}
+
+void MenuBarMerger::GetSubMenu(
+ const uno::Sequence< uno::Sequence< beans::PropertyValue > >& rSubMenuEntries,
+ AddonMenuContainer& rSubMenu )
+{
+ rSubMenu.clear();
+
+ const sal_Int32 nCount = rSubMenuEntries.getLength();
+ rSubMenu.reserve(rSubMenu.size() + nCount);
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ const uno::Sequence< beans::PropertyValue >& rMenuEntry = rSubMenuEntries[ i ];
+
+ AddonMenuItem aMenuItem;
+ GetMenuEntry( rMenuEntry, aMenuItem );
+ rSubMenu.push_back( aMenuItem );
+ }
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/menubarwrapper.cxx b/framework/source/uielement/menubarwrapper.cxx
new file mode 100644
index 000000000..3bbd71ed7
--- /dev/null
+++ b/framework/source/uielement/menubarwrapper.cxx
@@ -0,0 +1,352 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/menubarwrapper.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/ui/UIElementType.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+using namespace com::sun::star::awt;
+using namespace com::sun::star::util;
+using namespace ::com::sun::star::ui;
+
+namespace framework
+{
+
+void SAL_CALL MenuBarWrapper::acquire() noexcept \
+{ \
+ /* Don't use mutex in methods of XInterface! */ \
+ UIConfigElementWrapperBase::acquire(); \
+} \
+ \
+void SAL_CALL MenuBarWrapper::release() noexcept \
+{ \
+ /* Don't use mutex in methods of XInterface! */ \
+ UIConfigElementWrapperBase::release(); \
+}
+
+css::uno::Any SAL_CALL MenuBarWrapper::queryInterface( const css::uno::Type& aType )
+{
+ /* Attention: Don't use mutex or guard in this method!!! Is a method of XInterface. */
+ /* Ask for my own supported interfaces ... */
+ css::uno::Any aReturn = ::cppu::queryInterface( aType,
+ static_cast< css::lang::XTypeProvider* >( this ),
+ static_cast< css::ui::XUIElement* >( this ),
+ static_cast< css::ui::XUIElementSettings* >( this ),
+ static_cast< css::beans::XMultiPropertySet* >( this ),
+ static_cast< css::beans::XFastPropertySet* >( this ),
+ static_cast< css::beans::XPropertySet* >( this ),
+ static_cast< css::lang::XInitialization* >( this ),
+ static_cast< css::lang::XComponent* >( this ),
+ static_cast< css::util::XUpdatable* >( this ),
+ static_cast< css::ui::XUIConfigurationListener* >( this ),
+ static_cast< css::container::XNameAccess* >( static_cast< css::container::XElementAccess* >( this ) )
+ );
+ /* If searched interface not supported by this class ... */
+ if ( !aReturn.hasValue() )
+ {
+ /* ... ask baseclass for interfaces! */
+ aReturn = UIConfigElementWrapperBase::queryInterface( aType );
+ }
+ /* Return result of this search. */
+ return aReturn;
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL MenuBarWrapper::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+css::uno::Sequence< css::uno::Type > SAL_CALL MenuBarWrapper::getTypes()
+{
+ /* Attention: "TYPES" will expand to "(...)"! */
+ static cppu::OTypeCollection ourTypeCollection {
+ cppu::UnoType<css::lang::XTypeProvider>::get() ,
+ cppu::UnoType<css::ui::XUIElement>::get() ,
+ cppu::UnoType<css::ui::XUIElementSettings>::get() ,
+ cppu::UnoType<css::beans::XMultiPropertySet>::get() ,
+ cppu::UnoType<css::beans::XFastPropertySet>::get() ,
+ cppu::UnoType<css::beans::XPropertySet>::get() ,
+ cppu::UnoType<css::lang::XInitialization>::get() ,
+ cppu::UnoType<css::lang::XComponent>::get() ,
+ cppu::UnoType<css::util::XUpdatable>::get() ,
+ cppu::UnoType<css::ui::XUIConfigurationListener>::get() ,
+ cppu::UnoType<css::container::XNameAccess>::get() };
+ return ourTypeCollection.getTypes();
+}
+
+MenuBarWrapper::MenuBarWrapper(
+ css::uno::Reference< css::uno::XComponentContext > xContext
+ )
+: UIConfigElementWrapperBase( UIElementType::MENUBAR ),
+ m_bRefreshPopupControllerCache( true ),
+ m_xContext(std::move( xContext ))
+{
+}
+
+MenuBarWrapper::~MenuBarWrapper()
+{
+}
+
+void SAL_CALL MenuBarWrapper::dispose()
+{
+ Reference< XComponent > xThis(this);
+
+ css::lang::EventObject aEvent( xThis );
+ m_aListenerContainer.disposeAndClear( aEvent );
+
+ SolarMutexGuard g;
+
+ m_xMenuBarManager->dispose();
+ m_xMenuBarManager.clear();
+ m_xConfigSource.clear();
+ m_xConfigData.clear();
+
+ m_xMenuBar.clear();
+ m_bDisposed = true;
+}
+
+// XInitialization
+void SAL_CALL MenuBarWrapper::initialize( const Sequence< Any >& aArguments )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( m_bInitialized )
+ return;
+
+ OUString aModuleIdentifier;
+ UIConfigElementWrapperBase::initialize( aArguments );
+
+ Reference< XFrame > xFrame( m_xWeakFrame );
+ if ( !(xFrame.is() && m_xConfigSource.is()) )
+ return;
+
+ // Create VCL menubar which will be filled with settings data
+ VclPtr<MenuBar> pVCLMenuBar;
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ pVCLMenuBar = VclPtr<MenuBar>::Create();
+ }
+
+ Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext );
+
+ try
+ {
+ aModuleIdentifier = xModuleManager->identify( xFrame );
+ }
+ catch( const Exception& )
+ {
+ }
+
+ Reference< XURLTransformer > xTrans;
+ try
+ {
+ xTrans.set( URLTransformer::create(m_xContext) );
+ m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false );
+ if ( m_xConfigData.is() )
+ {
+ // Fill menubar with container contents
+ sal_uInt16 nId = 1;
+ MenuBarManager::FillMenuWithConfiguration( nId, pVCLMenuBar, aModuleIdentifier, m_xConfigData, xTrans );
+ }
+ }
+ catch ( const NoSuchElementException& )
+ {
+ }
+
+ bool bMenuOnly( false );
+ for ( const Any& rArg : aArguments )
+ {
+ PropertyValue aPropValue;
+ if ( rArg >>= aPropValue )
+ {
+ if ( aPropValue.Name == "MenuOnly" )
+ aPropValue.Value >>= bMenuOnly;
+ }
+ }
+
+ if ( !bMenuOnly )
+ {
+ // Initialize menubar manager with our vcl menu bar. There are some situations where we only want to get the menu without any
+ // interaction which is done by the menu bar manager. This must be requested by a special property called "MenuOnly". Be careful
+ // a menu bar created with this property is not fully supported. It must be attached to a real menu bar manager to have full
+ // support. This feature is currently used for "Inplace editing"!
+ Reference< XDispatchProvider > xDispatchProvider;
+
+ m_xMenuBarManager = new MenuBarManager( m_xContext,
+ xFrame,
+ xTrans,
+ xDispatchProvider,
+ aModuleIdentifier,
+ pVCLMenuBar,
+ false );
+ }
+
+ // Initialize toolkit menu bar implementation to have awt::XMenuBar for data exchange.
+ // Don't use this toolkit menu bar or one of its functions. It is only used as a data container!
+ m_xMenuBar = new VCLXMenuBar( pVCLMenuBar );
+}
+
+// XUIElementSettings
+void SAL_CALL MenuBarWrapper::updateSettings()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !m_xMenuBarManager.is() )
+ return;
+
+ if ( m_xConfigSource.is() && m_bPersistent )
+ {
+ try
+ {
+ MenuBarManager* pMenuBarManager = static_cast< MenuBarManager *>( m_xMenuBarManager.get() );
+
+ m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false );
+ if ( m_xConfigData.is() )
+ pMenuBarManager->SetItemContainer( m_xConfigData );
+ }
+ catch ( const NoSuchElementException& )
+ {
+ }
+ }
+ else if ( !m_bPersistent )
+ {
+ // Transient menubar: do nothing
+ }
+}
+void MenuBarWrapper::impl_fillNewData()
+{
+ // Transient menubar => Fill menubar with new data
+ MenuBarManager* pMenuBarManager = static_cast< MenuBarManager *>( m_xMenuBarManager.get() );
+
+ if ( pMenuBarManager )
+ pMenuBarManager->SetItemContainer( m_xConfigData );
+}
+
+void MenuBarWrapper::fillPopupControllerCache()
+{
+ if ( m_bRefreshPopupControllerCache )
+ {
+ MenuBarManager* pMenuBarManager = static_cast< MenuBarManager *>( m_xMenuBarManager.get() );
+ if ( pMenuBarManager )
+ pMenuBarManager->GetPopupController( m_aPopupControllerCache );
+ if ( !m_aPopupControllerCache.empty() )
+ m_bRefreshPopupControllerCache = false;
+ }
+}
+
+// XElementAccess
+Type SAL_CALL MenuBarWrapper::getElementType()
+{
+ return cppu::UnoType<XDispatchProvider>::get();
+}
+
+sal_Bool SAL_CALL MenuBarWrapper::hasElements()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ fillPopupControllerCache();
+ return ( !m_aPopupControllerCache.empty() );
+}
+
+// XNameAccess
+Any SAL_CALL MenuBarWrapper::getByName(
+ const OUString& aName )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ fillPopupControllerCache();
+
+ PopupControllerCache::const_iterator pIter = m_aPopupControllerCache.find( aName );
+ if ( pIter == m_aPopupControllerCache.end() )
+ throw container::NoSuchElementException();
+
+ uno::Reference< frame::XDispatchProvider > xDispatchProvider = pIter->second.m_xDispatchProvider;
+ return uno::Any( xDispatchProvider );
+}
+
+Sequence< OUString > SAL_CALL MenuBarWrapper::getElementNames()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ fillPopupControllerCache();
+
+ return comphelper::mapKeysToSequence( m_aPopupControllerCache );
+}
+
+sal_Bool SAL_CALL MenuBarWrapper::hasByName(
+ const OUString& aName )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ fillPopupControllerCache();
+
+ PopupControllerCache::const_iterator pIter = m_aPopupControllerCache.find( aName );
+ if ( pIter != m_aPopupControllerCache.end() )
+ return true;
+ else
+ return false;
+}
+
+// XUIElement
+Reference< XInterface > SAL_CALL MenuBarWrapper::getRealInterface()
+{
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ return Reference< XInterface >( m_xMenuBarManager, UNO_QUERY );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/newmenucontroller.cxx b/framework/source/uielement/newmenucontroller.cxx
new file mode 100644
index 000000000..d282509d9
--- /dev/null
+++ b/framework/source/uielement/newmenucontroller.cxx
@@ -0,0 +1,513 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/newmenucontroller.hxx>
+#include <menuconfiguration.hxx>
+
+#include <services.h>
+
+#include <com/sun/star/awt/MenuItemType.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <svtools/acceleratorexecute.hxx>
+#include <svtools/imagemgr.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/dynamicmenuoptions.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <osl/mutex.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+// Defines
+constexpr OUStringLiteral aSlotNewDocDirect = u".uno:AddDirect";
+constexpr OUStringLiteral aSlotAutoPilot = u".uno:AutoPilotMenu";
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+using namespace com::sun::star::container;
+using namespace com::sun::star::ui;
+
+namespace framework
+{
+
+OUString SAL_CALL NewMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.NewMenuController";
+}
+
+sal_Bool SAL_CALL NewMenuController::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL NewMenuController::getSupportedServiceNames()
+{
+ return { SERVICENAME_POPUPMENUCONTROLLER };
+}
+
+void NewMenuController::setMenuImages( PopupMenu* pPopupMenu, bool bSetImages )
+{
+ sal_uInt16 nItemCount = pPopupMenu->GetItemCount();
+ Reference< XFrame > xFrame( m_xFrame );
+
+ for ( sal_uInt16 i = 0; i < nItemCount; i++ )
+ {
+ sal_uInt16 nItemId = pPopupMenu->GetItemId( i );
+ if ( nItemId != 0 )
+ {
+ if ( bSetImages )
+ {
+ OUString aImageId;
+ OUString aCmd( pPopupMenu->GetItemCommand( nItemId ) );
+ void* nAttributePtr = pPopupMenu->GetUserValue( nItemId );
+ MenuAttributes* pAttributes = static_cast<MenuAttributes *>(nAttributePtr);
+ if (pAttributes)
+ aImageId = pAttributes->aImageId;
+
+ INetURLObject aURLObj( aImageId.isEmpty() ? aCmd : aImageId );
+ Image aImage = SvFileInformationManager::GetImageNoDefault( aURLObj );
+ if ( !aImage )
+ aImage = vcl::CommandInfoProvider::GetImageForCommand(aCmd, xFrame);
+
+ if ( !!aImage )
+ pPopupMenu->SetItemImage( nItemId, aImage );
+ }
+ else
+ pPopupMenu->SetItemImage( nItemId, Image() );
+ }
+ }
+}
+
+void NewMenuController::determineAndSetNewDocAccel(const css::awt::KeyEvent& rKeyCode)
+{
+ sal_uInt16 nCount(m_xPopupMenu->getItemCount());
+ sal_uInt16 nId( 0 );
+ bool bFound( false );
+ OUString aCommand;
+
+ if ( !m_aEmptyDocURL.isEmpty() )
+ {
+ // Search for the empty document URL
+
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ if (m_xPopupMenu->getItemType(i) != css::awt::MenuItemType_SEPARATOR)
+ {
+ nId = m_xPopupMenu->getItemId(i);
+ aCommand = m_xPopupMenu->getCommand(nId);
+ if ( aCommand.startsWith( m_aEmptyDocURL ) )
+ {
+ m_xPopupMenu->setAcceleratorKeyEvent(nId, rKeyCode);
+ bFound = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if ( bFound )
+ return;
+
+ // Search for the default module name
+ OUString aDefaultModuleName( SvtModuleOptions().GetDefaultModuleName() );
+ if ( aDefaultModuleName.isEmpty() )
+ return;
+
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ if (m_xPopupMenu->getItemType(i) != css::awt::MenuItemType_SEPARATOR)
+ {
+ nId = m_xPopupMenu->getItemId(i);
+ aCommand = m_xPopupMenu->getCommand(nId);
+ if ( aCommand.indexOf( aDefaultModuleName ) >= 0 )
+ {
+ m_xPopupMenu->setAcceleratorKeyEvent(nId, rKeyCode);
+ break;
+ }
+ }
+ }
+}
+
+void NewMenuController::setAccelerators()
+{
+ if ( !m_bModuleIdentified )
+ return;
+
+ Reference< XAcceleratorConfiguration > xDocAccelCfg( m_xDocAcceleratorManager );
+ Reference< XAcceleratorConfiguration > xModuleAccelCfg( m_xModuleAcceleratorManager );
+ Reference< XAcceleratorConfiguration > xGlobalAccelCfg( m_xGlobalAcceleratorManager );
+
+ if ( !m_bAcceleratorCfg )
+ {
+ // Retrieve references on demand
+ m_bAcceleratorCfg = true;
+ if ( !xDocAccelCfg.is() )
+ {
+ Reference< XController > xController = m_xFrame->getController();
+ Reference< XModel > xModel;
+ if ( xController.is() )
+ {
+ xModel = xController->getModel();
+ if ( xModel.is() )
+ {
+ Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY );
+ if ( xSupplier.is() )
+ {
+ Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager();
+ if ( xDocUICfgMgr.is() )
+ {
+ xDocAccelCfg = xDocUICfgMgr->getShortCutManager();
+ m_xDocAcceleratorManager = xDocAccelCfg;
+ }
+ }
+ }
+ }
+ }
+
+ if ( !xModuleAccelCfg.is() )
+ {
+ Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier =
+ theModuleUIConfigurationManagerSupplier::get( m_xContext );
+ Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier );
+ if ( xUICfgMgr.is() )
+ {
+ xModuleAccelCfg = xUICfgMgr->getShortCutManager();
+ m_xModuleAcceleratorManager = xModuleAccelCfg;
+ }
+ }
+
+ if ( !xGlobalAccelCfg.is() )
+ {
+ xGlobalAccelCfg = GlobalAcceleratorConfiguration::create( m_xContext );
+ m_xGlobalAcceleratorManager = xGlobalAccelCfg;
+ }
+ }
+
+ vcl::KeyCode aEmptyKeyCode;
+ sal_uInt16 nItemCount(m_xPopupMenu->getItemCount());
+ std::vector< vcl::KeyCode > aMenuShortCuts;
+ std::vector< OUString > aCmds;
+ std::vector< sal_uInt16 > aIds;
+ for ( sal_uInt16 i = 0; i < nItemCount; i++ )
+ {
+ if (m_xPopupMenu->getItemType(i) != css::awt::MenuItemType_SEPARATOR)
+ {
+ sal_uInt16 nId(m_xPopupMenu->getItemId(i));
+ aIds.push_back( nId );
+ aMenuShortCuts.push_back( aEmptyKeyCode );
+ aCmds.push_back(m_xPopupMenu->getCommand(nId));
+ }
+ }
+
+ sal_uInt32 nSeqCount( aIds.size() );
+
+ if ( m_bNewMenu )
+ nSeqCount+=1;
+
+ Sequence< OUString > aSeq( nSeqCount );
+ auto aSeqRange = asNonConstRange(aSeq);
+
+ // Add a special command for our "New" menu.
+ if ( m_bNewMenu )
+ {
+ aSeqRange[nSeqCount-1] = m_aCommandURL;
+ aMenuShortCuts.push_back( aEmptyKeyCode );
+ }
+
+ const sal_uInt32 nCount = aCmds.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ aSeqRange[i] = aCmds[i];
+
+ if ( m_xGlobalAcceleratorManager.is() )
+ retrieveShortcutsFromConfiguration( xGlobalAccelCfg, aSeq, aMenuShortCuts );
+ if ( m_xModuleAcceleratorManager.is() )
+ retrieveShortcutsFromConfiguration( xModuleAccelCfg, aSeq, aMenuShortCuts );
+ if ( m_xDocAcceleratorManager.is() )
+ retrieveShortcutsFromConfiguration( xDocAccelCfg, aSeq, aMenuShortCuts );
+
+ const sal_uInt32 nCount2 = aIds.size();
+ for ( sal_uInt32 i = 0; i < nCount2; i++ )
+ m_xPopupMenu->setAcceleratorKeyEvent(aIds[i], svt::AcceleratorExecute::st_VCLKey2AWTKey(aMenuShortCuts[i]));
+
+ // Special handling for "New" menu short-cut should be set at the
+ // document which will be opened using it.
+ if ( m_bNewMenu )
+ {
+ if ( aMenuShortCuts[nSeqCount-1] != aEmptyKeyCode )
+ determineAndSetNewDocAccel(svt::AcceleratorExecute::st_VCLKey2AWTKey(aMenuShortCuts[nSeqCount-1]));
+ }
+}
+
+void NewMenuController::retrieveShortcutsFromConfiguration(
+ const Reference< XAcceleratorConfiguration >& rAccelCfg,
+ const Sequence< OUString >& rCommands,
+ std::vector< vcl::KeyCode >& aMenuShortCuts )
+{
+ if ( !rAccelCfg.is() )
+ return;
+
+ try
+ {
+ css::awt::KeyEvent aKeyEvent;
+ Sequence< Any > aSeqKeyCode = rAccelCfg->getPreferredKeyEventsForCommandList( rCommands );
+ for ( sal_Int32 i = 0; i < aSeqKeyCode.getLength(); i++ )
+ {
+ if ( aSeqKeyCode[i] >>= aKeyEvent )
+ aMenuShortCuts[i] = svt::AcceleratorExecute::st_AWTKey2VCLKey( aKeyEvent );
+ }
+ }
+ catch ( const IllegalArgumentException& )
+ {
+ }
+}
+
+NewMenuController::NewMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
+ svt::PopupMenuControllerBase( xContext ),
+ m_bShowImages( true ),
+ m_bNewMenu( false ),
+ m_bModuleIdentified( false ),
+ m_bAcceleratorCfg( false ),
+ m_aTargetFrame( "_default" ),
+ m_xContext( xContext )
+{
+}
+
+NewMenuController::~NewMenuController()
+{
+}
+
+// private function
+void NewMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getFromUnoTunnel<VCLXMenu>( rPopupMenu ));
+ PopupMenu* pVCLPopupMenu = nullptr;
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu( rPopupMenu );
+ if ( pPopupMenu )
+ pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+
+ if ( !pVCLPopupMenu )
+ return;
+
+ Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
+ URL aTargetURL;
+ aTargetURL.Complete = m_bNewMenu ? OUString(aSlotNewDocDirect) : OUString(aSlotAutoPilot);
+ m_xURLTransformer->parseStrict( aTargetURL );
+ Reference< XDispatch > xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+ if(xMenuItemDispatch == nullptr)
+ return;
+
+ const std::vector< SvtDynMenuEntry > aDynamicMenuEntries =
+ SvtDynamicMenuOptions::GetMenu( m_bNewMenu ? EDynamicMenuType::NewMenu : EDynamicMenuType::WizardMenu );
+
+ sal_uInt16 nItemId = 1;
+
+ for ( const auto& aDynamicMenuEntry : aDynamicMenuEntries )
+ {
+ if ( aDynamicMenuEntry.sTitle.isEmpty() && aDynamicMenuEntry.sURL.isEmpty() )
+ continue;
+
+ if ( aDynamicMenuEntry.sURL == "private:separator" )
+ rPopupMenu->insertSeparator(-1);
+ else
+ {
+ rPopupMenu->insertItem(nItemId, aDynamicMenuEntry.sTitle, 0, -1);
+ rPopupMenu->setCommand(nItemId, aDynamicMenuEntry.sURL);
+
+ void* nAttributePtr = MenuAttributes::CreateAttribute( aDynamicMenuEntry.sTargetName, aDynamicMenuEntry.sImageIdentifier );
+ pPopupMenu->setUserValue(nItemId, nAttributePtr, MenuAttributes::ReleaseAttribute);
+
+ nItemId++;
+ }
+ }
+
+ if ( m_bShowImages )
+ setMenuImages( pVCLPopupMenu, m_bShowImages );
+}
+
+// XEventListener
+void SAL_CALL NewMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(this);
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+ m_xContext.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) );
+ m_xPopupMenu.clear();
+}
+
+// XStatusListener
+void SAL_CALL NewMenuController::statusChanged( const FeatureStateEvent& )
+{
+}
+
+// XMenuListener
+void SAL_CALL NewMenuController::itemSelected( const css::awt::MenuEvent& rEvent )
+{
+ Reference< css::awt::XPopupMenu > xPopupMenu;
+ Reference< XComponentContext > xContext;
+
+ {
+ osl::MutexGuard aLock(m_aMutex);
+ xPopupMenu = m_xPopupMenu;
+ xContext = m_xContext;
+ }
+
+ if ( !xPopupMenu.is() )
+ return;
+
+ VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getFromUnoTunnel<VCLXMenu>( xPopupMenu ));
+ if ( !pPopupMenu )
+ return;
+
+ OUString aURL;
+ OUString aTargetFrame( m_aTargetFrame );
+
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ aURL = pPopupMenu->getCommand(rEvent.MenuId);
+ void* nAttributePtr = pPopupMenu->getUserValue(rEvent.MenuId);
+ MenuAttributes* pAttributes = static_cast<MenuAttributes *>(nAttributePtr);
+ if (pAttributes)
+ aTargetFrame = pAttributes->aTargetFrame;
+ }
+
+ Sequence< PropertyValue > aArgsList{ comphelper::makePropertyValue("Referer",
+ OUString( "private:user" )) };
+
+ dispatchCommand( aURL, aArgsList, aTargetFrame );
+}
+
+void SAL_CALL NewMenuController::itemActivated( const css::awt::MenuEvent& )
+{
+ SolarMutexGuard aSolarMutexGuard;
+ if ( !(m_xFrame.is() && m_xPopupMenu.is()) )
+ return;
+
+ VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(comphelper::getFromUnoTunnel<VCLXMenu>( m_xPopupMenu ));
+ if ( !pPopupMenu )
+ return;
+
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ bool bShowImages( rSettings.GetUseImagesInMenus() );
+ OUString aIconTheme( rSettings.DetermineIconTheme() );
+
+ PopupMenu* pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
+
+ if ( m_bShowImages != bShowImages || m_aIconTheme != aIconTheme )
+ {
+ m_bShowImages = bShowImages;
+ m_aIconTheme = aIconTheme;
+ setMenuImages( pVCLPopupMenu, m_bShowImages );
+ }
+
+ setAccelerators();
+}
+
+// XPopupMenuController
+void NewMenuController::impl_setPopupMenu()
+{
+
+ if ( m_xPopupMenu.is() )
+ fillPopupMenu( m_xPopupMenu );
+
+ // Identify module that we are attach to. It's our context that we need to know.
+ Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext );
+ try
+ {
+ m_aModuleIdentifier = xModuleManager->identify( m_xFrame );
+ m_bModuleIdentified = true;
+
+ if ( !m_aModuleIdentifier.isEmpty() )
+ {
+ Sequence< PropertyValue > aSeq;
+
+ if ( xModuleManager->getByName( m_aModuleIdentifier ) >>= aSeq )
+ {
+ for ( PropertyValue const & prop : std::as_const(aSeq) )
+ {
+ if ( prop.Name == "ooSetupFactoryEmptyDocumentURL" )
+ {
+ prop.Value >>= m_aEmptyDocURL;
+ break;
+ }
+ }
+ }
+ }
+ }
+ catch ( const RuntimeException& )
+ {
+ throw;
+ }
+ catch ( const Exception& )
+ {
+ }
+}
+
+// XInitialization
+void SAL_CALL NewMenuController::initialize( const Sequence< Any >& aArguments )
+{
+ osl::MutexGuard aLock( m_aMutex );
+
+ bool bInitialized( m_bInitialized );
+ if ( bInitialized )
+ return;
+
+ svt::PopupMenuControllerBase::initialize( aArguments );
+
+ if ( m_bInitialized )
+ {
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+
+ m_bShowImages = rSettings.GetUseImagesInMenus();
+ m_aIconTheme = rSettings.DetermineIconTheme();
+ m_bNewMenu = m_aCommandURL == aSlotNewDocDirect;
+ }
+}
+
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_NewMenuController_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::NewMenuController(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/objectmenucontroller.cxx b/framework/source/uielement/objectmenucontroller.cxx
new file mode 100644
index 000000000..62b5586ee
--- /dev/null
+++ b/framework/source/uielement/objectmenucontroller.cxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdtypes.h>
+
+#include <com/sun/star/embed/VerbAttributes.hpp>
+#include <com/sun/star/embed/VerbDescriptor.hpp>
+
+#include <svtools/popupmenucontrollerbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/mutex.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+using namespace framework;
+
+namespace {
+
+class ObjectMenuController : public svt::PopupMenuControllerBase
+{
+ using svt::PopupMenuControllerBase::disposing;
+
+public:
+ explicit ObjectMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.ObjectMenuController";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.frame.PopupMenuController"};
+ }
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+private:
+ void fillPopupMenu( const css::uno::Sequence< css::embed::VerbDescriptor >& rVerbCommandSeq, css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu );
+};
+
+ObjectMenuController::ObjectMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
+ svt::PopupMenuControllerBase( xContext )
+{
+}
+
+// private function
+void ObjectMenuController::fillPopupMenu( const Sequence< css::embed::VerbDescriptor >& rVerbCommandSeq, Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ const css::embed::VerbDescriptor* pVerbCommandArray = rVerbCommandSeq.getConstArray();
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu( rPopupMenu );
+
+ static const OUStringLiteral aVerbCommand( u".uno:ObjectMenue?VerbID:short=" );
+ for ( sal_Int32 i = 0; i < rVerbCommandSeq.getLength(); i++ )
+ {
+ const css::embed::VerbDescriptor& rVerb = pVerbCommandArray[i];
+ if ( rVerb.VerbAttributes & css::embed::VerbAttributes::MS_VERBATTR_ONCONTAINERMENU )
+ {
+ m_xPopupMenu->insertItem( i+1, rVerb.VerbName, 0, i );
+ OUString aCommand = aVerbCommand + OUString::number( rVerb.VerbID );
+ m_xPopupMenu->setCommand( i+1, aCommand ); // Store verb command
+ }
+ }
+}
+
+// XEventListener
+void SAL_CALL ObjectMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(this);
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) );
+ m_xPopupMenu.clear();
+}
+
+// XStatusListener
+void SAL_CALL ObjectMenuController::statusChanged( const FeatureStateEvent& Event )
+{
+ Sequence < css::embed::VerbDescriptor > aVerbCommandSeq;
+ if ( Event.State >>= aVerbCommandSeq )
+ {
+ osl::MutexGuard aLock( m_aMutex );
+ if ( m_xPopupMenu.is() )
+ fillPopupMenu( aVerbCommandSeq, m_xPopupMenu );
+ }
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_ObjectMenuController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ObjectMenuController(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/popuptoolbarcontroller.cxx b/framework/source/uielement/popuptoolbarcontroller.cxx
new file mode 100644
index 000000000..6cf02e5e4
--- /dev/null
+++ b/framework/source/uielement/popuptoolbarcontroller.cxx
@@ -0,0 +1,799 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <bitmaps.hlst>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <menuconfiguration.hxx>
+#include <svtools/imagemgr.hxx>
+#include <svtools/toolboxcontroller.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+#include <utility>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+
+#include <com/sun/star/awt/PopupMenuDirection.hpp>
+#include <com/sun/star/awt/XPopupMenu.hpp>
+#include <com/sun/star/frame/thePopupMenuControllerFactory.hpp>
+#include <com/sun/star/frame/XPopupMenuController.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/frame/XSubToolbarController.hpp>
+#include <com/sun/star/frame/XUIControllerFactory.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+
+using namespace framework;
+
+namespace
+{
+
+typedef cppu::ImplInheritanceHelper< svt::ToolboxController,
+ css::lang::XServiceInfo >
+ ToolBarBase;
+
+class PopupMenuToolbarController : public ToolBarBase
+{
+public:
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+ // XToolbarController
+ virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createPopupWindow() override;
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+
+protected:
+ PopupMenuToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ OUString aPopupCommand = OUString() );
+ virtual void functionExecuted( const OUString &rCommand );
+ virtual ToolBoxItemBits getDropDownStyle() const;
+ void createPopupMenuController();
+
+ bool m_bHasController;
+ bool m_bResourceURL;
+ OUString m_aPopupCommand;
+ css::uno::Reference< css::awt::XPopupMenu > m_xPopupMenu;
+
+private:
+ css::uno::Reference< css::frame::XUIControllerFactory > m_xPopupMenuFactory;
+ css::uno::Reference< css::frame::XPopupMenuController > m_xPopupMenuController;
+};
+
+PopupMenuToolbarController::PopupMenuToolbarController(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ OUString aPopupCommand )
+ : ToolBarBase( xContext, css::uno::Reference< css::frame::XFrame >(), /*aCommandURL*/OUString() )
+ , m_bHasController( false )
+ , m_bResourceURL( false )
+ , m_aPopupCommand(std::move( aPopupCommand ))
+{
+}
+
+void SAL_CALL PopupMenuToolbarController::dispose()
+{
+ svt::ToolboxController::dispose();
+
+ osl::MutexGuard aGuard( m_aMutex );
+ if( m_xPopupMenuController.is() )
+ {
+ css::uno::Reference< css::lang::XComponent > xComponent(
+ m_xPopupMenuController, css::uno::UNO_QUERY );
+ if( xComponent.is() )
+ {
+ try
+ {
+ xComponent->dispose();
+ }
+ catch (...)
+ {}
+ }
+ m_xPopupMenuController.clear();
+ }
+
+ m_xContext.clear();
+ m_xPopupMenuFactory.clear();
+ m_xPopupMenu.clear();
+}
+
+void SAL_CALL PopupMenuToolbarController::initialize(
+ const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ ToolboxController::initialize( aArguments );
+
+ osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_aPopupCommand.getLength() )
+ m_aPopupCommand = m_aCommandURL;
+
+ try
+ {
+ m_xPopupMenuFactory.set(
+ css::frame::thePopupMenuControllerFactory::get( m_xContext ) );
+ m_bHasController = m_xPopupMenuFactory->hasController(
+ m_aPopupCommand, getModuleName() );
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_INFO_EXCEPTION( "fwk.uielement", "" );
+ }
+
+ if ( !m_bHasController && m_aPopupCommand.startsWith( "private:resource/" ) )
+ {
+ m_bResourceURL = true;
+ m_bHasController = true;
+ }
+
+ SolarMutexGuard aSolarLock;
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nItemId;
+ if ( getToolboxId( nItemId, &pToolBox ) )
+ {
+ ToolBoxItemBits nCurStyle( pToolBox->GetItemBits( nItemId ) );
+ ToolBoxItemBits nSetStyle( getDropDownStyle() );
+ pToolBox->SetItemBits( nItemId,
+ m_bHasController ?
+ nCurStyle | nSetStyle :
+ nCurStyle & ~nSetStyle );
+ }
+
+}
+
+void SAL_CALL PopupMenuToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ if ( m_bResourceURL )
+ return;
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nItemId;
+ if ( getToolboxId( nItemId, &pToolBox ) )
+ {
+ SolarMutexGuard aSolarLock;
+ pToolBox->EnableItem( nItemId, rEvent.IsEnabled );
+ bool bValue;
+ if ( rEvent.State >>= bValue )
+ pToolBox->CheckItem( nItemId, bValue );
+ }
+}
+
+css::uno::Reference< css::awt::XWindow > SAL_CALL
+PopupMenuToolbarController::createPopupWindow()
+{
+ css::uno::Reference< css::awt::XWindow > xRet;
+
+ osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_bHasController )
+ return xRet;
+
+ createPopupMenuController();
+
+ SolarMutexGuard aSolarLock;
+ VclPtr< ToolBox > pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) );
+ if ( !pToolBox )
+ return xRet;
+
+ pToolBox->SetItemDown( m_nToolBoxId, true );
+ WindowAlign eAlign( pToolBox->GetAlign() );
+
+ // If the parent ToolBox is in popup mode (e.g. sub toolbar, overflow popup),
+ // its ToolBarManager can be disposed along with our controller, destroying
+ // m_xPopupMenu, while the latter still in execute. This should be fixed at a
+ // different level, for now just hold it here so it won't crash.
+ css::uno::Reference< css::awt::XPopupMenu > xPopupMenu ( m_xPopupMenu );
+ sal_uInt16 nId = xPopupMenu->execute(
+ css::uno::Reference< css::awt::XWindowPeer >( getParent(), css::uno::UNO_QUERY ),
+ VCLUnoHelper::ConvertToAWTRect( pToolBox->GetItemRect( m_nToolBoxId ) ),
+ ( eAlign == WindowAlign::Top || eAlign == WindowAlign::Bottom ) ?
+ css::awt::PopupMenuDirection::EXECUTE_DOWN :
+ css::awt::PopupMenuDirection::EXECUTE_RIGHT );
+ pToolBox->SetItemDown( m_nToolBoxId, false );
+
+ if ( nId )
+ functionExecuted( xPopupMenu->getCommand( nId ) );
+
+ return xRet;
+}
+
+void PopupMenuToolbarController::functionExecuted( const OUString &/*rCommand*/)
+{
+}
+
+ToolBoxItemBits PopupMenuToolbarController::getDropDownStyle() const
+{
+ return ToolBoxItemBits::DROPDOWN;
+}
+
+void PopupMenuToolbarController::createPopupMenuController()
+{
+ if( !m_bHasController )
+ return;
+
+ if ( m_xPopupMenuController.is() )
+ {
+ m_xPopupMenuController->updatePopupMenu();
+ }
+ else
+ {
+ css::uno::Sequence<css::uno::Any> aArgs {
+ css::uno::Any(comphelper::makePropertyValue("Frame", m_xFrame)),
+ css::uno::Any(comphelper::makePropertyValue("ModuleIdentifier", m_sModuleName)),
+ css::uno::Any(comphelper::makePropertyValue("InToolbar", true))
+ };
+
+ try
+ {
+ m_xPopupMenu.set(
+ m_xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.awt.PopupMenu", m_xContext ),
+ css::uno::UNO_QUERY_THROW );
+
+ if (m_bResourceURL)
+ {
+ sal_Int32 nAppendIndex = aArgs.getLength();
+ aArgs.realloc(nAppendIndex + 1);
+ aArgs.getArray()[nAppendIndex] <<= comphelper::makePropertyValue("ResourceURL", m_aPopupCommand);
+
+ m_xPopupMenuController.set( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.framework.ResourceMenuController", aArgs, m_xContext), css::uno::UNO_QUERY_THROW );
+ }
+ else
+ {
+ m_xPopupMenuController.set( m_xPopupMenuFactory->createInstanceWithArgumentsAndContext(
+ m_aPopupCommand, aArgs, m_xContext), css::uno::UNO_QUERY_THROW );
+ }
+
+ m_xPopupMenuController->setPopupMenu( m_xPopupMenu );
+ }
+ catch ( const css::uno::Exception & )
+ {
+ TOOLS_INFO_EXCEPTION( "fwk.uielement", "" );
+ m_xPopupMenu.clear();
+ }
+ }
+}
+
+class GenericPopupToolbarController : public PopupMenuToolbarController
+{
+public:
+ GenericPopupToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Sequence< css::uno::Any >& rxArgs );
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& rxArgs ) override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & rServiceName) override;
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+private:
+ bool m_bSplitButton, m_bReplaceWithLast;
+ void functionExecuted(const OUString &rCommand) override;
+ ToolBoxItemBits getDropDownStyle() const override;
+};
+
+GenericPopupToolbarController::GenericPopupToolbarController(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Sequence< css::uno::Any >& rxArgs )
+ : PopupMenuToolbarController( xContext )
+ , m_bReplaceWithLast( false )
+{
+ css::beans::PropertyValue aPropValue;
+ for ( const auto& arg: rxArgs )
+ {
+ if ( ( arg >>= aPropValue ) && aPropValue.Name == "Value" )
+ {
+ sal_Int32 nIdx{ 0 };
+ OUString aValue;
+ aPropValue.Value >>= aValue;
+ m_aPopupCommand = aValue.getToken(0, ';', nIdx);
+ m_bReplaceWithLast = aValue.getToken(0, ';', nIdx).toBoolean();
+ break;
+ }
+ }
+ m_bSplitButton = m_bReplaceWithLast || !m_aPopupCommand.isEmpty();
+}
+
+OUString GenericPopupToolbarController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.GenericPopupToolbarController";
+}
+
+sal_Bool GenericPopupToolbarController::supportsService(OUString const & rServiceName)
+{
+ return cppu::supportsService( this, rServiceName );
+}
+
+css::uno::Sequence<OUString> GenericPopupToolbarController::getSupportedServiceNames()
+{
+ return {"com.sun.star.frame.ToolbarController"};
+}
+
+void GenericPopupToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& rxArgs )
+{
+ PopupMenuToolbarController::initialize( rxArgs );
+ if ( m_bReplaceWithLast )
+ // Create early, so we can use the menu is statusChanged method.
+ createPopupMenuController();
+}
+
+void GenericPopupToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ SolarMutexGuard aGuard;
+
+ if ( m_bReplaceWithLast && !rEvent.IsEnabled && m_xPopupMenu.is() )
+ {
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( getToolboxId( nId, &pToolBox ) && pToolBox->IsItemEnabled( nId ) )
+ {
+ Menu* pVclMenu = comphelper::getFromUnoTunnel<VCLXMenu>( m_xPopupMenu )->GetMenu();
+ pVclMenu->Activate();
+ pVclMenu->Deactivate();
+ }
+
+ for (sal_uInt16 i = 0, nCount = m_xPopupMenu->getItemCount(); i < nCount; ++i )
+ {
+ sal_uInt16 nItemId = m_xPopupMenu->getItemId(i);
+ if (nItemId && m_xPopupMenu->isItemEnabled(nItemId) && !m_xPopupMenu->getPopupMenu(nItemId).is())
+ {
+ functionExecuted(m_xPopupMenu->getCommand(nItemId));
+ return;
+ }
+ }
+ }
+
+ PopupMenuToolbarController::statusChanged( rEvent );
+}
+
+void GenericPopupToolbarController::functionExecuted( const OUString& rCommand )
+{
+ if ( !m_bReplaceWithLast )
+ return;
+
+ removeStatusListener( m_aCommandURL );
+
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommand, m_sModuleName);
+ OUString aRealCommand( vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties) );
+ m_aCommandURL = aRealCommand.isEmpty() ? rCommand : aRealCommand;
+ addStatusListener( m_aCommandURL );
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( getToolboxId( nId, &pToolBox ) )
+ {
+ pToolBox->SetItemCommand( nId, rCommand );
+ pToolBox->SetHelpText( nId, OUString() ); // Will retrieve the new one from help.
+ pToolBox->SetItemText(nId, vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
+ pToolBox->SetQuickHelpText(nId, vcl::CommandInfoProvider::GetTooltipForCommand(rCommand, aProperties, m_xFrame));
+
+ Image aImage = vcl::CommandInfoProvider::GetImageForCommand(rCommand, m_xFrame, pToolBox->GetImageSize());
+ if ( !!aImage )
+ pToolBox->SetItemImage( nId, aImage );
+ }
+}
+
+ToolBoxItemBits GenericPopupToolbarController::getDropDownStyle() const
+{
+ return m_bSplitButton ? ToolBoxItemBits::DROPDOWN : ToolBoxItemBits::DROPDOWNONLY;
+}
+
+class SaveToolbarController : public cppu::ImplInheritanceHelper< PopupMenuToolbarController,
+ css::frame::XSubToolbarController,
+ css::util::XModifyListener >
+{
+public:
+ explicit SaveToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XSubToolbarController
+ // Make ToolBarManager ask our controller for updated image, in case of icon theme change.
+ virtual sal_Bool SAL_CALL opensSubToolbar() override;
+ virtual OUString SAL_CALL getSubToolbarName() override;
+ virtual void SAL_CALL functionSelected( const OUString& aCommand ) override;
+ virtual void SAL_CALL updateImage() override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const css::lang::EventObject& rEvent ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& rEvent ) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( OUString const & rServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+private:
+ bool m_bReadOnly;
+ bool m_bModified;
+ css::uno::Reference< css::frame::XStorable > m_xStorable;
+ css::uno::Reference< css::util::XModifiable > m_xModifiable;
+};
+
+SaveToolbarController::SaveToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext )
+ : ImplInheritanceHelper( rxContext, ".uno:SaveAsMenu" )
+ , m_bReadOnly( false )
+ , m_bModified( false )
+{
+}
+
+void SaveToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ PopupMenuToolbarController::initialize( aArguments );
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( !getToolboxId( nId, &pToolBox ) )
+ return;
+
+ css::uno::Reference< css::frame::XController > xController = m_xFrame->getController();
+ if ( xController.is() )
+ m_xModifiable.set( xController->getModel(), css::uno::UNO_QUERY );
+
+ if ( m_xModifiable.is() && pToolBox->GetItemCommand( nId ) == m_aCommandURL )
+ // Will also enable the save as only mode.
+ m_xStorable.set( m_xModifiable, css::uno::UNO_QUERY );
+ else if ( !m_xModifiable.is() )
+ // Can be in table/query design.
+ m_xModifiable.set( xController, css::uno::UNO_QUERY );
+ else
+ // Simple save button, without the dropdown.
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) & ~ ToolBoxItemBits::DROPDOWN );
+
+ if ( m_xModifiable.is() )
+ {
+ m_xModifiable->addModifyListener( this );
+ modified( css::lang::EventObject() );
+ }
+}
+
+sal_Bool SaveToolbarController::opensSubToolbar()
+{
+ return true;
+}
+
+OUString SaveToolbarController::getSubToolbarName()
+{
+ return OUString();
+}
+
+void SaveToolbarController::functionSelected( const OUString& /*aCommand*/ )
+{
+}
+
+void SaveToolbarController::updateImage()
+{
+ SolarMutexGuard aGuard;
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( !getToolboxId( nId, &pToolBox ) )
+ return;
+
+ vcl::ImageType eImageType = pToolBox->GetImageSize();
+
+ Image aImage;
+
+ if ( m_bReadOnly )
+ {
+ aImage = vcl::CommandInfoProvider::GetImageForCommand(".uno:SaveAs", m_xFrame, eImageType);
+ }
+ else if ( m_bModified )
+ {
+ if (eImageType == vcl::ImageType::Size26)
+ aImage = Image(StockImage::Yes, BMP_SAVEMODIFIED_LARGE);
+ else if (eImageType == vcl::ImageType::Size32)
+ aImage = Image(StockImage::Yes, BMP_SAVEMODIFIED_EXTRALARGE);
+ else
+ aImage = Image(StockImage::Yes, BMP_SAVEMODIFIED_SMALL);
+ }
+
+ if ( !aImage )
+ aImage = vcl::CommandInfoProvider::GetImageForCommand(m_aCommandURL, m_xFrame, eImageType);
+
+ if ( !!aImage )
+ pToolBox->SetItemImage( nId, aImage );
+}
+
+void SaveToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( !getToolboxId( nId, &pToolBox ) )
+ return;
+
+ bool bLastReadOnly = m_bReadOnly;
+ m_bReadOnly = m_xStorable.is() && m_xStorable->isReadonly();
+ if ( bLastReadOnly != m_bReadOnly )
+ {
+ OUString sCommand = m_bReadOnly ? OUString( ".uno:SaveAs" ) : m_aCommandURL;
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(sCommand,
+ vcl::CommandInfoProvider::GetModuleIdentifier(m_xFrame));
+ pToolBox->SetQuickHelpText( nId,
+ vcl::CommandInfoProvider::GetTooltipForCommand(sCommand, aProperties, m_xFrame) );
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) & ~( m_bReadOnly ? ToolBoxItemBits::DROPDOWN : ToolBoxItemBits::DROPDOWNONLY ) );
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ( m_bReadOnly ? ToolBoxItemBits::DROPDOWNONLY : ToolBoxItemBits::DROPDOWN ) );
+ updateImage();
+ }
+
+ if ( !m_bReadOnly )
+ pToolBox->EnableItem( nId, rEvent.IsEnabled );
+}
+
+void SaveToolbarController::modified( const css::lang::EventObject& /*rEvent*/ )
+{
+ bool bLastModified = m_bModified;
+ m_bModified = m_xModifiable->isModified();
+ if ( bLastModified != m_bModified )
+ updateImage();
+}
+
+void SaveToolbarController::disposing( const css::lang::EventObject& rEvent )
+{
+ if ( rEvent.Source == m_xModifiable )
+ {
+ m_xModifiable.clear();
+ m_xStorable.clear();
+ }
+ else
+ PopupMenuToolbarController::disposing( rEvent );
+}
+
+void SaveToolbarController::dispose()
+{
+ PopupMenuToolbarController::dispose();
+ if ( m_xModifiable.is() )
+ {
+ m_xModifiable->removeModifyListener( this );
+ m_xModifiable.clear();
+ }
+ m_xStorable.clear();
+}
+
+OUString SaveToolbarController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.SaveToolbarController";
+}
+
+sal_Bool SaveToolbarController::supportsService( OUString const & rServiceName )
+{
+ return cppu::supportsService( this, rServiceName );
+}
+
+css::uno::Sequence< OUString > SaveToolbarController::getSupportedServiceNames()
+{
+ return {"com.sun.star.frame.ToolbarController"};
+}
+
+class NewToolbarController : public cppu::ImplInheritanceHelper<PopupMenuToolbarController, css::frame::XSubToolbarController>
+{
+public:
+ explicit NewToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & rServiceName) override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XSubToolbarController
+ // Make ToolBarManager ask our controller for updated image, in case of icon theme change.
+ sal_Bool SAL_CALL opensSubToolbar() override { return true; }
+ OUString SAL_CALL getSubToolbarName() override { return OUString(); }
+ void SAL_CALL functionSelected( const OUString& ) override {}
+ void SAL_CALL updateImage() override;
+
+private:
+ void functionExecuted( const OUString &rCommand ) override;
+ void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+ void SAL_CALL execute( sal_Int16 KeyModifier ) override;
+ sal_uInt16 getMenuIdForCommand( std::u16string_view rCommand );
+
+ sal_uInt16 m_nMenuId;
+};
+
+NewToolbarController::NewToolbarController(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext )
+ : ImplInheritanceHelper( xContext )
+ , m_nMenuId( 0 )
+{
+}
+
+OUString NewToolbarController::getImplementationName()
+{
+ return "org.apache.openoffice.comp.framework.NewToolbarController";
+}
+
+sal_Bool NewToolbarController::supportsService(OUString const & rServiceName)
+{
+ return cppu::supportsService( this, rServiceName );
+}
+
+css::uno::Sequence<OUString> NewToolbarController::getSupportedServiceNames()
+{
+ return {"com.sun.star.frame.ToolbarController"};
+}
+
+void SAL_CALL NewToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ PopupMenuToolbarController::initialize( aArguments );
+
+ osl::MutexGuard aGuard( m_aMutex );
+ createPopupMenuController();
+}
+
+void SAL_CALL NewToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ if ( rEvent.IsEnabled )
+ {
+ OUString aState;
+ rEvent.State >>= aState;
+ try
+ {
+ // set the image even if the state is not a string
+ // the toolbar item command will be used as a fallback
+ functionExecuted( aState );
+ }
+ catch (const css::ucb::CommandFailedException&)
+ {
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ }
+ }
+
+ enable( rEvent.IsEnabled );
+}
+
+void SAL_CALL NewToolbarController::execute( sal_Int16 /*KeyModifier*/ )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ OUString aURL, aTarget;
+ if ( m_xPopupMenu.is() && m_nMenuId )
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ aURL = m_xPopupMenu->getCommand(m_nMenuId);
+
+ // TODO investigate how to wrap Get/SetUserValue in css::awt::XMenu
+ VCLXMenu* pMenu = comphelper::getFromUnoTunnel<VCLXMenu>(m_xPopupMenu);
+ MenuAttributes* pMenuAttributes(static_cast<MenuAttributes*>(pMenu->getUserValue(m_nMenuId)));
+ if ( pMenuAttributes )
+ aTarget = pMenuAttributes->aTargetFrame;
+ else
+ aTarget = "_default";
+ }
+ else
+ aURL = m_aCommandURL;
+
+ css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
+ "Referer", OUString( "private:user" )) };
+
+ dispatchCommand( aURL, aArgs, aTarget );
+}
+
+void NewToolbarController::functionExecuted( const OUString &rCommand )
+{
+ m_nMenuId = getMenuIdForCommand( rCommand );
+ updateImage();
+}
+
+sal_uInt16 NewToolbarController::getMenuIdForCommand( std::u16string_view rCommand )
+{
+ if ( m_xPopupMenu.is() && !rCommand.empty() )
+ {
+ sal_uInt16 nCount = m_xPopupMenu->getItemCount();
+ for ( sal_uInt16 n = 0; n < nCount; ++n )
+ {
+ sal_uInt16 nId = m_xPopupMenu->getItemId(n);
+ OUString aCmd(m_xPopupMenu->getCommand(nId));
+
+ // match even if the menu command is more detailed
+ // (maybe an additional query) #i28667#
+ if ( aCmd.match( rCommand ) )
+ return nId;
+ }
+ }
+
+ return 0;
+}
+
+void SAL_CALL NewToolbarController::updateImage()
+{
+ SolarMutexGuard aSolarLock;
+ VclPtr< ToolBox> pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) );
+ if ( !pToolBox )
+ return;
+
+ OUString aURL, aImageId;
+ if ( m_xPopupMenu.is() && m_nMenuId )
+ {
+ aURL = m_xPopupMenu->getCommand(m_nMenuId);
+ VCLXMenu* pMenu = comphelper::getFromUnoTunnel<VCLXMenu>(m_xPopupMenu);
+ MenuAttributes* pMenuAttributes(static_cast<MenuAttributes*>(pMenu->getUserValue(m_nMenuId)));
+ if ( pMenuAttributes )
+ aImageId = pMenuAttributes->aImageId;
+ }
+ else
+ aURL = m_aCommandURL;
+
+ INetURLObject aURLObj( aImageId.isEmpty() ? aURL : aImageId );
+ vcl::ImageType eImageType( pToolBox->GetImageSize() );
+ Image aImage = SvFileInformationManager::GetImageNoDefault( aURLObj, eImageType );
+ if ( !aImage )
+ aImage = vcl::CommandInfoProvider::GetImageForCommand( aURL, m_xFrame, eImageType );
+
+ if ( !aImage )
+ return;
+
+ pToolBox->SetItemImage( m_nToolBoxId, aImage );
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_GenericPopupToolbarController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &args)
+{
+ return cppu::acquire(new GenericPopupToolbarController(context, args));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_SaveToolbarController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SaveToolbarController(context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+org_apache_openoffice_comp_framework_NewToolbarController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new NewToolbarController(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/progressbarwrapper.cxx b/framework/source/uielement/progressbarwrapper.cxx
new file mode 100644
index 000000000..ad147111f
--- /dev/null
+++ b/framework/source/uielement/progressbarwrapper.cxx
@@ -0,0 +1,310 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/progressbarwrapper.hxx>
+
+#include <uielement/statusindicatorinterfacewrapper.hxx>
+
+#include <com/sun/star/ui/UIElementType.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+
+#include <vcl/status.hxx>
+#include <vcl/svapp.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+using namespace ::com::sun::star;
+
+namespace framework{
+
+ProgressBarWrapper::ProgressBarWrapper() :
+UIElementWrapperBase( css::ui::UIElementType::PROGRESSBAR )
+ , m_bOwnsInstance( false )
+ , m_nRange( 100 )
+ , m_nValue( 0 )
+{
+}
+
+ProgressBarWrapper::~ProgressBarWrapper()
+{
+}
+
+// public interfaces
+void ProgressBarWrapper::setStatusBar( const uno::Reference< awt::XWindow >& rStatusBar, bool bOwnsInstance )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ if ( m_bOwnsInstance )
+ {
+ // dispose XWindow reference of our status bar
+ try
+ {
+ if ( m_xStatusBar.is() )
+ m_xStatusBar->dispose();
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+ m_xStatusBar.clear();
+ }
+
+ m_bOwnsInstance = bOwnsInstance;
+ m_xStatusBar = rStatusBar;
+}
+
+uno::Reference< awt::XWindow > ProgressBarWrapper::getStatusBar() const
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return uno::Reference< awt::XWindow >();
+
+ return m_xStatusBar;
+}
+
+// wrapped methods of css::task::XStatusIndicator
+void ProgressBarWrapper::start( const OUString& Text, ::sal_Int32 Range )
+{
+ uno::Reference< awt::XWindow > xWindow;
+ sal_Int32 nValue( 0 );
+
+ {
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ xWindow = m_xStatusBar;
+ m_nValue = 0;
+ m_nRange = Range;
+ nValue = m_nValue;
+ }
+
+ if ( !xWindow.is() )
+ return;
+
+ SolarMutexGuard aSolarMutexGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( !(pWindow && pWindow->GetType() == WindowType::STATUSBAR) )
+ return;
+
+ StatusBar* pStatusBar = static_cast<StatusBar *>(pWindow.get());
+ if ( !pStatusBar->IsProgressMode() )
+ pStatusBar->StartProgressMode( Text );
+ else
+ {
+ pStatusBar->SetUpdateMode( false );
+ pStatusBar->EndProgressMode();
+ pStatusBar->StartProgressMode( Text );
+ pStatusBar->SetProgressValue( sal_uInt16( nValue ));
+ pStatusBar->SetUpdateMode( true );
+ }
+ pStatusBar->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+}
+
+void ProgressBarWrapper::end()
+{
+ uno::Reference< awt::XWindow > xWindow;
+
+ {
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ xWindow = m_xStatusBar;
+ m_nRange = 100;
+ m_nValue = 0;
+ }
+
+ if ( xWindow.is() )
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->GetType() == WindowType::STATUSBAR )
+ {
+ StatusBar* pStatusBar = static_cast<StatusBar *>(pWindow.get());
+ if ( pStatusBar->IsProgressMode() )
+ pStatusBar->EndProgressMode();
+ }
+ }
+}
+
+void ProgressBarWrapper::setText( const OUString& Text )
+{
+ uno::Reference< awt::XWindow > xWindow;
+ sal_Int32 nValue( 0 );
+
+ {
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ xWindow = m_xStatusBar;
+ m_aText = Text;
+ nValue = m_nValue;
+ }
+
+ if ( !xWindow.is() )
+ return;
+
+ SolarMutexGuard aSolarMutexGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( !(pWindow && pWindow->GetType() == WindowType::STATUSBAR) )
+ return;
+
+ StatusBar* pStatusBar = static_cast<StatusBar *>(pWindow.get());
+ if( pStatusBar->IsProgressMode() )
+ {
+ pStatusBar->SetUpdateMode( false );
+ pStatusBar->EndProgressMode();
+ pStatusBar->StartProgressMode( Text );
+ pStatusBar->SetProgressValue( sal_uInt16( nValue ));
+ pStatusBar->SetUpdateMode( true );
+ }
+ else
+ pStatusBar->SetText( Text );
+}
+
+void ProgressBarWrapper::setValue( ::sal_Int32 nValue )
+{
+ uno::Reference< awt::XWindow > xWindow;
+ OUString aText;
+ bool bSetValue( false );
+
+ {
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ xWindow = m_xStatusBar;
+
+ double fVal( 0 );
+ if ( m_nRange > 0 )
+ {
+ fVal = ( double( nValue ) / double( m_nRange )) * 100;
+ fVal = std::clamp( fVal, 0.0, 100.0 );
+ }
+
+ if ( m_nValue != sal_Int32( fVal ))
+ {
+ m_nValue = sal_Int32( fVal );
+ bSetValue = true;
+ }
+
+ nValue = m_nValue;
+ aText = m_aText;
+ }
+
+ if ( xWindow.is() && bSetValue )
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->GetType() == WindowType::STATUSBAR )
+ {
+ StatusBar* pStatusBar = static_cast<StatusBar *>(pWindow.get());
+ if ( !pStatusBar->IsProgressMode() )
+ pStatusBar->StartProgressMode( aText );
+ pStatusBar->SetProgressValue( sal_uInt16( nValue ));
+ }
+ }
+}
+
+void ProgressBarWrapper::reset()
+{
+ setText( OUString() );
+ setValue( 0 );
+}
+
+// XInitialization
+void SAL_CALL ProgressBarWrapper::initialize( const uno::Sequence< uno::Any >& )
+{
+ // dummy - do nothing
+}
+
+// XUpdatable
+void SAL_CALL ProgressBarWrapper::update()
+{
+ // dummy - do nothing
+}
+
+// XComponent
+void SAL_CALL ProgressBarWrapper::dispose()
+{
+ uno::Reference< lang::XComponent > xThis(this);
+
+ {
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+ }
+
+ {
+ lang::EventObject aEvent( xThis );
+ m_aListenerContainer.disposeAndClear( aEvent );
+
+ SolarMutexGuard g;
+ if ( m_bOwnsInstance )
+ {
+ try
+ {
+ if ( m_xStatusBar.is() )
+ m_xStatusBar->dispose();
+ }
+ catch ( const lang::DisposedException& )
+ {
+ }
+ }
+
+ m_xStatusBar.clear();
+ m_bDisposed = true;
+ }
+}
+
+// XUIElement
+uno::Reference< uno::XInterface > SAL_CALL ProgressBarWrapper::getRealInterface()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return uno::Reference< uno::XInterface >();
+ else
+ {
+ uno::Reference< uno::XInterface > xComp( m_xProgressBarIfacWrapper );
+ if ( !xComp.is() )
+ {
+ rtl::Reference<StatusIndicatorInterfaceWrapper> pWrapper =
+ new StatusIndicatorInterfaceWrapper( uno::Reference< lang::XComponent >(this) );
+ xComp.set(static_cast< cppu::OWeakObject* >( pWrapper.get() ),
+ uno::UNO_QUERY );
+ m_xProgressBarIfacWrapper = xComp;
+ }
+
+ return xComp;
+ }
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/recentfilesmenucontroller.cxx b/framework/source/uielement/recentfilesmenucontroller.cxx
new file mode 100644
index 000000000..dc4001a1f
--- /dev/null
+++ b/framework/source/uielement/recentfilesmenucontroller.cxx
@@ -0,0 +1,411 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <strings.hrc>
+#include <classes/fwkresid.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/mutex.hxx>
+#include <svtools/imagemgr.hxx>
+#include <svtools/popupmenucontrollerbase.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/historyoptions.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace css;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+
+#define MAX_MENU_ITEMS 99
+
+namespace {
+
+constexpr OUStringLiteral CMD_CLEAR_LIST = u".uno:ClearRecentFileList";
+constexpr OUStringLiteral CMD_OPEN_AS_TEMPLATE = u".uno:OpenTemplate";
+constexpr OUStringLiteral CMD_OPEN_REMOTE = u".uno:OpenRemote";
+
+class RecentFilesMenuController : public svt::PopupMenuControllerBase
+{
+ using svt::PopupMenuControllerBase::disposing;
+
+public:
+ RecentFilesMenuController( const uno::Reference< uno::XComponentContext >& xContext,
+ const uno::Sequence< uno::Any >& args );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.RecentFilesMenuController";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.frame.PopupMenuController"};
+ }
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const frame::FeatureStateEvent& Event ) override;
+
+ // XMenuListener
+ virtual void SAL_CALL itemSelected( const awt::MenuEvent& rEvent ) override;
+ virtual void SAL_CALL itemActivated( const awt::MenuEvent& rEvent ) override;
+
+ // XDispatchProvider
+ virtual uno::Reference< frame::XDispatch > SAL_CALL queryDispatch( const util::URL& aURL, const OUString& sTarget, sal_Int32 nFlags ) override;
+
+ // XDispatch
+ virtual void SAL_CALL dispatch( const util::URL& aURL, const uno::Sequence< beans::PropertyValue >& seqProperties ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+private:
+ virtual void impl_setPopupMenu() override;
+ void fillPopupMenu( css::uno::Reference< css::awt::XPopupMenu > const & rPopupMenu );
+ void executeEntry( sal_Int32 nIndex );
+
+ std::vector<std::pair<OUString, bool>> m_aRecentFilesItems;
+ bool m_bDisabled : 1;
+ bool m_bShowToolbarEntries;
+};
+
+RecentFilesMenuController::RecentFilesMenuController( const uno::Reference< uno::XComponentContext >& xContext,
+ const uno::Sequence< uno::Any >& args ) :
+ svt::PopupMenuControllerBase( xContext ),
+ m_bDisabled( false ),
+ m_bShowToolbarEntries( false )
+{
+ css::beans::PropertyValue aPropValue;
+ for ( uno::Any const & arg : args )
+ {
+ arg >>= aPropValue;
+ if ( aPropValue.Name == "InToolbar" )
+ {
+ aPropValue.Value >>= m_bShowToolbarEntries;
+ break;
+ }
+ }
+}
+
+void InsertItem(const css::uno::Reference<css::awt::XPopupMenu>& rPopupMenu,
+ const OUString& rCommand,
+ const css::uno::Reference<css::frame::XFrame>& rFrame)
+{
+ sal_uInt16 nItemId = rPopupMenu->getItemCount() + 1;
+
+ if (rFrame.is())
+ {
+ OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommand, aModuleName);
+ OUString aLabel(vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties));
+ OUString aTooltip(vcl::CommandInfoProvider::GetTooltipForCommand(rCommand, aProperties, rFrame));
+ css::uno::Reference<css::graphic::XGraphic> xGraphic(vcl::CommandInfoProvider::GetXGraphicForCommand(rCommand, rFrame));
+
+ rPopupMenu->insertItem(nItemId, aLabel, 0, -1);
+ rPopupMenu->setItemImage(nItemId, xGraphic, false);
+ rPopupMenu->setHelpText(nItemId, aTooltip);
+ }
+ else
+ rPopupMenu->insertItem(nItemId, OUString(), 0, -1);
+
+ rPopupMenu->setCommand(nItemId, rCommand);
+}
+
+
+// private function
+void RecentFilesMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ resetPopupMenu( rPopupMenu );
+
+ std::vector< SvtHistoryOptions::HistoryItem > aHistoryList = SvtHistoryOptions::GetList( EHistoryType::PickList );
+
+ int nPickListMenuItems = std::min<sal_Int32>( aHistoryList.size(), MAX_MENU_ITEMS );
+ m_aRecentFilesItems.clear();
+
+ if (( nPickListMenuItems > 0 ) && !m_bDisabled )
+ {
+ for ( int i = 0; i < nPickListMenuItems; i++ )
+ {
+ const SvtHistoryOptions::HistoryItem& rPickListEntry = aHistoryList[i];
+ m_aRecentFilesItems.emplace_back(rPickListEntry.sURL, rPickListEntry.isReadOnly);
+ }
+ }
+
+ if ( !m_aRecentFilesItems.empty() )
+ {
+ const sal_uInt32 nCount = m_aRecentFilesItems.size();
+ StyleSettings aIconSettings;
+ bool bIsIconsAllowed = aIconSettings.GetUseImagesInMenus();
+
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+
+ OUStringBuffer aMenuShortCut;
+ if ( i <= 9 )
+ {
+ if ( i == 9 )
+ aMenuShortCut.append( "1~0. " );
+ else
+ {
+ aMenuShortCut.append( "~N. " );
+ aMenuShortCut[ 1 ] = sal_Unicode( i + '1' );
+ }
+ }
+ else
+ {
+ aMenuShortCut.append( sal_Int32( i + 1 ) );
+ aMenuShortCut.append( ". " );
+ }
+
+ OUString aURLString = "vnd.sun.star.popup:RecentFileList?entry=" + OUString::number(i);
+
+ // Abbreviate URL
+ OUString aMenuTitle;
+ INetURLObject const aURL(m_aRecentFilesItems[i].first);
+ OUString aTipHelpText( aURL.getFSysPath( FSysStyle::Detect ) );
+
+ if ( aURL.GetProtocol() == INetProtocol::File )
+ {
+ // Do handle file URL differently: don't show the protocol, just the file name
+ aMenuTitle = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
+ }
+ else
+ {
+ // In all other URLs show the protocol name before the file name
+ aMenuTitle = INetURLObject::GetSchemeName(aURL.GetProtocol()) + ": " + aURL.getName();
+ }
+
+ aMenuShortCut.append( aMenuTitle );
+
+ rPopupMenu->insertItem(sal_uInt16( i+1 ), aMenuShortCut.makeStringAndClear(), 0, -1);
+
+ if ( bIsIconsAllowed ) {
+ // tdf#146219: don't use SvFileInformationManager::GetImageId,
+ // which needs to access the URL to detect if it's a directory
+ BitmapEx aThumbnail(SvFileInformationManager::GetFileImageId(aURL));
+ rPopupMenu->setItemImage(sal_uInt16(i + 1), Graphic(aThumbnail).GetXGraphic(), false);
+ }
+
+ rPopupMenu->setTipHelpText(sal_uInt16(i + 1), aTipHelpText);
+ rPopupMenu->setCommand(sal_uInt16(i + 1), aURLString);
+ }
+
+ rPopupMenu->insertSeparator(-1);
+ // Clear List menu entry
+ rPopupMenu->insertItem(sal_uInt16(nCount + 1), FwkResId(STR_CLEAR_RECENT_FILES), 0, -1);
+ rPopupMenu->setCommand(sal_uInt16(nCount + 1), CMD_CLEAR_LIST);
+ rPopupMenu->setHelpText(sal_uInt16(nCount + 1), FwkResId(STR_CLEAR_RECENT_FILES_HELP));
+
+ // Open remote menu entry
+ if ( m_bShowToolbarEntries )
+ {
+ rPopupMenu->insertSeparator(-1);
+ InsertItem(rPopupMenu, CMD_OPEN_AS_TEMPLATE, m_xFrame);
+ InsertItem(rPopupMenu, CMD_OPEN_REMOTE, m_xFrame);
+ }
+ }
+ else
+ {
+ if ( m_bShowToolbarEntries )
+ {
+ InsertItem(rPopupMenu, CMD_OPEN_AS_TEMPLATE, m_xFrame);
+ InsertItem(rPopupMenu, CMD_OPEN_REMOTE, m_xFrame);
+ }
+ else
+ {
+ // Add InsertSeparator(), otherwise it will display
+ // the first item icon of recent files instead of displaying no icon.
+ rPopupMenu->insertSeparator(-1);
+ // No recent documents => insert "no documents" string
+ // Do not disable it, otherwise the Toolbar controller and MenuButton
+ // will display SV_RESID_STRING_NOSELECTIONPOSSIBLE instead of STR_NODOCUMENT
+ rPopupMenu->insertItem(1, FwkResId(STR_NODOCUMENT), static_cast<sal_Int16>(MenuItemBits::NOSELECT), -1);
+ }
+ }
+}
+
+void RecentFilesMenuController::executeEntry( sal_Int32 nIndex )
+{
+ if (( nIndex < 0 ) ||
+ ( nIndex >= sal::static_int_cast<sal_Int32>( m_aRecentFilesItems.size() )))
+ return;
+
+ Sequence< PropertyValue > aArgsList{
+ comphelper::makePropertyValue("Referer", OUString( "private:user" )),
+
+ // documents in the picklist will never be opened as templates
+ comphelper::makePropertyValue("AsTemplate", false),
+
+ // Type detection needs to know which app we are opening it from.
+ comphelper::makePropertyValue("DocumentService", m_aModuleName)
+ };
+ if (m_aRecentFilesItems[nIndex].second) // tdf#149170 only add if true
+ {
+ aArgsList.realloc(aArgsList.size()+1);
+ aArgsList.getArray()[aArgsList.size()-1] = comphelper::makePropertyValue("ReadOnly", true);
+ }
+ dispatchCommand(m_aRecentFilesItems[nIndex].first, aArgsList, "_default");
+}
+
+// XEventListener
+void SAL_CALL RecentFilesMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(this);
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) );
+ m_xPopupMenu.clear();
+}
+
+// XStatusListener
+void SAL_CALL RecentFilesMenuController::statusChanged( const FeatureStateEvent& Event )
+{
+ osl::MutexGuard aLock( m_aMutex );
+ m_bDisabled = !Event.IsEnabled;
+}
+
+void SAL_CALL RecentFilesMenuController::itemSelected( const css::awt::MenuEvent& rEvent )
+{
+ Reference< css::awt::XPopupMenu > xPopupMenu;
+
+ {
+ osl::MutexGuard aLock(m_aMutex);
+ xPopupMenu = m_xPopupMenu;
+ }
+
+ if ( !xPopupMenu.is() )
+ return;
+
+ const OUString aCommand( xPopupMenu->getCommand( rEvent.MenuId ) );
+
+ if ( aCommand == CMD_CLEAR_LIST )
+ {
+ SvtHistoryOptions::Clear( EHistoryType::PickList );
+ dispatchCommand(
+ "vnd.org.libreoffice.recentdocs:ClearRecentFileList",
+ css::uno::Sequence< css::beans::PropertyValue >() );
+ }
+ else if ( aCommand == CMD_OPEN_REMOTE )
+ {
+ Sequence< PropertyValue > aArgsList( 0 );
+ dispatchCommand( CMD_OPEN_REMOTE, aArgsList );
+ }
+ else if ( aCommand == CMD_OPEN_AS_TEMPLATE )
+ {
+ Sequence< PropertyValue > aArgsList( 0 );
+ dispatchCommand( CMD_OPEN_AS_TEMPLATE, aArgsList );
+ }
+ else
+ executeEntry( rEvent.MenuId-1 );
+}
+
+void SAL_CALL RecentFilesMenuController::itemActivated( const css::awt::MenuEvent& )
+{
+ osl::MutexGuard aLock( m_aMutex );
+ impl_setPopupMenu();
+}
+
+// XPopupMenuController
+void RecentFilesMenuController::impl_setPopupMenu()
+{
+ if ( m_xPopupMenu.is() )
+ fillPopupMenu( m_xPopupMenu );
+}
+
+// XDispatchProvider
+Reference< XDispatch > SAL_CALL RecentFilesMenuController::queryDispatch(
+ const URL& aURL,
+ const OUString& /*sTarget*/,
+ sal_Int32 /*nFlags*/ )
+{
+ osl::MutexGuard aLock( m_aMutex );
+
+ throwIfDisposed();
+
+ if ( aURL.Complete.startsWith( m_aBaseURL ) )
+ return Reference< XDispatch >( this );
+ else
+ return Reference< XDispatch >();
+}
+
+// XDispatch
+void SAL_CALL RecentFilesMenuController::dispatch(
+ const URL& aURL,
+ const Sequence< PropertyValue >& /*seqProperties*/ )
+{
+ osl::MutexGuard aLock( m_aMutex );
+
+ throwIfDisposed();
+
+ if ( !aURL.Complete.startsWith( m_aBaseURL ) )
+ return;
+
+ // Parse URL to retrieve entry argument and its value
+ sal_Int32 nQueryPart = aURL.Complete.indexOf( '?', m_aBaseURL.getLength() );
+ if ( nQueryPart <= 0 )
+ return;
+
+ static const OUStringLiteral aEntryArgStr( u"entry=" );
+ sal_Int32 nEntryArg = aURL.Complete.indexOf( aEntryArgStr, nQueryPart );
+ sal_Int32 nEntryPos = nEntryArg + aEntryArgStr.getLength();
+ if (( nEntryArg <= 0 ) || ( nEntryPos >= aURL.Complete.getLength() ))
+ return;
+
+ sal_Int32 nAddArgs = aURL.Complete.indexOf( '&', nEntryPos );
+ std::u16string_view aEntryArg;
+
+ if ( nAddArgs < 0 )
+ aEntryArg = aURL.Complete.subView( nEntryPos );
+ else
+ aEntryArg = aURL.Complete.subView( nEntryPos, nAddArgs-nEntryPos );
+
+ sal_Int32 nEntry = o3tl::toInt32(aEntryArg);
+ executeEntry( nEntry );
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_RecentFilesMenuController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &args)
+{
+ return cppu::acquire(new RecentFilesMenuController(context, args));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/resourcemenucontroller.cxx b/framework/source/uielement/resourcemenucontroller.cxx
new file mode 100644
index 000000000..7a1364b41
--- /dev/null
+++ b/framework/source/uielement/resourcemenucontroller.cxx
@@ -0,0 +1,584 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/menubarmanager.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <svtools/popupmenucontrollerbase.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/embed/VerbAttributes.hpp>
+#include <com/sun/star/embed/VerbDescriptor.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/util/URL.hpp>
+
+namespace {
+
+class ResourceMenuController : public cppu::ImplInheritanceHelper< svt::PopupMenuControllerBase, css::ui::XUIConfigurationListener >
+{
+public:
+ ResourceMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Sequence< css::uno::Any >& rxArgs, bool bToolbarContainer );
+
+ // XPopupMenuController
+ virtual void SAL_CALL updatePopupMenu() override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& rEvent ) override;
+
+ // XUIConfigurationListener
+ virtual void SAL_CALL elementInserted( const css::ui::ConfigurationEvent& rEvent ) override;
+ virtual void SAL_CALL elementRemoved( const css::ui::ConfigurationEvent& rEvent ) override;
+ virtual void SAL_CALL elementReplaced( const css::ui::ConfigurationEvent& rEvent ) override;
+
+ // XMenuListener
+ virtual void SAL_CALL itemActivated( const css::awt::MenuEvent& rEvent ) override;
+ virtual void SAL_CALL itemSelected( const css::awt::MenuEvent& rEvent ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+private:
+ OUString m_aMenuURL;
+ bool m_bContextMenu;
+ bool m_bInToolbar;
+ bool m_bToolbarContainer;
+ sal_uInt16 m_nNewMenuId;
+ rtl::Reference< framework::MenuBarManager > m_xMenuBarManager;
+ css::uno::Reference< css::frame::XDispatchProvider > m_xDispatchProvider;
+ css::uno::Reference< css::container::XIndexAccess > m_xMenuContainer;
+ css::uno::Reference< css::ui::XUIConfigurationManager > m_xConfigManager, m_xModuleConfigManager;
+ void addVerbs( const css::uno::Sequence< css::embed::VerbDescriptor >& rVerbs );
+ virtual void SAL_CALL disposing() override;
+
+protected:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+};
+
+ResourceMenuController::ResourceMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Sequence< css::uno::Any >& rxArgs, bool bToolbarContainer ) :
+ ImplInheritanceHelper( rxContext ),
+ m_bContextMenu( false ),
+ m_bInToolbar( false ),
+ m_bToolbarContainer( bToolbarContainer ),
+ m_nNewMenuId( 1 ),
+ m_xContext( rxContext )
+{
+ for ( const auto& arg: rxArgs )
+ {
+ css::beans::PropertyValue aPropValue;
+ if ( arg >>= aPropValue )
+ {
+ if ( aPropValue.Name == "Value" )
+ {
+ OUString aMenuName;
+ aPropValue.Value >>= aMenuName;
+ if ( aMenuName.isEmpty() )
+ continue;
+
+ if ( m_bToolbarContainer )
+ m_aMenuURL = "private:resource/toolbar/" + aMenuName;
+ else
+ m_aMenuURL = "private:resource/popupmenu/" + aMenuName;
+ }
+ else if ( aPropValue.Name == "ResourceURL" )
+ aPropValue.Value >>= m_aMenuURL;
+ else if ( aPropValue.Name == "Frame" )
+ aPropValue.Value >>= m_xFrame;
+ else if ( aPropValue.Name == "ModuleIdentifier" )
+ aPropValue.Value >>= m_aModuleName;
+ else if ( aPropValue.Name == "DispatchProvider" )
+ aPropValue.Value >>= m_xDispatchProvider;
+ else if ( aPropValue.Name == "IsContextMenu" )
+ aPropValue.Value >>= m_bContextMenu;
+ else if ( aPropValue.Name == "InToolbar" )
+ aPropValue.Value >>= m_bInToolbar;
+ }
+ }
+ if ( m_xFrame.is() )
+ // No need to initialize again through initialize method.
+ m_bInitialized = true;
+}
+
+void ResourceMenuController::updatePopupMenu()
+{
+ if ( ( m_xMenuContainer.is() && !m_bContextMenu ) || m_aMenuURL.isEmpty() )
+ return;
+
+ if ( m_aModuleName.isEmpty() )
+ {
+ try
+ {
+ css::uno::Reference< css::frame::XModuleManager > xModuleManager( css::frame::ModuleManager::create( m_xContext ) );
+ m_aModuleName = xModuleManager->identify( m_xFrame );
+ }
+ catch( const css::uno::Exception& )
+ {}
+ }
+
+ if ( !m_xConfigManager.is() )
+ {
+ try
+ {
+ css::uno::Reference< css::frame::XController > xController( m_xFrame->getController() );
+ css::uno::Reference< css::frame::XModel > xModel( xController->getModel() );
+ css::uno::Reference< css::ui::XUIConfigurationManagerSupplier > xSupplier( xModel, css::uno::UNO_QUERY_THROW );
+ m_xConfigManager.set( xSupplier->getUIConfigurationManager() );
+ css::uno::Reference< css::ui::XUIConfiguration > xConfig( m_xConfigManager, css::uno::UNO_QUERY_THROW );
+ xConfig->addConfigurationListener( this );
+ }
+ catch( const css::uno::RuntimeException& )
+ {}
+ }
+
+ if ( !m_xModuleConfigManager.is() )
+ {
+ try
+ {
+ css::uno::Reference< css::ui::XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier(
+ css::ui::theModuleUIConfigurationManagerSupplier::get( m_xContext ) );
+ m_xModuleConfigManager.set( xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleName ) );
+ css::uno::Reference< css::ui::XUIConfiguration > xConfig( m_xModuleConfigManager, css::uno::UNO_QUERY_THROW );
+ xConfig->addConfigurationListener( this );
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ SAL_WARN( "fwk.uielement", "Invalid module identifier: " << m_aModuleName );
+ }
+ catch( const css::uno::RuntimeException& )
+ {}
+ }
+
+ if ( !m_xMenuContainer.is() && m_xConfigManager.is() )
+ {
+ try
+ {
+ m_xMenuContainer.set( m_xConfigManager->getSettings( m_aMenuURL, false ) );
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ // Not an error - element may exist only in the module.
+ }
+ catch ( const css::lang::IllegalArgumentException& )
+ {
+ SAL_WARN( "fwk.uielement", "The given URL is not valid: " << m_aMenuURL );
+ return;
+ }
+ }
+
+ if ( !m_xMenuContainer.is() && m_xModuleConfigManager.is() )
+ {
+ try
+ {
+ m_xMenuContainer.set( m_xModuleConfigManager->getSettings( m_aMenuURL, false ) );
+ }
+ catch ( const css::container::NoSuchElementException& )
+ {
+ SAL_WARN( "fwk.uielement", "Can not find settings for " << m_aMenuURL );
+ return;
+ }
+ catch ( const css::lang::IllegalArgumentException& )
+ {
+ SAL_WARN( "fwk.uielement", "The given URL is not valid: " << m_aMenuURL );
+ return;
+ }
+ }
+
+ if ( !m_xMenuContainer.is() )
+ return;
+
+ // Clear previous content.
+ if ( m_xMenuBarManager.is() )
+ {
+ m_xMenuBarManager->dispose();
+ m_xMenuBarManager.clear();
+ }
+ resetPopupMenu( m_xPopupMenu );
+ m_nNewMenuId = 1;
+
+ // Now fill the menu with the configuration data.
+ framework::MenuBarManager::FillMenu( m_nNewMenuId, comphelper::getFromUnoTunnel<VCLXMenu>( m_xPopupMenu )->GetMenu(), m_aModuleName, m_xMenuContainer, m_xDispatchProvider );
+
+ // For context menus, add object verbs.
+ if ( !m_bContextMenu )
+ return;
+
+ css::util::URL aObjectMenuURL;
+ aObjectMenuURL.Complete = ".uno:ObjectMenue";
+ m_xURLTransformer->parseStrict( aObjectMenuURL );
+ css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider( m_xFrame, css::uno::UNO_QUERY );
+ css::uno::Reference< css::frame::XDispatch > xDispatch( xDispatchProvider->queryDispatch( aObjectMenuURL, OUString(), 0 ) );
+ if ( xDispatch.is() )
+ {
+ xDispatch->addStatusListener( this, aObjectMenuURL );
+ xDispatch->removeStatusListener( this, aObjectMenuURL );
+ }
+}
+
+void ResourceMenuController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ css::uno::Sequence< css::embed::VerbDescriptor > aVerbs;
+ if ( rEvent.IsEnabled && ( rEvent.State >>= aVerbs ) )
+ addVerbs( aVerbs );
+}
+
+void ResourceMenuController::addVerbs( const css::uno::Sequence< css::embed::VerbDescriptor >& rVerbs )
+{
+ // Check if the document is read-only.
+ css::uno::Reference< css::frame::XController > xController( m_xFrame->getController() );
+ css::uno::Reference< css::frame::XStorable > xStorable;
+ if ( xController.is() )
+ xStorable.set( xController->getModel(), css::uno::UNO_QUERY );
+
+ bool bReadOnly = xStorable.is() && xStorable->isReadonly();
+ VCLXMenu* pAwtMenu = comphelper::getFromUnoTunnel<VCLXMenu>( m_xPopupMenu );
+ Menu* pVCLMenu = pAwtMenu->GetMenu();
+
+ for ( const auto& rVerb : rVerbs )
+ {
+ if ( !( rVerb.VerbAttributes & css::embed::VerbAttributes::MS_VERBATTR_ONCONTAINERMENU ) ||
+ ( bReadOnly && !( rVerb.VerbAttributes & css::embed::VerbAttributes::MS_VERBATTR_NEVERDIRTIES ) ) )
+ continue;
+
+ pVCLMenu->InsertItem( m_nNewMenuId, rVerb.VerbName );
+ pVCLMenu->SetItemCommand( m_nNewMenuId, ".uno:ObjectMenue?VerbID:short=" + OUString::number( rVerb.VerbID ) );
+ ++m_nNewMenuId;
+ }
+}
+
+void ResourceMenuController::itemActivated( const css::awt::MenuEvent& /*rEvent*/ )
+{
+ // Must initialize MenuBarManager here, because we want to let the app do context menu interception before.
+ if ( !m_xMenuBarManager.is() )
+ {
+ VCLXMenu* pAwtMenu = comphelper::getFromUnoTunnel<VCLXMenu>( m_xPopupMenu );
+ m_xMenuBarManager.set( new framework::MenuBarManager(
+ m_xContext, m_xFrame, m_xURLTransformer, m_xDispatchProvider, m_aModuleName, pAwtMenu->GetMenu(), false, !m_bContextMenu && !m_bInToolbar ) );
+ m_xFrame->addFrameActionListener( m_xMenuBarManager );
+ }
+}
+
+void ResourceMenuController::itemSelected( const css::awt::MenuEvent& /*rEvent*/ )
+{
+ // Must override this, because we are managed by MenuBarManager, so don't want the handler found in the base class.
+}
+
+void ResourceMenuController::elementInserted( const css::ui::ConfigurationEvent& rEvent )
+{
+ if ( rEvent.ResourceURL == m_aMenuURL )
+ m_xMenuContainer.clear();
+}
+
+void ResourceMenuController::elementRemoved( const css::ui::ConfigurationEvent& rEvent )
+{
+ elementInserted( rEvent );
+}
+
+void ResourceMenuController::elementReplaced( const css::ui::ConfigurationEvent& rEvent )
+{
+ elementInserted( rEvent );
+}
+
+void ResourceMenuController::disposing( const css::lang::EventObject& rEvent )
+{
+ if ( rEvent.Source == m_xConfigManager )
+ m_xConfigManager.clear();
+ else if ( rEvent.Source == m_xModuleConfigManager )
+ m_xModuleConfigManager.clear();
+ else
+ {
+ if ( m_xMenuBarManager.is() )
+ {
+ if (m_xFrame.is())
+ m_xFrame->removeFrameActionListener( m_xMenuBarManager );
+
+ m_xMenuBarManager->dispose();
+ m_xMenuBarManager.clear();
+ }
+ svt::PopupMenuControllerBase::disposing( rEvent );
+ }
+}
+
+void ResourceMenuController::disposing()
+{
+ css::uno::Reference< css::ui::XUIConfiguration > xConfig( m_xConfigManager, css::uno::UNO_QUERY );
+ if ( xConfig.is() )
+ xConfig->removeConfigurationListener( this );
+
+ css::uno::Reference< css::ui::XUIConfiguration > xModuleConfig( m_xModuleConfigManager, css::uno::UNO_QUERY );
+ if ( xModuleConfig.is() )
+ xModuleConfig->removeConfigurationListener( this );
+
+ m_xConfigManager.clear();
+ m_xModuleConfigManager.clear();
+ m_xMenuContainer.clear();
+ m_xDispatchProvider.clear();
+ if ( m_xMenuBarManager.is() )
+ {
+ if (m_xFrame.is())
+ m_xFrame->removeFrameActionListener( m_xMenuBarManager );
+
+ m_xMenuBarManager->dispose();
+ m_xMenuBarManager.clear();
+ }
+
+ svt::PopupMenuControllerBase::disposing();
+}
+
+OUString ResourceMenuController::getImplementationName()
+{
+ if ( m_bToolbarContainer )
+ return "com.sun.star.comp.framework.ToolbarAsMenuController";
+
+ return "com.sun.star.comp.framework.ResourceMenuController";
+}
+
+css::uno::Sequence< OUString > ResourceMenuController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.PopupMenuController" };
+}
+
+class SaveAsMenuController : public ResourceMenuController
+{
+public:
+ SaveAsMenuController( const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ const css::uno::Sequence< css::uno::Any >& rArgs );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+
+private:
+ virtual void impl_setPopupMenu() override;
+};
+
+SaveAsMenuController::SaveAsMenuController( const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ const css::uno::Sequence< css::uno::Any >& rArgs )
+ : ResourceMenuController( rContext, rArgs, false )
+{
+}
+
+void InsertItem(const css::uno::Reference<css::awt::XPopupMenu>& rPopupMenu,
+ const OUString& rCommand)
+{
+ sal_uInt16 nItemId = rPopupMenu->getItemCount() + 1;
+ rPopupMenu->insertItem(nItemId, OUString(), 0, -1);
+ rPopupMenu->setCommand(nItemId, rCommand);
+}
+
+void SaveAsMenuController::impl_setPopupMenu()
+{
+ SolarMutexGuard aGuard;
+
+ InsertItem(m_xPopupMenu, ".uno:SaveAs");
+ InsertItem(m_xPopupMenu, ".uno:ExportTo");
+ InsertItem(m_xPopupMenu, ".uno:SaveACopy");
+ InsertItem(m_xPopupMenu, ".uno:SaveAsTemplate");
+ m_xPopupMenu->insertSeparator(-1);
+ InsertItem(m_xPopupMenu, ".uno:SaveAsRemote");
+}
+
+OUString SaveAsMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.SaveAsMenuController";
+}
+
+class WindowListMenuController : public ResourceMenuController
+{
+public:
+ WindowListMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Sequence< css::uno::Any >& rxArgs )
+ : ResourceMenuController(rxContext, rxArgs, false) {}
+
+ // XMenuListener
+ void SAL_CALL itemActivated( const css::awt::MenuEvent& rEvent ) override;
+ void SAL_CALL itemSelected( const css::awt::MenuEvent& rEvent ) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+
+private:
+ void impl_setPopupMenu() override;
+};
+
+constexpr sal_uInt16 START_ITEMID_WINDOWLIST = 4600;
+constexpr sal_uInt16 END_ITEMID_WINDOWLIST = 4699;
+
+void WindowListMenuController::itemActivated( const css::awt::MenuEvent& rEvent )
+{
+ ResourceMenuController::itemActivated( rEvent );
+
+ // update window list
+ ::std::vector< OUString > aNewWindowListVector;
+
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext );
+
+ sal_uInt16 nActiveItemId = 0;
+ sal_uInt16 nItemId = START_ITEMID_WINDOWLIST;
+
+ css::uno::Reference< css::frame::XFrame > xCurrentFrame = xDesktop->getCurrentFrame();
+ css::uno::Reference< css::container::XIndexAccess > xList = xDesktop->getFrames();
+ sal_Int32 nFrameCount = xList->getCount();
+ aNewWindowListVector.reserve(nFrameCount);
+ for (sal_Int32 i=0; i<nFrameCount; ++i )
+ {
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ xList->getByIndex(i) >>= xFrame;
+
+ if (xFrame.is())
+ {
+ if ( xFrame == xCurrentFrame )
+ nActiveItemId = nItemId;
+
+ VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() );
+ OUString sWindowTitle;
+ if ( pWin && pWin->IsVisible() )
+ sWindowTitle = pWin->GetText();
+
+ // tdf#101658 In case the frame is embedded somewhere, LO has no control over it.
+ // So we just skip it.
+ if ( sWindowTitle.isEmpty() )
+ continue;
+
+ aNewWindowListVector.push_back( sWindowTitle );
+ ++nItemId;
+ }
+ }
+
+ {
+ SolarMutexGuard g;
+
+ VCLXMenu* pAwtMenu = comphelper::getFromUnoTunnel<VCLXMenu>( m_xPopupMenu );
+ Menu* pVCLMenu = pAwtMenu->GetMenu();
+ int nItemCount = pVCLMenu->GetItemCount();
+
+ if ( nItemCount > 0 )
+ {
+ // remove all old window list entries from menu
+ sal_uInt16 nPos = pVCLMenu->GetItemPos( START_ITEMID_WINDOWLIST );
+ for ( sal_uInt16 n = nPos; n < pVCLMenu->GetItemCount(); )
+ pVCLMenu->RemoveItem( n );
+
+ if ( pVCLMenu->GetItemType( pVCLMenu->GetItemCount()-1 ) == MenuItemType::SEPARATOR )
+ pVCLMenu->RemoveItem( pVCLMenu->GetItemCount()-1 );
+ }
+
+ if ( !aNewWindowListVector.empty() )
+ {
+ // append new window list entries to menu
+ pVCLMenu->InsertSeparator();
+ nItemId = START_ITEMID_WINDOWLIST;
+ const sal_uInt32 nCount = aNewWindowListVector.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ pVCLMenu->InsertItem( nItemId, aNewWindowListVector.at( i ), MenuItemBits::RADIOCHECK );
+ if ( nItemId == nActiveItemId )
+ pVCLMenu->CheckItem( nItemId );
+ ++nItemId;
+ }
+ }
+ }
+}
+
+void WindowListMenuController::itemSelected( const css::awt::MenuEvent& rEvent )
+{
+ if ( rEvent.MenuId < START_ITEMID_WINDOWLIST || rEvent.MenuId > END_ITEMID_WINDOWLIST )
+ return;
+
+ // window list menu item selected
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext );
+
+ sal_uInt16 nTaskId = START_ITEMID_WINDOWLIST;
+ css::uno::Reference< css::container::XIndexAccess > xList = xDesktop->getFrames();
+ sal_Int32 nCount = xList->getCount();
+ for ( sal_Int32 i=0; i<nCount; ++i )
+ {
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ xList->getByIndex(i) >>= xFrame;
+ if ( xFrame.is() && nTaskId == rEvent.MenuId )
+ {
+ VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() );
+ pWin->GrabFocus();
+ pWin->ToTop( ToTopFlags::RestoreWhenMin );
+ break;
+ }
+
+ nTaskId++;
+ }
+}
+
+void WindowListMenuController::impl_setPopupMenu()
+{
+ // Make this controller work also with initially empty
+ // menu, which PopupMenu::ImplExecute doesn't allow.
+ if (m_xPopupMenu.is() && !m_xPopupMenu->getItemCount())
+ m_xPopupMenu->insertSeparator(0);
+}
+
+OUString WindowListMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.WindowListMenuController";
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_ResourceMenuController_get_implementation(
+ css::uno::XComponentContext* context,
+ css::uno::Sequence< css::uno::Any > const & args )
+{
+ return cppu::acquire( new ResourceMenuController( context, args, false ) );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_ToolbarAsMenuController_get_implementation(
+ css::uno::XComponentContext* context,
+ css::uno::Sequence< css::uno::Any > const & args )
+{
+ return cppu::acquire( new ResourceMenuController( context, args, true ) );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_WindowListMenuController_get_implementation(
+ css::uno::XComponentContext* context,
+ css::uno::Sequence< css::uno::Any > const & args )
+{
+ return cppu::acquire( new WindowListMenuController( context, args ) );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_SaveAsMenuController_get_implementation(
+ css::uno::XComponentContext* context,
+ css::uno::Sequence< css::uno::Any > const & args )
+{
+ return cppu::acquire( new SaveAsMenuController( context, args ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/spinfieldtoolbarcontroller.cxx b/framework/source/uielement/spinfieldtoolbarcontroller.cxx
new file mode 100644
index 000000000..fdcac34fd
--- /dev/null
+++ b/framework/source/uielement/spinfieldtoolbarcontroller.cxx
@@ -0,0 +1,452 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <stdio.h>
+
+#include <uielement/spinfieldtoolbarcontroller.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <svtools/toolboxcontroller.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <vcl/event.hxx>
+#include <vcl/formatter.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::util;
+
+namespace framework
+{
+
+// Wrapper class to notify controller about events from combobox.
+// Unfortunaltly the events are notified through virtual methods instead
+// of Listeners.
+
+class SpinfieldControl final : public InterimItemWindow
+{
+public:
+ SpinfieldControl(vcl::Window* pParent, SpinfieldToolbarController* pSpinfieldToolbarController);
+ virtual ~SpinfieldControl() override;
+ virtual void dispose() override;
+
+ Formatter& GetFormatter()
+ {
+ return m_xWidget->GetFormatter();
+ }
+
+ OUString get_entry_text() const { return m_xWidget->get_text(); }
+
+ DECL_LINK(ValueChangedHdl, weld::FormattedSpinButton&, void);
+ DECL_LINK(FormatOutputHdl, LinkParamNone*, bool);
+ DECL_LINK(ParseInputHdl, sal_Int64*, TriState);
+ DECL_LINK(ModifyHdl, weld::Entry&, void);
+ DECL_LINK(ActivateHdl, weld::Entry&, bool);
+ DECL_LINK(FocusInHdl, weld::Widget&, void);
+ DECL_LINK(FocusOutHdl, weld::Widget&, void);
+ DECL_LINK(KeyInputHdl, const ::KeyEvent&, bool);
+
+private:
+ std::unique_ptr<weld::FormattedSpinButton> m_xWidget;
+ SpinfieldToolbarController* m_pSpinfieldToolbarController;
+};
+
+SpinfieldControl::SpinfieldControl(vcl::Window* pParent, SpinfieldToolbarController* pSpinfieldToolbarController)
+ : InterimItemWindow(pParent, "svt/ui/spinfieldcontrol.ui", "SpinFieldControl")
+ , m_xWidget(m_xBuilder->weld_formatted_spin_button("spinbutton"))
+ , m_pSpinfieldToolbarController(pSpinfieldToolbarController)
+{
+ InitControlBase(m_xWidget.get());
+
+ m_xWidget->connect_focus_in(LINK(this, SpinfieldControl, FocusInHdl));
+ m_xWidget->connect_focus_out(LINK(this, SpinfieldControl, FocusOutHdl));
+ Formatter& rFormatter = m_xWidget->GetFormatter();
+ rFormatter.SetOutputHdl(LINK(this, SpinfieldControl, FormatOutputHdl));
+ rFormatter.SetInputHdl(LINK(this, SpinfieldControl, ParseInputHdl));
+ m_xWidget->connect_value_changed(LINK(this, SpinfieldControl, ValueChangedHdl));
+ m_xWidget->connect_changed(LINK(this, SpinfieldControl, ModifyHdl));
+ m_xWidget->connect_activate(LINK(this, SpinfieldControl, ActivateHdl));
+ m_xWidget->connect_key_press(LINK(this, SpinfieldControl, KeyInputHdl));
+
+ // so a later narrow size request can stick
+ m_xWidget->set_width_chars(3);
+ m_xWidget->set_size_request(42, -1);
+
+ SetSizePixel(get_preferred_size());
+}
+
+IMPL_LINK(SpinfieldControl, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
+{
+ return ChildKeyInput(rKEvt);
+}
+
+IMPL_LINK(SpinfieldControl, ParseInputHdl, sal_Int64*, result, TriState)
+{
+ *result = m_xWidget->get_text().toDouble() * weld::SpinButton::Power10(m_xWidget->GetFormatter().GetDecimalDigits());
+ return TRISTATE_TRUE;
+}
+
+SpinfieldControl::~SpinfieldControl()
+{
+ disposeOnce();
+}
+
+void SpinfieldControl::dispose()
+{
+ m_pSpinfieldToolbarController = nullptr;
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+}
+
+IMPL_LINK_NOARG(SpinfieldControl, ValueChangedHdl, weld::FormattedSpinButton&, void)
+{
+ if (m_pSpinfieldToolbarController)
+ m_pSpinfieldToolbarController->execute(0);
+}
+
+IMPL_LINK_NOARG(SpinfieldControl, ModifyHdl, weld::Entry&, void)
+{
+ if (m_pSpinfieldToolbarController)
+ m_pSpinfieldToolbarController->Modify();
+}
+
+IMPL_LINK_NOARG(SpinfieldControl, FocusInHdl, weld::Widget&, void)
+{
+ if (m_pSpinfieldToolbarController)
+ m_pSpinfieldToolbarController->GetFocus();
+}
+
+IMPL_LINK_NOARG(SpinfieldControl, FocusOutHdl, weld::Widget&, void)
+{
+ if (m_pSpinfieldToolbarController)
+ m_pSpinfieldToolbarController->LoseFocus();
+}
+
+IMPL_LINK_NOARG(SpinfieldControl, ActivateHdl, weld::Entry&, bool)
+{
+ bool bConsumed = false;
+ if (m_pSpinfieldToolbarController)
+ {
+ m_pSpinfieldToolbarController->Activate();
+ bConsumed = true;
+ }
+ return bConsumed;
+}
+
+IMPL_LINK_NOARG(SpinfieldControl, FormatOutputHdl, LinkParamNone*, bool)
+{
+ OUString aText = m_pSpinfieldToolbarController->FormatOutputString(m_xWidget->GetFormatter().GetValue());
+ m_xWidget->set_text(aText);
+ return true;
+}
+
+SpinfieldToolbarController::SpinfieldToolbarController(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ ToolBox* pToolbar,
+ ToolBoxItemId nID,
+ sal_Int32 nWidth,
+ const OUString& aCommand ) :
+ ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand )
+ , m_bFloat( false )
+ , m_nMax( 0.0 )
+ , m_nMin( 0.0 )
+ , m_nValue( 0.0 )
+ , m_nStep( 0.0 )
+ , m_pSpinfieldControl( nullptr )
+{
+ m_pSpinfieldControl = VclPtr<SpinfieldControl>::Create(m_xToolbar, this);
+ if ( nWidth == 0 )
+ nWidth = 100;
+
+ // SpinFieldControl ctor has set a suitable height already
+ auto nHeight = m_pSpinfieldControl->GetSizePixel().Height();
+
+ m_pSpinfieldControl->SetSizePixel( ::Size( nWidth, nHeight ));
+ m_xToolbar->SetItemWindow( m_nID, m_pSpinfieldControl );
+}
+
+SpinfieldToolbarController::~SpinfieldToolbarController()
+{
+}
+
+void SAL_CALL SpinfieldToolbarController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ m_xToolbar->SetItemWindow( m_nID, nullptr );
+ m_pSpinfieldControl.disposeAndClear();
+
+ ComplexToolbarController::dispose();
+}
+
+Sequence<PropertyValue> SpinfieldToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const
+{
+ OUString aSpinfieldText = m_pSpinfieldControl->get_entry_text();
+
+ // Add key modifier to argument list
+ auto aArgs0 = comphelper::makePropertyValue("KeyModifier", KeyModifier);
+ auto aArgs1 = comphelper::makePropertyValue("Value", m_bFloat ? Any(aSpinfieldText.toDouble())
+ : Any(aSpinfieldText.toInt32()));
+ return { aArgs0, aArgs1 };
+}
+
+void SpinfieldToolbarController::Modify()
+{
+ notifyTextChanged(m_pSpinfieldControl->get_entry_text());
+}
+
+void SpinfieldToolbarController::GetFocus()
+{
+ notifyFocusGet();
+}
+
+void SpinfieldToolbarController::LoseFocus()
+{
+ notifyFocusLost();
+}
+
+void SpinfieldToolbarController::Activate()
+{
+ // Call execute only with non-empty text
+ if (!m_pSpinfieldControl->get_entry_text().isEmpty())
+ execute(0);
+}
+
+void SpinfieldToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand )
+{
+ OUString aValue;
+ OUString aMax;
+ OUString aMin;
+ OUString aStep;
+ bool bFloatValue( false );
+
+ if ( rControlCommand.Command == "SetStep" )
+ {
+ for ( auto const & arg : rControlCommand.Arguments )
+ {
+ if ( arg.Name == "Step" )
+ {
+ sal_Int32 nValue;
+ double fValue;
+ bool bFloat( false );
+ if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
+ aStep = bFloat ? OUString( OUString::number( fValue )) :
+ OUString( OUString::number( nValue ));
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "SetValue" )
+ {
+ for ( auto const & arg : rControlCommand.Arguments )
+ {
+ if ( arg.Name == "Value" )
+ {
+ sal_Int32 nValue;
+ double fValue;
+ bool bFloat( false );
+
+ if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
+ {
+ aValue = bFloat ? OUString( OUString::number( fValue )) :
+ OUString( OUString::number( nValue ));
+ bFloatValue = bFloat;
+ }
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "SetValues" )
+ {
+ for ( auto const & arg : rControlCommand.Arguments )
+ {
+ sal_Int32 nValue;
+ double fValue;
+ bool bFloat( false );
+
+ OUString aName = arg.Name;
+ if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
+ {
+ if ( aName == "Value" )
+ {
+ aValue = bFloat ? OUString( OUString::number( fValue )) :
+ OUString( OUString::number( nValue ));
+ bFloatValue = bFloat;
+ }
+ else if ( aName == "Step" )
+ aStep = bFloat ? OUString( OUString::number( fValue )) :
+ OUString( OUString::number( nValue ));
+ else if ( aName == "LowerLimit" )
+ aMin = bFloat ? OUString( OUString::number( fValue )) :
+ OUString( OUString::number( nValue ));
+ else if ( aName == "UpperLimit" )
+ aMax = bFloat ? OUString( OUString::number( fValue )) :
+ OUString( OUString::number( nValue ));
+ }
+ else if ( aName == "OutputFormat" )
+ arg.Value >>= m_aOutFormat;
+ }
+ }
+ else if ( rControlCommand.Command == "SetLowerLimit" )
+ {
+ for ( auto const & arg : rControlCommand.Arguments )
+ {
+ if ( arg.Name == "LowerLimit" )
+ {
+ sal_Int32 nValue;
+ double fValue;
+ bool bFloat( false );
+ if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
+ aMin = bFloat ? OUString( OUString::number( fValue )) :
+ OUString( OUString::number( nValue ));
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "SetUpperLimit" )
+ {
+ for ( auto const & arg : rControlCommand.Arguments )
+ {
+ if ( arg.Name == "UpperLimit" )
+ {
+ sal_Int32 nValue;
+ double fValue;
+ bool bFloat( false );
+ if ( impl_getValue( arg.Value, nValue, fValue, bFloat ))
+ aMax = bFloat ? OUString( OUString::number( fValue )) :
+ OUString( OUString::number( nValue ));
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "SetOutputFormat" )
+ {
+ for ( auto const & arg : rControlCommand.Arguments )
+ {
+ if ( arg.Name == "OutputFormat" )
+ {
+ arg.Value >>= m_aOutFormat;
+ break;
+ }
+ }
+ }
+
+ Formatter& rFormatter = m_pSpinfieldControl->GetFormatter();
+
+ // Check values and set members
+ if (bFloatValue)
+ rFormatter.SetDecimalDigits(2);
+ if ( !aValue.isEmpty() )
+ {
+ m_bFloat = bFloatValue;
+ m_nValue = aValue.toDouble();
+ rFormatter.SetValue(m_nValue);
+ }
+ if ( !aMax.isEmpty() )
+ {
+ m_nMax = aMax.toDouble();
+ rFormatter.SetMaxValue(m_nMax);
+ }
+ if ( !aMin.isEmpty() )
+ {
+ m_nMin = aMin.toDouble();
+ rFormatter.SetMinValue(m_nMin);
+ }
+ if ( !aStep.isEmpty() )
+ {
+ m_nStep = aStep.toDouble();
+ rFormatter.SetSpinSize(m_nStep);
+ }
+}
+
+bool SpinfieldToolbarController::impl_getValue(
+ const Any& rAny, sal_Int32& nValue, double& fValue, bool& bFloat )
+{
+ using ::com::sun::star::uno::TypeClass;
+
+ bool bValueValid( false );
+
+ bFloat = false;
+ TypeClass aTypeClass = rAny.getValueTypeClass();
+ if (( aTypeClass == TypeClass( typelib_TypeClass_LONG )) ||
+ ( aTypeClass == TypeClass( typelib_TypeClass_SHORT )) ||
+ ( aTypeClass == TypeClass( typelib_TypeClass_BYTE )))
+ bValueValid = rAny >>= nValue;
+ else if (( aTypeClass == TypeClass( typelib_TypeClass_FLOAT )) ||
+ ( aTypeClass == TypeClass( typelib_TypeClass_DOUBLE )))
+ {
+ bValueValid = rAny >>= fValue;
+ bFloat = true;
+ }
+
+ return bValueValid;
+}
+
+OUString SpinfieldToolbarController::FormatOutputString( double fValue )
+{
+ if ( m_aOutFormat.isEmpty() )
+ {
+ if ( m_bFloat )
+ return OUString::number( fValue );
+ else
+ return OUString::number( sal_Int32( fValue ));
+ }
+ else
+ {
+#ifdef _WIN32
+ sal_Unicode aBuffer[128];
+
+ aBuffer[0] = 0;
+ if ( m_bFloat )
+ _snwprintf( o3tl::toW(aBuffer), SAL_N_ELEMENTS(aBuffer), o3tl::toW(m_aOutFormat.getStr()), fValue );
+ else
+ _snwprintf( o3tl::toW(aBuffer), SAL_N_ELEMENTS(aBuffer), o3tl::toW(m_aOutFormat.getStr()), sal_Int32( fValue ));
+
+ return OUString(aBuffer);
+#else
+ // Currently we have no support for a format string using sal_Unicode. wchar_t
+ // is 32 bit on Unix platform!
+ char aBuffer[128];
+
+ OString aFormat = OUStringToOString( m_aOutFormat, osl_getThreadTextEncoding() );
+ if ( m_bFloat )
+ snprintf( aBuffer, 128, aFormat.getStr(), fValue );
+ else
+ snprintf( aBuffer, 128, aFormat.getStr(), static_cast<tools::Long>( fValue ));
+
+ sal_Int32 nSize = strlen( aBuffer );
+ OString aTmp( aBuffer, nSize );
+ return OStringToOUString( aTmp, osl_getThreadTextEncoding() );
+#endif
+ }
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/statusbar.cxx b/framework/source/uielement/statusbar.cxx
new file mode 100644
index 000000000..7189e6615
--- /dev/null
+++ b/framework/source/uielement/statusbar.cxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/statusbar.hxx>
+
+#include <vcl/svapp.hxx>
+
+namespace framework
+{
+
+FrameworkStatusBar::FrameworkStatusBar(
+ vcl::Window* pParent,
+ WinBits nWinBits ) :
+ StatusBar( pParent, nWinBits ),
+ m_pMgr( nullptr )
+{
+ // set optimal size
+ SetOutputSizePixel( CalcWindowSizePixel() );
+}
+
+void FrameworkStatusBar::SetStatusBarManager( StatusBarManager* pStatusBarManager )
+{
+ SolarMutexGuard aSolarMutexGuard;
+ m_pMgr = pStatusBarManager;
+}
+
+void FrameworkStatusBar::UserDraw(const UserDrawEvent& rUDEvt)
+{
+ if ( m_pMgr )
+ m_pMgr->UserDraw( rUDEvt );
+}
+
+void FrameworkStatusBar::Command( const CommandEvent& rEvt )
+{
+ if ( m_pMgr )
+ m_pMgr->Command( rEvt );
+}
+
+void FrameworkStatusBar::StateChanged( StateChangedType )
+{
+}
+
+void FrameworkStatusBar::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ StatusBar::DataChanged( rDCEvt );
+ if ( m_pMgr )
+ m_pMgr->DataChanged( rDCEvt );
+}
+
+void FrameworkStatusBar::MouseMove( const MouseEvent& rMEvt )
+{
+ StatusBar::MouseMove( rMEvt );
+ if ( m_pMgr )
+ m_pMgr->MouseMove( rMEvt );
+}
+
+void FrameworkStatusBar::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ StatusBar::MouseButtonDown( rMEvt );
+ if ( m_pMgr )
+ m_pMgr->MouseButtonDown( rMEvt );
+}
+
+void FrameworkStatusBar::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ StatusBar::MouseButtonUp( rMEvt );
+ if ( m_pMgr )
+ m_pMgr->MouseButtonUp( rMEvt );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/statusbaritem.cxx b/framework/source/uielement/statusbaritem.cxx
new file mode 100644
index 000000000..baf321fd6
--- /dev/null
+++ b/framework/source/uielement/statusbaritem.cxx
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/statusbaritem.hxx>
+#include <utility>
+#include <vcl/status.hxx>
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/ui/ItemStyle.hpp>
+
+using namespace com::sun::star::ui;
+
+namespace framework
+{
+
+namespace
+{
+sal_uInt16 impl_convertItemBitsToItemStyle( StatusBarItemBits nItemBits )
+{
+ sal_uInt16 nStyle( 0 );
+
+ if ( nItemBits & StatusBarItemBits::Right )
+ nStyle |= ItemStyle::ALIGN_RIGHT;
+ else if ( nItemBits & StatusBarItemBits::Left )
+ nStyle |= ItemStyle::ALIGN_LEFT;
+ else
+ nStyle |= ItemStyle::ALIGN_CENTER;
+
+ if ( nItemBits & StatusBarItemBits::Flat )
+ nStyle |= ItemStyle::DRAW_FLAT;
+ else if ( nItemBits & StatusBarItemBits::Out )
+ nStyle |= ItemStyle::DRAW_OUT3D;
+ else
+ nStyle |= ItemStyle::DRAW_IN3D;
+
+ if ( nItemBits & StatusBarItemBits::AutoSize )
+ nStyle |= ItemStyle::AUTO_SIZE;
+
+ if ( nItemBits & StatusBarItemBits::UserDraw )
+ nStyle |= ItemStyle::OWNER_DRAW;
+
+ return nStyle;
+}
+}
+
+StatusbarItem::StatusbarItem(
+ StatusBar *pStatusBar,
+ sal_uInt16 nId,
+ OUString aCommand )
+ : m_pStatusBar( pStatusBar )
+ , m_nId( nId )
+ , m_nStyle( 0 )
+ , m_aCommand(std::move( aCommand ))
+{
+ if ( m_pStatusBar )
+ m_nStyle = impl_convertItemBitsToItemStyle(
+ m_pStatusBar->GetItemBits( m_nId ) );
+}
+
+StatusbarItem::~StatusbarItem()
+{
+}
+
+void StatusbarItem::disposing(std::unique_lock<std::mutex>&)
+{
+ m_pStatusBar = nullptr;
+}
+
+OUString SAL_CALL StatusbarItem::getCommand()
+{
+ std::unique_lock aGuard( m_aMutex );
+ return m_aCommand;
+}
+
+::sal_uInt16 SAL_CALL StatusbarItem::getItemId()
+{
+ std::unique_lock aGuard( m_aMutex );
+ return m_nId;
+}
+
+::sal_uInt32 SAL_CALL StatusbarItem::getWidth()
+{
+ SolarMutexGuard aGuard;
+ if ( m_pStatusBar )
+ return m_pStatusBar->GetItemWidth( m_nId );
+
+ return ::sal_uInt32(0);
+}
+
+::sal_uInt16 SAL_CALL StatusbarItem::getStyle()
+{
+ std::unique_lock aGuard( m_aMutex );
+ return m_nStyle;
+}
+
+::sal_Int32 SAL_CALL StatusbarItem::getOffset()
+{
+ SolarMutexGuard aGuard;
+ if ( m_pStatusBar )
+ return m_pStatusBar->GetItemOffset( m_nId );
+
+ return 0;
+}
+
+css::awt::Rectangle SAL_CALL StatusbarItem::getItemRect()
+{
+ SolarMutexGuard aGuard;
+ css::awt::Rectangle aAWTRect;
+ if ( m_pStatusBar )
+ {
+ tools::Rectangle aRect = m_pStatusBar->GetItemRect( m_nId );
+ return css::awt::Rectangle( aRect.Left(),
+ aRect.Top(),
+ aRect.GetWidth(),
+ aRect.GetHeight() );
+ }
+
+ return aAWTRect;
+}
+
+OUString SAL_CALL StatusbarItem::getText()
+{
+ SolarMutexGuard aGuard;
+ if ( m_pStatusBar )
+ return m_pStatusBar->GetItemText( m_nId );
+
+ return OUString();
+}
+
+void SAL_CALL StatusbarItem::setText( const OUString& rText )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pStatusBar )
+ m_pStatusBar->SetItemText( m_nId, rText );
+}
+
+OUString SAL_CALL StatusbarItem::getHelpText()
+{
+ SolarMutexGuard aGuard;
+ if ( m_pStatusBar )
+ return m_pStatusBar->GetHelpText( m_nId );
+
+ return OUString();
+}
+
+void SAL_CALL StatusbarItem::setHelpText( const OUString& rHelpText )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pStatusBar )
+ m_pStatusBar->SetHelpText( m_nId, rHelpText );
+}
+
+OUString SAL_CALL StatusbarItem::getQuickHelpText()
+{
+ SolarMutexGuard aGuard;
+ if ( m_pStatusBar )
+ return m_pStatusBar->GetHelpText( m_nId );
+
+ return OUString();
+}
+
+void SAL_CALL StatusbarItem::setQuickHelpText( const OUString& rQuickHelpText )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pStatusBar )
+ m_pStatusBar->SetQuickHelpText( m_nId, rQuickHelpText );
+}
+
+OUString SAL_CALL StatusbarItem::getAccessibleName()
+{
+ SolarMutexGuard aGuard;
+ if ( m_pStatusBar )
+ return m_pStatusBar->GetAccessibleName( m_nId );
+
+ return OUString();
+}
+
+void SAL_CALL StatusbarItem::setAccessibleName( const OUString& rAccessibleName )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pStatusBar )
+ m_pStatusBar->SetAccessibleName( m_nId, rAccessibleName );
+}
+
+sal_Bool SAL_CALL StatusbarItem::getVisible()
+{
+ SolarMutexGuard aGuard;
+ if ( m_pStatusBar )
+ return m_pStatusBar->IsItemVisible( m_nId );
+
+ return false;
+}
+
+void SAL_CALL StatusbarItem::setVisible( sal_Bool bVisible )
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pStatusBar )
+ return;
+
+ if ( bool(bVisible) != m_pStatusBar->IsItemVisible( m_nId ) )
+ {
+ if ( bVisible )
+ m_pStatusBar->ShowItem( m_nId );
+ else
+ m_pStatusBar->HideItem( m_nId );
+ }
+}
+
+void SAL_CALL StatusbarItem::repaint( )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pStatusBar )
+ {
+ m_pStatusBar->RedrawItem( m_nId );
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/statusbarmanager.cxx b/framework/source/uielement/statusbarmanager.cxx
new file mode 100644
index 000000000..dbc305c69
--- /dev/null
+++ b/framework/source/uielement/statusbarmanager.cxx
@@ -0,0 +1,656 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/statusbarmanager.hxx>
+#include <uielement/genericstatusbarcontroller.hxx>
+
+#include <framework/sfxhelperfunctions.hxx>
+#include <framework/addonsoptions.hxx>
+#include <uielement/statusbarmerger.hxx>
+#include <uielement/statusbaritem.hxx>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/theStatusbarControllerFactory.hpp>
+#include <com/sun/star/ui/ItemStyle.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/awt/Command.hpp>
+#include <com/sun/star/ui/XStatusbarItem.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <svtools/statusbarcontroller.hxx>
+#include <tools/debug.hxx>
+
+#include <utility>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/status.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandinfoprovider.hxx>
+
+#include <cassert>
+
+using namespace ::com::sun::star;
+
+namespace framework
+{
+
+namespace
+{
+
+template< class MAP >
+struct lcl_UpdateController
+{
+ void operator()( typename MAP::value_type &rElement ) const
+ {
+ try
+ {
+ if ( rElement.second.is() )
+ rElement.second->update();
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+};
+
+template< class MAP >
+struct lcl_RemoveController
+{
+ void operator()( typename MAP::value_type &rElement ) const
+ {
+ try
+ {
+ if ( rElement.second.is() )
+ rElement.second->dispose();
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+};
+
+StatusBarItemBits impl_convertItemStyleToItemBits( sal_Int16 nStyle )
+{
+ StatusBarItemBits nItemBits( StatusBarItemBits::NONE );
+
+ if (( nStyle & css::ui::ItemStyle::ALIGN_RIGHT ) == css::ui::ItemStyle::ALIGN_RIGHT )
+ nItemBits |= StatusBarItemBits::Right;
+ else if ( nStyle & css::ui::ItemStyle::ALIGN_LEFT )
+ nItemBits |= StatusBarItemBits::Left;
+ else
+ nItemBits |= StatusBarItemBits::Center;
+
+ if (( nStyle & css::ui::ItemStyle::DRAW_FLAT ) == css::ui::ItemStyle::DRAW_FLAT )
+ nItemBits |= StatusBarItemBits::Flat;
+ else if ( nStyle & css::ui::ItemStyle::DRAW_OUT3D )
+ nItemBits |= StatusBarItemBits::Out;
+ else
+ nItemBits |= StatusBarItemBits::In;
+
+ if (( nStyle & css::ui::ItemStyle::AUTO_SIZE ) == css::ui::ItemStyle::AUTO_SIZE )
+ nItemBits |= StatusBarItemBits::AutoSize;
+ if ( nStyle & css::ui::ItemStyle::OWNER_DRAW )
+ nItemBits |= StatusBarItemBits::UserDraw;
+
+ if ( nStyle & css::ui::ItemStyle::MANDATORY )
+ nItemBits |= StatusBarItemBits::Mandatory;
+
+ return nItemBits;
+}
+
+}
+
+StatusBarManager::StatusBarManager(
+ uno::Reference< uno::XComponentContext > xContext,
+ uno::Reference< frame::XFrame > rFrame,
+ StatusBar* pStatusBar ) :
+ m_bDisposed( false ),
+ m_bFrameActionRegistered( false ),
+ m_bUpdateControllers( false ),
+ m_pStatusBar( pStatusBar ),
+ m_xFrame(std::move( rFrame )),
+ m_xContext(std::move( xContext ))
+{
+
+ m_xStatusbarControllerFactory = frame::theStatusbarControllerFactory::get(
+ ::comphelper::getProcessComponentContext());
+
+ m_pStatusBar->AdjustItemWidthsForHiDPI();
+ m_pStatusBar->SetClickHdl( LINK( this, StatusBarManager, Click ) );
+ m_pStatusBar->SetDoubleClickHdl( LINK( this, StatusBarManager, DoubleClick ) );
+}
+
+StatusBarManager::~StatusBarManager()
+{
+}
+
+StatusBar* StatusBarManager::GetStatusBar() const
+{
+ SolarMutexGuard g;
+ return m_pStatusBar;
+}
+
+void StatusBarManager::frameAction( const frame::FrameActionEvent& Action )
+{
+ SolarMutexGuard g;
+ if ( Action.Action == frame::FrameAction_CONTEXT_CHANGED )
+ UpdateControllers();
+}
+
+void SAL_CALL StatusBarManager::disposing( const lang::EventObject& Source )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ RemoveControllers();
+
+ if ( Source.Source == uno::Reference< uno::XInterface >( m_xFrame, uno::UNO_QUERY ))
+ m_xFrame.clear();
+
+ m_xContext.clear();
+}
+
+// XComponent
+void SAL_CALL StatusBarManager::dispose()
+{
+ uno::Reference< lang::XComponent > xThis(this );
+
+ {
+ lang::EventObject aEvent( xThis );
+ std::unique_lock aGuard(m_mutex);
+ m_aListenerContainer.disposeAndClear( aGuard, aEvent );
+ }
+ {
+ SolarMutexGuard g;
+ if ( m_bDisposed )
+ return;
+
+ RemoveControllers();
+
+ // destroy the item data
+ for ( sal_uInt16 n = 0; n < m_pStatusBar->GetItemCount(); n++ )
+ {
+ AddonStatusbarItemData *pUserData = static_cast< AddonStatusbarItemData *>(
+ m_pStatusBar->GetItemData( m_pStatusBar->GetItemId( n ) ) );
+ delete pUserData;
+ }
+
+ m_pStatusBar.disposeAndClear();
+
+ if ( m_bFrameActionRegistered && m_xFrame.is() )
+ {
+ try
+ {
+ m_xFrame->removeFrameActionListener( uno::Reference< frame::XFrameActionListener >(this) );
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+ }
+
+ m_xFrame.clear();
+ m_xContext.clear();
+
+ m_bDisposed = true;
+ }
+}
+
+void SAL_CALL StatusBarManager::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw lang::DisposedException();
+
+ std::unique_lock aGuard(m_mutex);
+ m_aListenerContainer.addInterface( aGuard, xListener );
+}
+
+void SAL_CALL StatusBarManager::removeEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ std::unique_lock aGuard(m_mutex);
+ m_aListenerContainer.removeInterface( aGuard, xListener );
+}
+
+// XUIConfigurationListener
+void SAL_CALL StatusBarManager::elementInserted( const css::ui::ConfigurationEvent& )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+}
+
+void SAL_CALL StatusBarManager::elementRemoved( const css::ui::ConfigurationEvent& )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+}
+
+void SAL_CALL StatusBarManager::elementReplaced( const css::ui::ConfigurationEvent& )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+}
+
+void StatusBarManager::UpdateControllers()
+{
+ if ( !m_bUpdateControllers )
+ {
+ m_bUpdateControllers = true;
+ std::for_each( m_aControllerMap.begin(),
+ m_aControllerMap.end(),
+ lcl_UpdateController< StatusBarControllerMap >() );
+ }
+ m_bUpdateControllers = false;
+}
+
+void StatusBarManager::RemoveControllers()
+{
+ DBG_TESTSOLARMUTEX();
+ assert(!m_bDisposed);
+
+ std::for_each( m_aControllerMap.begin(),
+ m_aControllerMap.end(),
+ lcl_RemoveController< StatusBarControllerMap >() );
+ m_aControllerMap.clear();
+}
+
+void StatusBarManager::CreateControllers()
+{
+ uno::Reference< awt::XWindow > xStatusbarWindow = VCLUnoHelper::GetInterface( m_pStatusBar );
+
+ for ( sal_uInt16 i = 0; i < m_pStatusBar->GetItemCount(); i++ )
+ {
+ sal_uInt16 nId = m_pStatusBar->GetItemId( i );
+ if ( nId == 0 )
+ continue;
+
+ OUString aCommandURL( m_pStatusBar->GetItemCommand( nId ));
+ bool bInit( true );
+ uno::Reference< frame::XStatusbarController > xController;
+ AddonStatusbarItemData *pItemData = static_cast< AddonStatusbarItemData *>( m_pStatusBar->GetItemData( nId ) );
+ uno::Reference< ui::XStatusbarItem > xStatusbarItem = new StatusbarItem( m_pStatusBar, nId, aCommandURL );
+
+ beans::PropertyValue aPropValue;
+ std::vector< uno::Any > aPropVector;
+
+ aPropValue.Name = "CommandURL";
+ aPropValue.Value <<= aCommandURL;
+ aPropVector.push_back( uno::Any( aPropValue ) );
+
+ aPropValue.Name = "ModuleIdentifier";
+ aPropValue.Value <<= OUString();
+ aPropVector.push_back( uno::Any( aPropValue ) );
+
+ aPropValue.Name = "Frame";
+ aPropValue.Value <<= m_xFrame;
+ aPropVector.push_back( uno::Any( aPropValue ) );
+
+ // TODO remove this
+ aPropValue.Name = "ServiceManager";
+ aPropValue.Value <<= uno::Reference<lang::XMultiServiceFactory>(m_xContext->getServiceManager(), uno::UNO_QUERY_THROW);
+ aPropVector.push_back( uno::Any( aPropValue ) );
+
+ aPropValue.Name = "ParentWindow";
+ aPropValue.Value <<= xStatusbarWindow;
+ aPropVector.push_back( uno::Any( aPropValue ) );
+
+ // TODO still needing with the css::ui::XStatusbarItem?
+ aPropValue.Name = "Identifier";
+ aPropValue.Value <<= nId;
+ aPropVector.push_back( uno::Any( aPropValue ) );
+
+ aPropValue.Name = "StatusbarItem";
+ aPropValue.Value <<= xStatusbarItem;
+ aPropVector.push_back( uno::Any( aPropValue ) );
+
+ uno::Sequence< uno::Any > aArgs( comphelper::containerToSequence( aPropVector ) );
+
+ // 1) UNO Statusbar controllers, registered in Controllers.xcu
+ if ( m_xStatusbarControllerFactory.is() &&
+ m_xStatusbarControllerFactory->hasController( aCommandURL, "" ))
+ {
+ xController.set(m_xStatusbarControllerFactory->createInstanceWithArgumentsAndContext(
+ aCommandURL, aArgs, m_xContext ),
+ uno::UNO_QUERY );
+ bInit = false; // Initialization is done through the factory service
+ }
+
+ if ( !xController.is() )
+ {
+ // 2) Old SFX2 Statusbar controllers
+ xController = CreateStatusBarController( m_xFrame, m_pStatusBar, nId, aCommandURL );
+ if ( !xController )
+ {
+ // 3) Is Add-on? Generic statusbar controller
+ if ( pItemData )
+ {
+ xController = new GenericStatusbarController( m_xContext,
+ m_xFrame,
+ xStatusbarItem,
+ pItemData );
+ }
+ else
+ {
+ // 4) Default Statusbar controller
+ xController = new svt::StatusbarController( m_xContext, m_xFrame, aCommandURL, nId );
+ }
+ }
+ }
+
+ m_aControllerMap[nId] = xController;
+ if ( bInit )
+ {
+ xController->initialize( aArgs );
+ }
+ }
+
+ // add frame action listeners
+ if ( !m_bFrameActionRegistered && m_xFrame.is() )
+ {
+ m_bFrameActionRegistered = true;
+ m_xFrame->addFrameActionListener( uno::Reference< frame::XFrameActionListener >(this) );
+ }
+}
+
+void StatusBarManager::FillStatusBar( const uno::Reference< container::XIndexAccess >& rItemContainer )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed || !m_pStatusBar )
+ return;
+
+ sal_uInt16 nId( 1 );
+
+ RemoveControllers();
+
+ // reset and fill command map
+ m_pStatusBar->Clear();
+ m_aControllerMap.clear();// TODO already done in RemoveControllers
+
+ for ( sal_Int32 n = 0; n < rItemContainer->getCount(); n++ )
+ {
+ uno::Sequence< beans::PropertyValue > aProps;
+ OUString aCommandURL;
+ sal_Int16 nOffset( 0 );
+ sal_Int16 nStyle( 0 );
+ sal_Int16 nWidth( 0 );
+ sal_uInt16 nType( css::ui::ItemType::DEFAULT );
+
+ try
+ {
+ if ( rItemContainer->getByIndex( n ) >>= aProps )
+ {
+ for ( beans::PropertyValue const & prop : std::as_const(aProps) )
+ {
+ if ( prop.Name == "CommandURL" )
+ {
+ prop.Value >>= aCommandURL;
+ }
+ else if ( prop.Name == "Style" )
+ {
+ prop.Value >>= nStyle;
+ }
+ else if ( prop.Name == "Type" )
+ {
+ prop.Value >>= nType;
+ }
+ else if ( prop.Name == "Width" )
+ {
+ prop.Value >>= nWidth;
+ }
+ else if ( prop.Name == "Offset" )
+ {
+ prop.Value >>= nOffset;
+ }
+ }
+
+ if (( nType == css::ui::ItemType::DEFAULT ) && !aCommandURL.isEmpty() )
+ {
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, "");
+ OUString aString(vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
+ StatusBarItemBits nItemBits( impl_convertItemStyleToItemBits( nStyle ));
+
+ m_pStatusBar->InsertItem( nId, nWidth, nItemBits, nOffset );
+ m_pStatusBar->SetItemCommand( nId, aCommandURL );
+ m_pStatusBar->SetAccessibleName( nId, aString );
+ ++nId;
+ }
+ }
+ }
+ catch ( const css::lang::IndexOutOfBoundsException& )
+ {
+ break;
+ }
+ }
+
+ // Statusbar Merging
+ constexpr sal_uInt16 STATUSBAR_ITEM_STARTID = 1000;
+ MergeStatusbarInstructionContainer aMergeInstructions = AddonsOptions().GetMergeStatusbarInstructions();
+ if ( !aMergeInstructions.empty() )
+ {
+ const sal_uInt32 nCount = aMergeInstructions.size();
+ sal_uInt16 nItemId( STATUSBAR_ITEM_STARTID );
+
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ MergeStatusbarInstruction &rInstruction = aMergeInstructions[i];
+ if ( !StatusbarMerger::IsCorrectContext( rInstruction.aMergeContext ) )
+ continue;
+
+ AddonStatusbarItemContainer aItems;
+ StatusbarMerger::ConvertSeqSeqToVector( rInstruction.aMergeStatusbarItems, aItems );
+
+ sal_uInt16 nRefPos = StatusbarMerger::FindReferencePos( m_pStatusBar, rInstruction.aMergePoint );
+ if ( nRefPos != STATUSBAR_ITEM_NOTFOUND )
+ {
+ StatusbarMerger::ProcessMergeOperation( m_pStatusBar,
+ nRefPos,
+ nItemId,
+ rInstruction.aMergeCommand,
+ rInstruction.aMergeCommandParameter,
+ aItems );
+ }
+ else
+ {
+ StatusbarMerger::ProcessMergeFallback( m_pStatusBar,
+ nItemId,
+ rInstruction.aMergeCommand,
+ rInstruction.aMergeCommandParameter,
+ aItems );
+ }
+ }
+ }
+
+ // Create controllers
+ CreateControllers();
+
+ // Notify controllers that they are now correctly initialized and can start listening
+ UpdateControllers();
+}
+
+void StatusBarManager::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ SolarMutexClearableGuard aGuard;
+
+ if ((( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) ||
+ ( rDCEvt.GetType() == DataChangedEventType::FONTS ) ||
+ ( rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION ) ||
+ ( rDCEvt.GetType() == DataChangedEventType::DISPLAY )) &&
+ ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ))
+ {
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ css::uno::Reference< css::beans::XPropertySet > xPropSet( m_xFrame, css::uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
+ if ( xLayoutManager.is() )
+ {
+ aGuard.clear();
+ xLayoutManager->doLayout();
+ }
+ }
+}
+
+void StatusBarManager::UserDraw( const UserDrawEvent& rUDEvt )
+{
+ SolarMutexClearableGuard aGuard;
+
+ if ( m_bDisposed )
+ return;
+
+ sal_uInt16 nId( rUDEvt.GetItemId() );
+ StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId );
+ if (( nId <= 0 ) || ( it == m_aControllerMap.end() ))
+ return;
+
+ uno::Reference< frame::XStatusbarController > xController( it->second );
+ if (xController.is() && rUDEvt.GetRenderContext())
+ {
+ uno::Reference< awt::XGraphics > xGraphics = rUDEvt.GetRenderContext()->CreateUnoGraphics();
+
+ awt::Rectangle aRect( rUDEvt.GetRect().Left(),
+ rUDEvt.GetRect().Top(),
+ rUDEvt.GetRect().GetWidth(),
+ rUDEvt.GetRect().GetHeight() );
+ aGuard.clear();
+ xController->paint(xGraphics, aRect, 0);
+ }
+}
+
+void StatusBarManager::Command( const CommandEvent& rEvt )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ if ( rEvt.GetCommand() != CommandEventId::ContextMenu )
+ return;
+
+ sal_uInt16 nId = m_pStatusBar->GetItemId( rEvt.GetMousePosPixel() );
+ StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId );
+ if (( nId > 0 ) && ( it != m_aControllerMap.end() ))
+ {
+ uno::Reference< frame::XStatusbarController > xController( it->second );
+ if ( xController.is() )
+ {
+ awt::Point aPos;
+ aPos.X = rEvt.GetMousePosPixel().X();
+ aPos.Y = rEvt.GetMousePosPixel().Y();
+ xController->command( aPos, awt::Command::CONTEXTMENU, true, uno::Any() );
+ }
+ }
+}
+
+void StatusBarManager::MouseMove( const MouseEvent& rMEvt )
+{
+ MouseButton(rMEvt,&frame::XStatusbarController::mouseMove);
+}
+
+void StatusBarManager::MouseButton( const MouseEvent& rMEvt ,sal_Bool ( SAL_CALL frame::XStatusbarController::*_pMethod )(const css::awt::MouseEvent&))
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ sal_uInt16 nId = m_pStatusBar->GetItemId( rMEvt.GetPosPixel() );
+ StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId );
+ if (( nId <= 0 ) || ( it == m_aControllerMap.end() ))
+ return;
+
+ uno::Reference< frame::XStatusbarController > xController( it->second );
+ if ( xController.is() )
+ {
+ css::awt::MouseEvent aMouseEvent;
+ aMouseEvent.Buttons = rMEvt.GetButtons();
+ aMouseEvent.X = rMEvt.GetPosPixel().X();
+ aMouseEvent.Y = rMEvt.GetPosPixel().Y();
+ aMouseEvent.ClickCount = rMEvt.GetClicks();
+ (xController.get()->*_pMethod)( aMouseEvent);
+ }
+}
+
+void StatusBarManager::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ MouseButton(rMEvt,&frame::XStatusbarController::mouseButtonDown);
+}
+
+void StatusBarManager::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ MouseButton(rMEvt,&frame::XStatusbarController::mouseButtonUp);
+}
+
+IMPL_LINK_NOARG(StatusBarManager, Click, StatusBar*, void)
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ sal_uInt16 nId = m_pStatusBar->GetCurItemId();
+ StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId );
+ if (( nId > 0 ) && ( it != m_aControllerMap.end() ))
+ {
+ uno::Reference< frame::XStatusbarController > xController( it->second );
+ if ( xController.is() )
+ {
+ const Point aVCLPos = m_pStatusBar->GetPointerPosPixel();
+ const awt::Point aAWTPoint( aVCLPos.X(), aVCLPos.Y() );
+ xController->click( aAWTPoint );
+ }
+ }
+}
+
+IMPL_LINK_NOARG(StatusBarManager, DoubleClick, StatusBar*, void)
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ sal_uInt16 nId = m_pStatusBar->GetCurItemId();
+ StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId );
+ if (( nId > 0 ) && ( it != m_aControllerMap.end() ))
+ {
+ uno::Reference< frame::XStatusbarController > xController( it->second );
+ if ( xController.is() )
+ {
+ const Point aVCLPos = m_pStatusBar->GetPointerPosPixel();
+ const awt::Point aAWTPoint( aVCLPos.X(), aVCLPos.Y() );
+ xController->doubleClick( aAWTPoint );
+ }
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/statusbarmerger.cxx b/framework/source/uielement/statusbarmerger.cxx
new file mode 100644
index 000000000..c8e6633be
--- /dev/null
+++ b/framework/source/uielement/statusbarmerger.cxx
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <uielement/statusbarmerger.hxx>
+#include <o3tl/string_view.hxx>
+
+using com::sun::star::beans::PropertyValue;
+using com::sun::star::uno::Sequence;
+
+namespace framework
+{
+namespace {
+
+const char16_t MERGECOMMAND_ADDAFTER[] = u"AddAfter";
+const char16_t MERGECOMMAND_ADDBEFORE[] = u"AddBefore";
+const char16_t MERGECOMMAND_REPLACE[] = u"Replace";
+const char16_t MERGECOMMAND_REMOVE[] = u"Remove";
+
+void lcl_ConvertSequenceToValues(
+ const Sequence< PropertyValue > &rSequence,
+ AddonStatusbarItem &rItem )
+{
+ OUString sAlignment;
+ bool bAutoSize = false;
+ bool bOwnerDraw = false;
+ bool bMandatory = true;
+
+ for ( PropertyValue const & aPropVal : rSequence )
+ {
+ if ( aPropVal.Name == "URL" )
+ aPropVal.Value >>= rItem.aCommandURL;
+ else if ( aPropVal.Name == "Title" )
+ aPropVal.Value >>= rItem.aLabel;
+ else if ( aPropVal.Name == "Context" )
+ aPropVal.Value >>= rItem.aContext;
+ else if ( aPropVal.Name == "Alignment" )
+ aPropVal.Value >>= sAlignment;
+ else if ( aPropVal.Name == "AutoSize" )
+ aPropVal.Value >>= bAutoSize;
+ else if ( aPropVal.Name == "OwnerDraw" )
+ aPropVal.Value >>= bOwnerDraw;
+ else if ( aPropVal.Name == "Mandatory" )
+ aPropVal.Value >>= bMandatory;
+ else if ( aPropVal.Name == "Width" )
+ {
+ sal_Int32 aWidth = 0;
+ aPropVal.Value >>= aWidth;
+ rItem.nWidth = sal_uInt16( aWidth );
+ }
+ }
+
+ StatusBarItemBits nItemBits(StatusBarItemBits::NONE);
+ if ( bAutoSize )
+ nItemBits |= StatusBarItemBits::AutoSize;
+ if ( bOwnerDraw )
+ nItemBits |= StatusBarItemBits::UserDraw;
+ if ( bMandatory )
+ nItemBits |= StatusBarItemBits::Mandatory;
+ if ( sAlignment == "center" )
+ nItemBits |= StatusBarItemBits::Center;
+ else if ( sAlignment == "right" )
+ nItemBits |= StatusBarItemBits::Right;
+ else
+ // if unset, defaults to left alignment
+ nItemBits |= StatusBarItemBits::Left;
+ rItem.nItemBits = nItemBits;
+}
+
+void lcl_CreateStatusbarItem( StatusBar* pStatusbar,
+ sal_uInt16 nPos,
+ sal_uInt16 nItemId,
+ const AddonStatusbarItem& rAddonItem )
+{
+ pStatusbar->InsertItem( nItemId,
+ rAddonItem.nWidth,
+ rAddonItem.nItemBits,
+ STATUSBAR_OFFSET,
+ nPos );
+ pStatusbar->SetItemCommand( nItemId, rAddonItem.aCommandURL );
+ pStatusbar->SetQuickHelpText( nItemId, rAddonItem.aLabel );
+ pStatusbar->SetAccessibleName( nItemId, rAddonItem.aLabel );
+
+ // add-on specific data
+ AddonStatusbarItemData *pUserData = new AddonStatusbarItemData;
+ pUserData->aLabel = rAddonItem.aLabel;
+ pStatusbar->SetItemData( nItemId, pUserData );
+}
+
+bool lcl_MergeItems( StatusBar* pStatusbar,
+ sal_uInt16 nPos,
+ sal_uInt16 nModIndex,
+ sal_uInt16& rItemId,
+ const AddonStatusbarItemContainer& rAddonItems )
+{
+ const sal_uInt16 nSize( rAddonItems.size() );
+ for ( sal_Int32 i = 0; i < nSize; i++ )
+ {
+ const AddonStatusbarItem& rItem = rAddonItems[i];
+ if ( !StatusbarMerger::IsCorrectContext( rItem.aContext ) )
+ continue;
+
+ sal_uInt16 nInsPos = nPos + nModIndex + i;
+ if ( nInsPos > pStatusbar->GetItemCount() )
+ nInsPos = STATUSBAR_APPEND;
+
+ lcl_CreateStatusbarItem( pStatusbar, nInsPos, rItemId, rItem );
+ ++rItemId;
+ }
+
+ return true;
+}
+
+bool lcl_ReplaceItem( StatusBar* pStatusbar,
+ sal_uInt16 nPos,
+ sal_uInt16& rItemId,
+ const AddonStatusbarItemContainer& rAddonToolbarItems )
+{
+ pStatusbar->RemoveItem( pStatusbar->GetItemId( nPos ) );
+ return lcl_MergeItems( pStatusbar, nPos, 0, rItemId, rAddonToolbarItems );
+}
+
+bool lcl_RemoveItems( StatusBar* pStatusbar,
+ sal_uInt16 nPos,
+ std::u16string_view rMergeCommandParameter )
+{
+ sal_Int32 nCount = o3tl::toInt32(rMergeCommandParameter);
+ if ( nCount > 0 )
+ {
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ if ( nPos < pStatusbar->GetItemCount() )
+ pStatusbar->RemoveItem( nPos );
+ }
+ }
+ return true;
+}
+
+}
+
+bool StatusbarMerger::IsCorrectContext(
+ std::u16string_view rContext )
+{
+ return rContext.empty();
+}
+
+bool StatusbarMerger::ConvertSeqSeqToVector(
+ const Sequence< Sequence< PropertyValue > > &rSequence,
+ AddonStatusbarItemContainer& rContainer )
+{
+ for ( auto const & i : rSequence )
+ {
+ AddonStatusbarItem aStatusBarItem;
+ lcl_ConvertSequenceToValues( i, aStatusBarItem );
+ rContainer.push_back( aStatusBarItem );
+ }
+
+ return true;
+}
+
+sal_uInt16 StatusbarMerger::FindReferencePos(
+ StatusBar* pStatusbar,
+ std::u16string_view rReferencePoint )
+{
+ for ( sal_uInt16 nPos = 0; nPos < pStatusbar->GetItemCount(); nPos++ )
+ {
+ const OUString rCmd = pStatusbar->GetItemCommand( pStatusbar->GetItemId( nPos ) );
+ if ( rReferencePoint == rCmd )
+ return nPos;
+ }
+
+ return STATUSBAR_ITEM_NOTFOUND;
+}
+
+bool StatusbarMerger::ProcessMergeOperation(
+ StatusBar* pStatusbar,
+ sal_uInt16 nPos,
+ sal_uInt16& rItemId,
+ std::u16string_view rMergeCommand,
+ std::u16string_view rMergeCommandParameter,
+ const AddonStatusbarItemContainer& rItems )
+{
+ if ( rMergeCommand == MERGECOMMAND_ADDAFTER )
+ return lcl_MergeItems( pStatusbar, nPos, 1, rItemId, rItems );
+ else if ( rMergeCommand == MERGECOMMAND_ADDBEFORE )
+ return lcl_MergeItems( pStatusbar, nPos, 0, rItemId, rItems );
+ else if ( rMergeCommand == MERGECOMMAND_REPLACE )
+ return lcl_ReplaceItem( pStatusbar, nPos, rItemId, rItems );
+ else if ( rMergeCommand == MERGECOMMAND_REMOVE )
+ return lcl_RemoveItems( pStatusbar, nPos, rMergeCommandParameter );
+
+ return false;
+}
+
+bool StatusbarMerger::ProcessMergeFallback(
+ StatusBar* pStatusbar,
+ sal_uInt16& rItemId,
+ std::u16string_view rMergeCommand,
+ std::u16string_view rMergeFallback,
+ const AddonStatusbarItemContainer& rItems )
+{
+ // fallback IGNORE or REPLACE/REMOVE item not found
+ if (( rMergeFallback == u"Ignore" ) ||
+ ( rMergeCommand == MERGECOMMAND_REPLACE ) ||
+ ( rMergeCommand == MERGECOMMAND_REMOVE ) )
+ {
+ return true;
+ }
+ else if (( rMergeCommand == MERGECOMMAND_ADDBEFORE ) ||
+ ( rMergeCommand == MERGECOMMAND_ADDAFTER ) )
+ {
+ if ( rMergeFallback == u"AddFirst" )
+ return lcl_MergeItems( pStatusbar, 0, 0, rItemId, rItems );
+ else if ( rMergeFallback == u"AddLast" )
+ return lcl_MergeItems( pStatusbar, STATUSBAR_APPEND, 0, rItemId, rItems );
+ }
+
+ return false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/statusbarwrapper.cxx b/framework/source/uielement/statusbarwrapper.cxx
new file mode 100644
index 000000000..7627b2f0a
--- /dev/null
+++ b/framework/source/uielement/statusbarwrapper.cxx
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/statusbarwrapper.hxx>
+
+#include <uielement/statusbar.hxx>
+
+#include <com/sun/star/ui/UIElementType.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <tools/solar.h>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+using namespace com::sun::star::awt;
+using namespace ::com::sun::star::ui;
+
+namespace framework
+{
+
+StatusBarWrapper::StatusBarWrapper(
+ css::uno::Reference< css::uno::XComponentContext > xContext
+ )
+ : UIConfigElementWrapperBase( UIElementType::STATUSBAR ),
+ m_xContext(std::move( xContext ))
+{
+}
+
+StatusBarWrapper::~StatusBarWrapper()
+{
+}
+
+void SAL_CALL StatusBarWrapper::dispose()
+{
+ Reference< XComponent > xThis(this);
+
+ css::lang::EventObject aEvent( xThis );
+ m_aListenerContainer.disposeAndClear( aEvent );
+
+ SolarMutexGuard g;
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( m_xStatusBarManager.is() )
+ m_xStatusBarManager->dispose();
+ m_xStatusBarManager.clear();
+ m_xConfigSource.clear();
+ m_xConfigData.clear();
+ m_xContext.clear();
+
+ m_bDisposed = true;
+
+}
+
+// XInitialization
+void SAL_CALL StatusBarWrapper::initialize( const Sequence< Any >& aArguments )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( m_bInitialized )
+ return;
+
+ UIConfigElementWrapperBase::initialize( aArguments );
+
+ Reference< XFrame > xFrame( m_xWeakFrame );
+ if ( !(xFrame.is() && m_xConfigSource.is()) )
+ return;
+
+ // Create VCL based toolbar which will be filled with settings data
+ StatusBar* pStatusBar( nullptr );
+ rtl::Reference<StatusBarManager> pStatusBarManager;
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() );
+ if ( pWindow )
+ {
+ sal_uLong nStyles = WinBits( WB_LEFT | WB_3DLOOK );
+
+ pStatusBar = VclPtr<FrameworkStatusBar>::Create( pWindow, nStyles );
+ pStatusBarManager = new StatusBarManager( m_xContext, xFrame, pStatusBar );
+ static_cast<FrameworkStatusBar*>(pStatusBar)->SetStatusBarManager( pStatusBarManager.get() );
+ m_xStatusBarManager = pStatusBarManager;
+ }
+ }
+
+ try
+ {
+ m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false );
+ if ( m_xConfigData.is() && pStatusBar && pStatusBarManager )
+ {
+ // Fill statusbar with container contents
+ pStatusBarManager->FillStatusBar( m_xConfigData );
+ }
+ }
+ catch ( const NoSuchElementException& )
+ {
+ }
+}
+
+// XUIElementSettings
+void SAL_CALL StatusBarWrapper::updateSettings()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !(m_bPersistent &&
+ m_xConfigSource.is() &&
+ m_xStatusBarManager.is()) )
+ return;
+
+ try
+ {
+ StatusBarManager* pStatusBarManager = static_cast< StatusBarManager *>( m_xStatusBarManager.get() );
+
+ m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false );
+ if ( m_xConfigData.is() )
+ pStatusBarManager->FillStatusBar( m_xConfigData );
+ }
+ catch ( const NoSuchElementException& )
+ {
+ }
+}
+
+Reference< XInterface > SAL_CALL StatusBarWrapper::getRealInterface()
+{
+ SolarMutexGuard g;
+
+ if ( m_xStatusBarManager.is() )
+ {
+ StatusBarManager* pStatusBarManager = static_cast< StatusBarManager *>( m_xStatusBarManager.get() );
+ if ( pStatusBarManager )
+ {
+ vcl::Window* pWindow = pStatusBarManager->GetStatusBar();
+ if ( pWindow )
+ return Reference< XInterface >( VCLUnoHelper::GetInterface( pWindow ), UNO_QUERY );
+ }
+ }
+
+ return Reference< XInterface >();
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/statusindicatorinterfacewrapper.cxx b/framework/source/uielement/statusindicatorinterfacewrapper.cxx
new file mode 100644
index 000000000..ba796036b
--- /dev/null
+++ b/framework/source/uielement/statusindicatorinterfacewrapper.cxx
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/statusindicatorinterfacewrapper.hxx>
+#include <uielement/progressbarwrapper.hxx>
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+
+namespace framework
+{
+
+StatusIndicatorInterfaceWrapper::StatusIndicatorInterfaceWrapper(
+ const css::uno::Reference< css::lang::XComponent >& rStatusIndicatorImpl ) :
+ m_xStatusIndicatorImpl( rStatusIndicatorImpl )
+{
+}
+
+StatusIndicatorInterfaceWrapper::~StatusIndicatorInterfaceWrapper()
+{
+}
+
+void SAL_CALL StatusIndicatorInterfaceWrapper::start(
+ const OUString& sText,
+ sal_Int32 nRange )
+{
+ Reference< XComponent > xComp( m_xStatusIndicatorImpl );
+ if ( xComp.is() )
+ {
+ ProgressBarWrapper* pProgressBar = static_cast<ProgressBarWrapper*>(xComp.get());
+ if ( pProgressBar )
+ pProgressBar->start( sText, nRange );
+ }
+}
+
+void SAL_CALL StatusIndicatorInterfaceWrapper::end()
+{
+ Reference< XComponent > xComp( m_xStatusIndicatorImpl );
+ if ( xComp.is() )
+ {
+ ProgressBarWrapper* pProgressBar = static_cast<ProgressBarWrapper*>(xComp.get());
+ if ( pProgressBar )
+ pProgressBar->end();
+ }
+}
+
+void SAL_CALL StatusIndicatorInterfaceWrapper::reset()
+{
+ Reference< XComponent > xComp( m_xStatusIndicatorImpl );
+ if ( xComp.is() )
+ {
+ ProgressBarWrapper* pProgressBar = static_cast<ProgressBarWrapper*>(xComp.get());
+ if ( pProgressBar )
+ pProgressBar->reset();
+ }
+}
+
+void SAL_CALL StatusIndicatorInterfaceWrapper::setText(
+ const OUString& sText )
+{
+ Reference< XComponent > xComp( m_xStatusIndicatorImpl );
+ if ( xComp.is() )
+ {
+ ProgressBarWrapper* pProgressBar = static_cast<ProgressBarWrapper*>(xComp.get());
+ if ( pProgressBar )
+ pProgressBar->setText( sText );
+ }
+}
+
+void SAL_CALL StatusIndicatorInterfaceWrapper::setValue(
+ sal_Int32 nValue )
+{
+ Reference< XComponent > xComp( m_xStatusIndicatorImpl );
+ if ( xComp.is() )
+ {
+ ProgressBarWrapper* pProgressBar = static_cast<ProgressBarWrapper*>(xComp.get());
+ if ( pProgressBar )
+ pProgressBar->setValue( nValue );
+ }
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/styletoolbarcontroller.cxx b/framework/source/uielement/styletoolbarcontroller.cxx
new file mode 100644
index 000000000..31aed5336
--- /dev/null
+++ b/framework/source/uielement/styletoolbarcontroller.cxx
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <uielement/styletoolbarcontroller.hxx>
+
+#include <tools/urlobj.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/status/Template.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+namespace {
+
+OUString MapFamilyToCommand( std::u16string_view rFamily )
+{
+ if ( rFamily == u"ParagraphStyles" ||
+ rFamily == u"CellStyles" || // In sc
+ rFamily == u"graphics" ) // In sd
+ return ".uno:ParaStyle";
+ else if ( rFamily == u"CharacterStyles" )
+ return ".uno:CharStyle";
+ else if ( rFamily == u"PageStyles" )
+ return ".uno:PageStyle";
+ else if ( rFamily == u"FrameStyles" )
+ return ".uno:FrameStyle";
+ else if ( rFamily == u"NumberingStyles" )
+ return ".uno:ListStyle";
+ else if ( rFamily == u"TableStyles" )
+ return ".uno:TableStyle";
+
+ return OUString();
+}
+
+OUString GetDisplayFromInternalName( const css::uno::Reference< css::frame::XFrame >& rFrame,
+ const OUString& rStyleName,
+ const OUString& rFamilyName )
+{
+ try
+ {
+ css::uno::Reference< css::frame::XController > xController(
+ rFrame->getController(), css::uno::UNO_SET_THROW );
+ css::uno::Reference< css::style::XStyleFamiliesSupplier > xStylesSupplier(
+ xController->getModel(), css::uno::UNO_QUERY_THROW );
+ css::uno::Reference< css::container::XNameAccess > xFamilies(
+ xStylesSupplier->getStyleFamilies(), css::uno::UNO_SET_THROW );
+
+ css::uno::Reference< css::container::XNameAccess > xStyleSet;
+ xFamilies->getByName( rFamilyName ) >>= xStyleSet;
+ css::uno::Reference< css::beans::XPropertySet > xStyle;
+ xStyleSet->getByName( rStyleName ) >>= xStyle;
+
+ OUString aDisplayName;
+ if ( xStyle.is() )
+ xStyle->getPropertyValue( "DisplayName" ) >>= aDisplayName;
+ return aDisplayName;
+ }
+ catch ( const css::uno::Exception& )
+ {
+ // We couldn't get the display name. As a last resort we'll
+ // try to use the internal name, as was specified in the URL.
+ }
+
+ return rStyleName;
+}
+
+}
+
+namespace framework {
+
+StyleDispatcher::StyleDispatcher( const css::uno::Reference< css::frame::XFrame >& rFrame,
+ css::uno::Reference< css::util::XURLTransformer > xUrlTransformer,
+ const css::util::URL& rURL )
+ : m_aCommand( rURL.Complete )
+ , m_xUrlTransformer(std::move( xUrlTransformer ))
+ , m_xFrame( rFrame, css::uno::UNO_QUERY )
+{
+ SAL_WARN_IF( !m_aCommand.startsWith( ".uno:StyleApply?" ), "fwk.uielement", "Wrong dispatcher!" );
+
+ OUString aParams = rURL.Arguments;
+ OUString aStyleName, aFamilyName;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ std::u16string_view aParam = o3tl::getToken(aParams, 0, '&', nIndex );
+
+ sal_Int32 nParamIndex = 0;
+ std::u16string_view aParamName = o3tl::getToken(aParam, 0, '=', nParamIndex );
+ if ( nParamIndex < 0 )
+ break;
+
+ if ( aParamName == u"Style:string" )
+ {
+ std::u16string_view aValue = o3tl::getToken(aParam, 0, '=', nParamIndex );
+ aStyleName = INetURLObject::decode( aValue, INetURLObject::DecodeMechanism::WithCharset );
+ }
+ else if ( aParamName == u"FamilyName:string" )
+ {
+ aFamilyName = o3tl::getToken(aParam, 0, '=', nParamIndex );
+ }
+
+ } while ( nIndex >= 0 );
+
+ m_aStatusCommand = MapFamilyToCommand( aFamilyName );
+ if ( m_aStatusCommand.isEmpty() || aStyleName.isEmpty() )
+ {
+ // We can't provide status updates for this command, but just executing
+ // the command should still work (given that the command is valid).
+ SAL_WARN( "fwk.uielement", "Unable to parse as a style command: " << m_aCommand );
+ return;
+ }
+
+ m_aStyleName = GetDisplayFromInternalName( rFrame, aStyleName, aFamilyName );
+ if ( m_xFrame.is() )
+ {
+ css::util::URL aStatusURL;
+ aStatusURL.Complete = m_aStatusCommand;
+ m_xUrlTransformer->parseStrict( aStatusURL );
+ m_xStatusDispatch = m_xFrame->queryDispatch( aStatusURL, OUString(), 0 );
+ }
+}
+
+void StyleDispatcher::dispatch( const css::util::URL& rURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& rArguments )
+{
+ if ( !m_xFrame.is() )
+ return;
+
+ css::uno::Reference< css::frame::XDispatch > xDispatch( m_xFrame->queryDispatch( rURL, OUString(), 0 ) );
+ if ( xDispatch.is() )
+ xDispatch->dispatch( rURL, rArguments );
+}
+
+void StyleDispatcher::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& rListener,
+ const css::util::URL& /*rURL*/ )
+{
+ if ( m_xStatusDispatch.is() )
+ {
+ if ( !m_xOwner.is() )
+ m_xOwner.set( rListener );
+
+ css::util::URL aStatusURL;
+ aStatusURL.Complete = m_aStatusCommand;
+ m_xUrlTransformer->parseStrict( aStatusURL );
+ m_xStatusDispatch->addStatusListener( this, aStatusURL );
+ }
+}
+
+void StyleDispatcher::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*rListener*/,
+ const css::util::URL& /*rURL*/ )
+{
+ if ( m_xStatusDispatch.is() )
+ {
+ css::util::URL aStatusURL;
+ aStatusURL.Complete = m_aStatusCommand;
+ m_xUrlTransformer->parseStrict( aStatusURL );
+ m_xStatusDispatch->removeStatusListener( this, aStatusURL );
+ }
+}
+
+void StyleDispatcher::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ css::frame::status::Template aTemplate;
+ rEvent.State >>= aTemplate;
+
+ css::frame::FeatureStateEvent aEvent;
+ aEvent.FeatureURL.Complete = m_aCommand;
+ m_xUrlTransformer->parseStrict( aEvent.FeatureURL );
+
+ aEvent.IsEnabled = rEvent.IsEnabled;
+ aEvent.Requery = rEvent.Requery;
+ aEvent.State <<= m_aStyleName == aTemplate.StyleName;
+ m_xOwner->statusChanged( aEvent );
+}
+
+void StyleDispatcher::disposing( const css::lang::EventObject& /*rSource*/ )
+{
+ m_xStatusDispatch.clear();
+}
+
+StyleToolbarController::StyleToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ const css::uno::Reference< css::frame::XFrame >& rFrame,
+ const OUString& rCommand )
+ : ToolboxController( rContext, rFrame, rCommand )
+{
+}
+
+void StyleToolbarController::update()
+{
+ if ( m_bDisposed )
+ throw css::lang::DisposedException();
+
+ css::util::URL aURL;
+ aURL.Complete = m_aCommandURL;
+ m_xUrlTransformer->parseStrict( aURL );
+
+ auto& xDispatcher = m_aListenerMap[m_aCommandURL];
+ if ( xDispatcher.is() )
+ xDispatcher->removeStatusListener( this, aURL );
+
+ xDispatcher.set( new StyleDispatcher( m_xFrame, m_xUrlTransformer, aURL ) );
+ xDispatcher->addStatusListener( this, aURL );
+}
+
+void StyleToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ SolarMutexGuard aGuard;
+
+ if ( m_bDisposed )
+ throw css::lang::DisposedException();
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nItemId;
+ if ( getToolboxId( nItemId, &pToolBox ) )
+ {
+ bool bChecked = false;
+ rEvent.State >>= bChecked;
+ pToolBox->CheckItem( nItemId, bChecked );
+ pToolBox->EnableItem( nItemId, rEvent.IsEnabled );
+ }
+}
+
+void StyleToolbarController::dispose()
+{
+ ToolboxController::dispose();
+ m_aListenerMap.clear(); // Break the cycle with StyleDispatcher.
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/framework/source/uielement/subtoolbarcontroller.cxx b/framework/source/uielement/subtoolbarcontroller.cxx
new file mode 100644
index 000000000..de345f545
--- /dev/null
+++ b/framework/source/uielement/subtoolbarcontroller.cxx
@@ -0,0 +1,547 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/propertysequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <svtools/popupwindowcontroller.hxx>
+#include <svtools/toolbarmenu.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/gen.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/weldutils.hxx>
+
+#include <com/sun/star/awt/XDockableWindow.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/XSubToolbarController.hpp>
+#include <com/sun/star/frame/status/Visibility.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/ui/theUIElementFactoryManager.hpp>
+#include <com/sun/star/container/NoSuchElementException.hpp>
+
+typedef cppu::ImplInheritanceHelper< svt::PopupWindowController,
+ css::frame::XSubToolbarController,
+ css::awt::XDockableWindowListener> ToolBarBase;
+
+namespace {
+
+class SubToolBarController : public ToolBarBase
+{
+ OUString m_aSubTbName;
+ OUString m_aLastCommand;
+ css::uno::Reference< css::ui::XUIElement > m_xUIElement;
+ void disposeUIElement();
+public:
+ explicit SubToolBarController( const rtl::Reference< com::sun::star::uno::XComponentContext >& rxContext,
+ const css::uno::Sequence< css::uno::Any >& rxArgs );
+ virtual ~SubToolBarController() override;
+
+ void PopoverDestroyed();
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& rxArgs ) override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override;
+
+ // XToolbarController
+ virtual void SAL_CALL execute( sal_Int16 nKeyModifier ) override;
+
+ // PopupWindowController
+ virtual VclPtr<vcl::Window> createVclPopupWindow(vcl::Window* pParent) override;
+ virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
+
+ // XSubToolbarController
+ virtual sal_Bool SAL_CALL opensSubToolbar() override;
+ virtual OUString SAL_CALL getSubToolbarName() override;
+ virtual void SAL_CALL functionSelected( const OUString& rCommand ) override;
+ virtual void SAL_CALL updateImage() override;
+
+ // XDockableWindowListener
+ virtual void SAL_CALL startDocking( const css::awt::DockingEvent& e ) override;
+ virtual css::awt::DockingData SAL_CALL docking( const css::awt::DockingEvent& e ) override;
+ virtual void SAL_CALL endDocking( const css::awt::EndDockingEvent& e ) override;
+ virtual sal_Bool SAL_CALL prepareToggleFloatingMode( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL toggleFloatingMode( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL closed( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL endPopupMode( const css::awt::EndPopupModeEvent& e ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& e ) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+};
+
+}
+
+SubToolBarController::SubToolBarController(
+ const rtl::Reference< com::sun::star::uno::XComponentContext >& rxContext,
+ const css::uno::Sequence< css::uno::Any >& rxArgs
+) : ToolBarBase(
+ rxContext,
+ rtl::Reference< css::frame::XFrame >(),
+ ""
+ )
+{
+ for ( css::uno::Any const & arg : rxArgs )
+ {
+ css::beans::PropertyValue aPropValue;
+ arg >>= aPropValue;
+ if ( aPropValue.Name == "Value" )
+ {
+ sal_Int32 nIdx{ 0 };
+ OUString aValue;
+ aPropValue.Value >>= aValue;
+ m_aSubTbName = aValue.getToken(0, ';', nIdx);
+ m_aCommandURL = m_aSubTbName;
+ m_aLastCommand = aValue.getToken(0, ';', nIdx);
+ break;
+ }
+ }
+ if ( !m_aLastCommand.isEmpty() )
+ addStatusListener( m_aLastCommand );
+}
+
+SubToolBarController::~SubToolBarController()
+{
+ disposeUIElement();
+ m_xUIElement = nullptr;
+}
+
+void SubToolBarController::disposeUIElement()
+{
+ if ( m_xUIElement.is() )
+ {
+ css::uno::Reference< css::lang::XComponent > xComponent( m_xUIElement, css::uno::UNO_QUERY );
+ xComponent->dispose();
+ }
+}
+
+void SubToolBarController::statusChanged( const css::frame::FeatureStateEvent& Event )
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ if ( m_bDisposed )
+ return;
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( !getToolboxId( nId, &pToolBox ) )
+ return;
+
+ ToolBoxItemBits nItemBits = pToolBox->GetItemBits( nId );
+ nItemBits &= ~ToolBoxItemBits::CHECKABLE;
+ TriState eTri = TRISTATE_FALSE;
+
+ if ( Event.FeatureURL.Complete == m_aCommandURL )
+ {
+ pToolBox->EnableItem( nId, Event.IsEnabled );
+
+ OUString aStrValue;
+ css::frame::status::Visibility aItemVisibility;
+ if ( Event.State >>= aStrValue )
+ {
+ // Enum command, such as the current custom shape,
+ // toggle checked state.
+ if ( m_aLastCommand == OUStringConcatenation( m_aCommandURL + "." + aStrValue ) )
+ {
+ eTri = TRISTATE_TRUE;
+ nItemBits |= ToolBoxItemBits::CHECKABLE;
+ }
+ }
+ else if ( Event.State >>= aItemVisibility )
+ {
+ pToolBox->ShowItem( nId, aItemVisibility.bVisible );
+ }
+ }
+ else
+ {
+ bool bValue;
+ if ( Event.State >>= bValue )
+ {
+ // Boolean, treat it as checked/unchecked
+ if ( bValue )
+ eTri = TRISTATE_TRUE;
+ nItemBits |= ToolBoxItemBits::CHECKABLE;
+ }
+ }
+
+ pToolBox->SetItemState( nId, eTri );
+ pToolBox->SetItemBits( nId, nItemBits );
+}
+
+void SubToolBarController::execute( sal_Int16 nKeyModifier )
+{
+ if ( !m_aLastCommand.isEmpty() )
+ {
+ auto aArgs( comphelper::InitPropertySequence( {
+ { "KeyModifier", css::uno::Any( nKeyModifier ) }
+ } ) );
+ dispatchCommand( m_aLastCommand, aArgs );
+ }
+}
+
+namespace {
+class SubToolbarControl final : public WeldToolbarPopup
+{
+public:
+ explicit SubToolbarControl(SubToolBarController& rController, weld::Widget* pParent);
+ virtual ~SubToolbarControl() override;
+
+ virtual void GrabFocus() override;
+
+ weld::Container* GetContainer() { return m_xTargetContainer.get(); }
+
+private:
+ SubToolBarController& m_rController;
+ std::unique_ptr<weld::Container> m_xTargetContainer;
+};
+}
+
+SubToolbarControl::SubToolbarControl(SubToolBarController& rController,
+ weld::Widget* pParent)
+ : WeldToolbarPopup(rController.getFrameInterface(), pParent, "svt/ui/subtoolbar.ui", "subtoolbar")
+ , m_rController(rController)
+ , m_xTargetContainer(m_xBuilder->weld_container("container"))
+{
+}
+
+void SubToolbarControl::GrabFocus()
+{
+ // TODO
+}
+
+SubToolbarControl::~SubToolbarControl()
+{
+ m_rController.PopoverDestroyed();
+}
+
+std::unique_ptr<WeldToolbarPopup> SubToolBarController::weldPopupWindow()
+{
+ SolarMutexGuard aGuard;
+
+ auto pPopup = std::make_unique<SubToolbarControl>(*this, m_pToolbar);
+
+ css::uno::Reference< css::frame::XFrame > xFrame ( getFrameInterface() );
+
+ // create element with factory
+ static css::uno::WeakReference< css::ui::XUIElementFactoryManager > xWeakUIElementFactory;
+ css::uno::Reference< css::ui::XUIElementFactoryManager > xUIElementFactory = xWeakUIElementFactory;
+ if ( !xUIElementFactory.is() )
+ {
+ xUIElementFactory = css::ui::theUIElementFactoryManager::get( m_xContext );
+ xWeakUIElementFactory = xUIElementFactory;
+ }
+
+ css::uno::Reference< css::awt::XWindow > xParent = new weld::TransportAsXWindow(pPopup->GetContainer());
+
+ auto aPropSeq( comphelper::InitPropertySequence( {
+ { "Frame", css::uno::Any( xFrame ) },
+ { "ParentWindow", css::uno::Any( xParent ) },
+ { "Persistent", css::uno::Any( false ) },
+ { "PopupMode", css::uno::Any( true ) }
+ } ) );
+
+ try
+ {
+ m_xUIElement = xUIElementFactory->createUIElement( "private:resource/toolbar/" + m_aSubTbName, aPropSeq );
+ }
+ catch ( css::container::NoSuchElementException& )
+ {}
+ catch ( css::lang::IllegalArgumentException& )
+ {}
+
+ return pPopup;
+}
+
+VclPtr<vcl::Window> SubToolBarController::createVclPopupWindow(vcl::Window* /*pParent*/)
+{
+ SolarMutexGuard aGuard;
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( getToolboxId( nId, &pToolBox ) )
+ {
+ css::uno::Reference< css::frame::XFrame > xFrame ( getFrameInterface() );
+
+ // create element with factory
+ static css::uno::WeakReference< css::ui::XUIElementFactoryManager > xWeakUIElementFactory;
+ css::uno::Reference< css::ui::XUIElement > xUIElement;
+ css::uno::Reference< css::ui::XUIElementFactoryManager > xUIElementFactory = xWeakUIElementFactory;
+ if ( !xUIElementFactory.is() )
+ {
+ xUIElementFactory = css::ui::theUIElementFactoryManager::get( m_xContext );
+ xWeakUIElementFactory = xUIElementFactory;
+ }
+
+ auto aPropSeq( comphelper::InitPropertySequence( {
+ { "Frame", css::uno::Any( xFrame ) },
+ { "ParentWindow", css::uno::Any( m_xParentWindow ) },
+ { "Persistent", css::uno::Any( false ) },
+ { "PopupMode", css::uno::Any( true ) }
+ } ) );
+
+ try
+ {
+ xUIElement = xUIElementFactory->createUIElement( "private:resource/toolbar/" + m_aSubTbName, aPropSeq );
+ }
+ catch ( css::container::NoSuchElementException& )
+ {}
+ catch ( css::lang::IllegalArgumentException& )
+ {}
+
+ if ( xUIElement.is() )
+ {
+ css::uno::Reference< css::awt::XWindow > xSubToolBar( xUIElement->getRealInterface(), css::uno::UNO_QUERY );
+ if ( xSubToolBar.is() )
+ {
+ css::uno::Reference< css::awt::XDockableWindow > xDockWindow( xSubToolBar, css::uno::UNO_QUERY );
+ xDockWindow->addDockableWindowListener( css::uno::Reference< css::awt::XDockableWindowListener >(this) );
+ xDockWindow->enableDocking( true );
+
+ // keep reference to UIElement to avoid its destruction
+ disposeUIElement();
+ m_xUIElement = xUIElement;
+
+ VclPtr<vcl::Window> pTbxWindow = VCLUnoHelper::GetWindow( xSubToolBar );
+ if ( pTbxWindow && pTbxWindow->GetType() == WindowType::TOOLBOX )
+ {
+ ToolBox* pToolBar = static_cast< ToolBox* >( pTbxWindow.get() );
+ // calc and set size for popup mode
+ Size aSize = pToolBar->CalcPopupWindowSizePixel();
+ pToolBar->SetSizePixel( aSize );
+ // open subtoolbox in popup mode
+ vcl::Window::GetDockingManager()->StartPopupMode( pToolBox, pToolBar );
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+sal_Bool SubToolBarController::opensSubToolbar()
+{
+ return !m_aLastCommand.isEmpty();
+}
+
+OUString SubToolBarController::getSubToolbarName()
+{
+ return m_aSubTbName;
+}
+
+void SubToolBarController::functionSelected( const OUString& rCommand )
+{
+ if ( !m_aLastCommand.isEmpty() && m_aLastCommand != rCommand )
+ {
+ removeStatusListener( m_aLastCommand );
+ m_aLastCommand = rCommand;
+ addStatusListener( m_aLastCommand );
+ updateImage();
+ }
+}
+
+void SubToolBarController::updateImage()
+{
+ SolarMutexGuard aGuard;
+ if ( !m_aLastCommand.isEmpty() )
+ {
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( getToolboxId( nId, &pToolBox ) )
+ {
+ vcl::ImageType eImageType = pToolBox->GetImageSize();
+ Image aImage = vcl::CommandInfoProvider::GetImageForCommand(m_aLastCommand, getFrameInterface(), eImageType);
+ if ( !!aImage )
+ pToolBox->SetItemImage( nId, aImage );
+ }
+ }
+}
+
+void SubToolBarController::startDocking( const css::awt::DockingEvent& )
+{
+}
+
+css::awt::DockingData SubToolBarController::docking( const css::awt::DockingEvent& )
+{
+ return css::awt::DockingData();
+}
+
+void SubToolBarController::endDocking( const css::awt::EndDockingEvent& )
+{
+}
+
+sal_Bool SubToolBarController::prepareToggleFloatingMode( const css::lang::EventObject& )
+{
+ return false;
+}
+
+void SubToolBarController::toggleFloatingMode( const css::lang::EventObject& )
+{
+}
+
+void SubToolBarController::closed( const css::lang::EventObject& )
+{
+}
+
+void SubToolBarController::endPopupMode( const css::awt::EndPopupModeEvent& e )
+{
+ SolarMutexGuard aGuard;
+
+ OUString aSubToolBarResName;
+ if ( m_xUIElement.is() )
+ {
+ css::uno::Reference< css::beans::XPropertySet > xPropSet( m_xUIElement, css::uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ xPropSet->getPropertyValue("ResourceURL") >>= aSubToolBarResName;
+ }
+ catch ( css::beans::UnknownPropertyException& )
+ {}
+ catch ( css::lang::WrappedTargetException& )
+ {}
+ }
+ disposeUIElement();
+ }
+ m_xUIElement = nullptr;
+
+ // if the toolbar was teared-off recreate it and place it at the given position
+ if( !e.bTearoff )
+ return;
+
+ css::uno::Reference< css::ui::XUIElement > xUIElement;
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager = getLayoutManager();
+
+ if ( !xLayoutManager.is() )
+ return;
+
+ xLayoutManager->createElement( aSubToolBarResName );
+ xUIElement = xLayoutManager->getElement( aSubToolBarResName );
+ if ( !xUIElement.is() )
+ return;
+
+ css::uno::Reference< css::awt::XWindow > xSubToolBar( xUIElement->getRealInterface(), css::uno::UNO_QUERY );
+ css::uno::Reference< css::beans::XPropertySet > xProp( xUIElement, css::uno::UNO_QUERY );
+ if ( !(xSubToolBar.is() && xProp.is()) )
+ return;
+
+ try
+ {
+ VclPtr<vcl::Window> pTbxWindow = VCLUnoHelper::GetWindow( xSubToolBar );
+ if ( pTbxWindow && pTbxWindow->GetType() == WindowType::TOOLBOX )
+ {
+ OUString aPersistentString( "Persistent" );
+ css::uno::Any a = xProp->getPropertyValue( aPersistentString );
+ xProp->setPropertyValue( aPersistentString, css::uno::Any( false ) );
+
+ xLayoutManager->hideElement( aSubToolBarResName );
+ xLayoutManager->floatWindow( aSubToolBarResName );
+
+ xLayoutManager->setElementPos( aSubToolBarResName, e.FloatingPosition );
+ xLayoutManager->showElement( aSubToolBarResName );
+
+ xProp->setPropertyValue("Persistent", a );
+ }
+ }
+ catch ( css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( css::uno::Exception& )
+ {}
+}
+
+void SubToolBarController::disposing( const css::lang::EventObject& e )
+{
+ svt::ToolboxController::disposing( e );
+}
+
+void SubToolBarController::initialize( const css::uno::Sequence< css::uno::Any >& rxArgs )
+{
+ svt::PopupWindowController::initialize( rxArgs );
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( getToolboxId( nId, &pToolBox ) )
+ {
+ if ( m_aLastCommand.isEmpty() )
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY );
+ else
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWN );
+ }
+
+ if (m_pToolbar)
+ {
+ mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar));
+ m_pToolbar->set_item_popover(m_aCommandURL.toUtf8(), mxPopoverContainer->getTopLevel());
+ }
+
+ updateImage();
+}
+
+void SubToolBarController::PopoverDestroyed()
+{
+ disposeUIElement();
+ m_xUIElement = nullptr;
+}
+
+void SubToolBarController::dispose()
+{
+ if ( m_bDisposed )
+ return;
+
+ svt::PopupWindowController::dispose();
+ disposeUIElement();
+ m_xUIElement = nullptr;
+}
+
+OUString SubToolBarController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.SubToolBarController";
+}
+
+sal_Bool SubToolBarController::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService( this, rServiceName );
+}
+
+css::uno::Sequence< OUString > SubToolBarController::getSupportedServiceNames()
+{
+ return {"com.sun.star.frame.ToolbarController"};
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_SubToolBarController_get_implementation(
+ css::uno::XComponentContext* rxContext,
+ css::uno::Sequence<css::uno::Any> const & rxArgs )
+{
+ return cppu::acquire( new SubToolBarController( rxContext, rxArgs ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/thesaurusmenucontroller.cxx b/framework/source/uielement/thesaurusmenucontroller.cxx
new file mode 100644
index 000000000..fb7f3175a
--- /dev/null
+++ b/framework/source/uielement/thesaurusmenucontroller.cxx
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <sal/log.hxx>
+#include <svl/lngmisc.hxx>
+#include <svtools/popupmenucontrollerbase.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/lingucfg.hxx>
+#include <vcl/commandinfoprovider.hxx>
+
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+#include <com/sun/star/linguistic2/LinguServiceManager.hpp>
+
+namespace {
+
+class ThesaurusMenuController : public svt::PopupMenuControllerBase
+{
+public:
+ explicit ThesaurusMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+private:
+ void fillPopupMenu();
+ void getMeanings( std::vector< OUString >& rSynonyms, const OUString& rWord, const css::lang::Locale& rLocale, size_t nMaxSynonms );
+ OUString getThesImplName( const css::lang::Locale& rLocale ) const;
+ css::uno::Reference< css::linguistic2::XLinguServiceManager2 > m_xLinguServiceManager;
+ css::uno::Reference< css::linguistic2::XThesaurus > m_xThesaurus;
+ OUString m_aLastWord;
+};
+
+}
+
+ThesaurusMenuController::ThesaurusMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) :
+ svt::PopupMenuControllerBase( rxContext ),
+ m_xLinguServiceManager( css::linguistic2::LinguServiceManager::create( rxContext ) ),
+ m_xThesaurus( m_xLinguServiceManager->getThesaurus() )
+{
+}
+
+void ThesaurusMenuController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ rEvent.State >>= m_aLastWord;
+ m_xPopupMenu->clear();
+ if ( rEvent.IsEnabled )
+ fillPopupMenu();
+}
+
+void ThesaurusMenuController::fillPopupMenu()
+{
+ sal_Int32 nIdx{ 0 };
+ OUString aText = m_aLastWord.getToken(0, '#', nIdx);
+ OUString aIsoLang = m_aLastWord.getToken(0, '#', nIdx);
+ if ( aText.isEmpty() || aIsoLang.isEmpty() )
+ return;
+
+ std::vector< OUString > aSynonyms;
+ css::lang::Locale aLocale = LanguageTag::convertToLocale( aIsoLang );
+ getMeanings( aSynonyms, aText, aLocale, 7 /*max number of synonyms to retrieve*/ );
+
+ m_xPopupMenu->enableAutoMnemonics(false);
+ if ( aSynonyms.empty() )
+ return;
+
+ SvtLinguConfig aCfg;
+ css::uno::Reference<css::graphic::XGraphic> xGraphic;
+ OUString aThesImplName( getThesImplName( aLocale ) );
+ OUString aSynonymsImageUrl( aCfg.GetSynonymsContextImage( aThesImplName ) );
+ if (!aThesImplName.isEmpty() && !aSynonymsImageUrl.isEmpty())
+ {
+ try
+ {
+ css::uno::Reference<css::uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
+ css::uno::Reference<css::graphic::XGraphicProvider> xProvider(css::graphic::GraphicProvider::create(xContext));
+ xGraphic = xProvider->queryGraphic({ comphelper::makePropertyValue("URL", aSynonymsImageUrl) });
+ }
+ catch (const css::uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ }
+
+ sal_uInt16 nId = 1;
+ for ( const auto& aSynonym : aSynonyms )
+ {
+ OUString aItemText( linguistic::GetThesaurusReplaceText( aSynonym ) );
+ m_xPopupMenu->insertItem(nId, aItemText, 0, -1);
+ m_xPopupMenu->setCommand(nId, ".uno:ThesaurusFromContext?WordReplace:string=" + aItemText);
+
+ if (xGraphic.is())
+ m_xPopupMenu->setItemImage(nId, xGraphic, false);
+
+ nId++;
+ }
+
+ m_xPopupMenu->insertSeparator(-1);
+ OUString aThesaurusDialogCmd( ".uno:ThesaurusDialog" );
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aThesaurusDialogCmd, m_aModuleName);
+ m_xPopupMenu->insertItem(nId, vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties), 0, -1);
+ m_xPopupMenu->setCommand(nId, aThesaurusDialogCmd);
+}
+
+void ThesaurusMenuController::getMeanings( std::vector< OUString >& rSynonyms, const OUString& rWord,
+ const css::lang::Locale& rLocale, size_t nMaxSynonms )
+{
+ rSynonyms.clear();
+ if ( !(m_xThesaurus.is() && m_xThesaurus->hasLocale( rLocale ) && !rWord.isEmpty() && nMaxSynonms > 0) )
+ return;
+
+ try
+ {
+ const css::uno::Sequence< css::uno::Reference< css::linguistic2::XMeaning > > aMeaningSeq(
+ m_xThesaurus->queryMeanings( rWord, rLocale, css::uno::Sequence< css::beans::PropertyValue >() ) );
+
+ for ( const auto& xMeaning : aMeaningSeq )
+ {
+ const css::uno::Sequence< OUString > aSynonymSeq( xMeaning->querySynonyms() );
+ for ( const auto& aSynonym : aSynonymSeq )
+ {
+ rSynonyms.push_back( aSynonym );
+ if ( rSynonyms.size() == nMaxSynonms )
+ return;
+ }
+ }
+ }
+ catch ( const css::uno::Exception& )
+ {
+ SAL_WARN( "fwk.uielement", "Failed to get synonyms" );
+ }
+}
+
+OUString ThesaurusMenuController::getThesImplName( const css::lang::Locale& rLocale ) const
+{
+ css::uno::Sequence< OUString > aServiceNames =
+ m_xLinguServiceManager->getConfiguredServices( "com.sun.star.linguistic2.Thesaurus", rLocale );
+ SAL_WARN_IF( aServiceNames.getLength() > 1, "fwk.uielement", "Only one thesaurus is allowed per locale, but found more!" );
+ if ( aServiceNames.getLength() == 1 )
+ return aServiceNames[0];
+
+ return OUString();
+}
+
+OUString ThesaurusMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.ThesaurusMenuController";
+}
+
+css::uno::Sequence< OUString > ThesaurusMenuController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.PopupMenuController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_ThesaurusMenuController_get_implementation(
+ css::uno::XComponentContext* xContext,
+ css::uno::Sequence< css::uno::Any > const & )
+{
+ return cppu::acquire( new ThesaurusMenuController( xContext ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/togglebuttontoolbarcontroller.cxx b/framework/source/uielement/togglebuttontoolbarcontroller.cxx
new file mode 100644
index 000000000..c17b08efe
--- /dev/null
+++ b/framework/source/uielement/togglebuttontoolbarcontroller.cxx
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/togglebuttontoolbarcontroller.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/menu.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::util;
+
+namespace framework
+{
+
+ToggleButtonToolbarController::ToggleButtonToolbarController(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ ToolBox* pToolbar,
+ ToolBoxItemId nID,
+ Style eStyle,
+ const OUString& aCommand ) :
+ ComplexToolbarController( rxContext, rFrame, pToolbar, nID, aCommand )
+{
+ if ( eStyle == Style::DropDownButton )
+ m_xToolbar->SetItemBits( m_nID, ToolBoxItemBits::DROPDOWNONLY | m_xToolbar->GetItemBits( m_nID ) );
+ else // Style::ToggleDropDownButton
+ m_xToolbar->SetItemBits( m_nID, ToolBoxItemBits::DROPDOWN | m_xToolbar->GetItemBits( m_nID ) );
+}
+
+ToggleButtonToolbarController::~ToggleButtonToolbarController()
+{
+}
+
+void SAL_CALL ToggleButtonToolbarController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+ ComplexToolbarController::dispose();
+}
+
+Sequence<PropertyValue> ToggleButtonToolbarController::getExecuteArgs(sal_Int16 KeyModifier) const
+{
+ Sequence<PropertyValue> aArgs{ // Add key modifier to argument list
+ comphelper::makePropertyValue("KeyModifier", KeyModifier),
+ comphelper::makePropertyValue("Text", m_aCurrentSelection) };
+ return aArgs;
+}
+
+uno::Reference< awt::XWindow > SAL_CALL ToggleButtonToolbarController::createPopupWindow()
+{
+ uno::Reference< awt::XWindow > xWindow;
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ // create popup menu
+ ScopedVclPtrInstance<::PopupMenu> aPopup;
+ const sal_uInt32 nCount = m_aDropdownMenuList.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ const OUString & rLabel = m_aDropdownMenuList[i].mLabel;
+ aPopup->InsertItem( sal_uInt16( i+1 ), rLabel );
+ if ( rLabel == m_aCurrentSelection )
+ aPopup->CheckItem( sal_uInt16( i+1 ) );
+ else
+ aPopup->CheckItem( sal_uInt16( i+1 ), false );
+
+ if ( !m_aDropdownMenuList[i].mTipHelpText.isEmpty() )
+ aPopup->SetTipHelpText( sal_uInt16( i+1 ), m_aDropdownMenuList[i].mTipHelpText );
+ }
+
+ m_xToolbar->SetItemDown( m_nID, true );
+ aPopup->SetSelectHdl( LINK( this, ToggleButtonToolbarController, MenuSelectHdl ));
+ aPopup->Execute( m_xToolbar, m_xToolbar->GetItemRect( m_nID ));
+ m_xToolbar->SetItemDown( m_nID, false );
+
+ return xWindow;
+}
+
+void ToggleButtonToolbarController::executeControlCommand( const css::frame::ControlCommand& rControlCommand )
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ if ( rControlCommand.Command == "SetList" )
+ {
+ for ( auto const & arg : rControlCommand.Arguments )
+ {
+ if ( arg.Name == "List" )
+ {
+ Sequence< OUString > aList;
+ m_aDropdownMenuList.clear();
+ m_aCurrentSelection.clear();
+
+ arg.Value >>= aList;
+ for ( OUString const & label : std::as_const(aList) )
+ {
+ m_aDropdownMenuList.push_back( DropdownMenuItem() );
+ m_aDropdownMenuList.back().mLabel = label;
+ }
+
+ // send notification
+ uno::Sequence< beans::NamedValue > aInfo { { "List", css::uno::Any(aList) } };
+ addNotifyInfo( "ListChanged",
+ getDispatchFromCommand( m_aCommandURL ),
+ aInfo );
+
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "CheckItemPos" )
+ {
+ for ( auto const & arg : rControlCommand.Arguments )
+ {
+ if ( arg.Name == "Pos" )
+ {
+ sal_Int32 nPos( -1 );
+
+ arg.Value >>= nPos;
+ if ( nPos >= 0 &&
+ ( sal::static_int_cast< sal_uInt32 >(nPos)
+ < m_aDropdownMenuList.size() ) )
+ {
+ m_aCurrentSelection = m_aDropdownMenuList[nPos].mLabel;
+
+ // send notification
+ uno::Sequence< beans::NamedValue > aInfo { { "ItemChecked", css::uno::Any(nPos) } };
+ addNotifyInfo( "Pos",
+ getDispatchFromCommand( m_aCommandURL ),
+ aInfo );
+ }
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "AddEntry" )
+ {
+ OUString aText;
+ OUString aTipHelpText;
+
+ for ( auto const & arg : rControlCommand.Arguments )
+ {
+ if ( arg.Name == "Text" )
+ {
+ arg.Value >>= aText;
+ }
+ else if ( arg.Name == "TipHelpText" )
+ {
+ arg.Value >>= aTipHelpText;
+ }
+ }
+
+ if (!aText.isEmpty())
+ {
+ m_aDropdownMenuList.push_back( DropdownMenuItem() );
+ m_aDropdownMenuList.back().mLabel = aText;
+ m_aDropdownMenuList.back().mTipHelpText = aTipHelpText;
+ }
+ }
+ else if ( rControlCommand.Command == "InsertEntry" )
+ {
+ sal_Int32 nPos(0);
+ sal_Int32 nSize = sal_Int32( m_aDropdownMenuList.size() );
+ OUString aText;
+ for ( auto const & arg : rControlCommand.Arguments )
+ {
+ if ( arg.Name == "Pos" )
+ {
+ sal_Int32 nTmpPos = 0;
+ if ( arg.Value >>= nTmpPos )
+ {
+ if (( nTmpPos >= 0 ) && ( nTmpPos < nSize ))
+ nPos = nTmpPos;
+ }
+ }
+ else if ( arg.Name == "Text" )
+ arg.Value >>= aText;
+ }
+
+ std::vector< DropdownMenuItem >::iterator aIter = m_aDropdownMenuList.begin();
+ aIter += nPos;
+ aIter = m_aDropdownMenuList.insert(aIter, DropdownMenuItem());
+ if (aIter != m_aDropdownMenuList.end())
+ aIter->mLabel = aText;
+ }
+ else if ( rControlCommand.Command == "RemoveEntryPos" )
+ {
+ for ( auto const & arg : rControlCommand.Arguments )
+ {
+ if ( arg.Name == "Pos" )
+ {
+ sal_Int32 nPos( -1 );
+ if ( arg.Value >>= nPos )
+ {
+ if ( nPos < sal_Int32( m_aDropdownMenuList.size() ))
+ {
+ m_aDropdownMenuList.erase(m_aDropdownMenuList.begin() + nPos);
+ }
+ }
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "RemoveEntryText" )
+ {
+ for ( auto const & arg : rControlCommand.Arguments )
+ {
+ if ( arg.Name == "Text" )
+ {
+ OUString aText;
+ if ( arg.Value >>= aText )
+ {
+ sal_Int32 nSize = sal_Int32( m_aDropdownMenuList.size() );
+ for ( sal_Int32 j = 0; j < nSize; j++ )
+ {
+ if ( m_aDropdownMenuList[j].mLabel == aText )
+ {
+ m_aDropdownMenuList.erase(m_aDropdownMenuList.begin() + j);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ else if ( rControlCommand.Command == "createPopupMenu" )
+ {
+ createPopupWindow();
+ }
+}
+
+IMPL_LINK( ToggleButtonToolbarController, MenuSelectHdl, Menu *, pMenu, bool )
+{
+ SolarMutexGuard aGuard;
+
+ sal_uInt16 nItemId = pMenu->GetCurItemId();
+ if ( nItemId > 0 && nItemId <= m_aDropdownMenuList.size() )
+ {
+ m_aCurrentSelection = m_aDropdownMenuList[nItemId-1].mLabel;
+
+ execute( 0 );
+ }
+ return false;
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/toolbarmanager.cxx b/framework/source/uielement/toolbarmanager.cxx
new file mode 100644
index 000000000..3cce6eef4
--- /dev/null
+++ b/framework/source/uielement/toolbarmanager.cxx
@@ -0,0 +1,2365 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <cassert>
+
+#include <uielement/toolbarmanager.hxx>
+
+#include <framework/generictoolbarcontroller.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <uielement/styletoolbarcontroller.hxx>
+#include <properties.h>
+#include <framework/sfxhelperfunctions.hxx>
+#include <classes/fwkresid.hxx>
+#include <classes/resource.hxx>
+#include <strings.hrc>
+#include <uielement/toolbarmerger.hxx>
+
+#include <com/sun/star/ui/ItemType.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/awt/XDockableWindow.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/ui/DockingArea.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/theToolbarControllerFactory.hpp>
+#include <com/sun/star/ui/ItemStyle.hpp>
+#include <com/sun/star/ui/XUIElementSettings.hpp>
+#include <com/sun/star/ui/XUIConfigurationPersistence.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/ImageType.hpp>
+#include <com/sun/star/ui/UIElementType.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+
+#include <svtools/toolboxcontroller.hxx>
+#include <unotools/cmdoptions.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <svtools/miscopt.hxx>
+#include <svtools/imgdef.hxx>
+#include <utility>
+#include <vcl/event.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/syswin.hxx>
+#include <vcl/taskpanelist.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/weldutils.hxx>
+#include <tools/debug.hxx>
+
+// namespaces
+
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::graphic;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::ui;
+using namespace ::com::sun::star;
+
+namespace framework
+{
+
+const char ITEM_DESCRIPTOR_COMMANDURL[] = "CommandURL";
+const char ITEM_DESCRIPTOR_VISIBLE[] = "IsVisible";
+
+const sal_uInt16 STARTID_CUSTOMIZE_POPUPMENU = 1000;
+
+static css::uno::Reference< css::frame::XLayoutManager > getLayoutManagerFromFrame(
+ css::uno::Reference< css::frame::XFrame > const & rFrame )
+{
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+
+ Reference< XPropertySet > xPropSet( rFrame, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ return xLayoutManager;
+}
+namespace
+{
+
+sal_Int16 getCurrentImageType()
+{
+ SvtMiscOptions aMiscOptions;
+ sal_Int16 nImageType = css::ui::ImageType::SIZE_DEFAULT;
+ if (aMiscOptions.GetCurrentSymbolsSize() == SFX_SYMBOLS_SIZE_LARGE)
+ nImageType |= css::ui::ImageType::SIZE_LARGE;
+ else if (aMiscOptions.GetCurrentSymbolsSize() == SFX_SYMBOLS_SIZE_32)
+ nImageType |= css::ui::ImageType::SIZE_32;
+ return nImageType;
+}
+
+class VclToolBarManager : public ToolBarManagerImpl
+{
+ DECL_LINK(Click, ToolBox*, void);
+
+public:
+ VclToolBarManager(VclPtr<ToolBox> pToolbar)
+ : m_pToolBar(std::move(pToolbar))
+ , m_bAddedToTaskPaneList(true)
+ , m_pManager(nullptr)
+ {}
+
+ ~VclToolBarManager()
+ {
+ OSL_ASSERT( !m_bAddedToTaskPaneList );
+ }
+
+ virtual void Init() override
+ {
+ vcl::Window* pWindow = m_pToolBar;
+ while ( pWindow && !pWindow->IsSystemWindow() )
+ pWindow = pWindow->GetParent();
+
+ if ( pWindow )
+ static_cast<SystemWindow *>(pWindow)->GetTaskPaneList()->AddWindow( m_pToolBar );
+ }
+
+ virtual void Destroy() override
+ {
+ OSL_ASSERT( m_pToolBar != nullptr );
+ SolarMutexGuard g;
+ if ( m_bAddedToTaskPaneList )
+ {
+ vcl::Window* pWindow = m_pToolBar;
+ while ( pWindow && !pWindow->IsSystemWindow() )
+ pWindow = pWindow->GetParent();
+
+ if ( pWindow )
+ static_cast<SystemWindow *>(pWindow)->GetTaskPaneList()->RemoveWindow( m_pToolBar );
+ m_bAddedToTaskPaneList = false;
+ }
+
+ // Delete the additional add-ons data
+ for ( ToolBox::ImplToolItems::size_type i = 0; i < m_pToolBar->GetItemCount(); i++ )
+ {
+ ToolBoxItemId nItemId = m_pToolBar->GetItemId( i );
+ if ( nItemId > ToolBoxItemId(0) )
+ delete static_cast< AddonsParams* >( m_pToolBar->GetItemData( nItemId ));
+ }
+
+ // #i93173# note we can still be in one of the toolbar's handlers
+ m_pToolBar->SetSelectHdl( Link<ToolBox *, void>() );
+ m_pToolBar->SetActivateHdl( Link<ToolBox *, void>() );
+ m_pToolBar->SetDeactivateHdl( Link<ToolBox *, void>() );
+ m_pToolBar->SetClickHdl( Link<ToolBox *, void>() );
+ m_pToolBar->SetDropdownClickHdl( Link<ToolBox *, void>() );
+ m_pToolBar->SetDoubleClickHdl( Link<ToolBox *, void>() );
+ m_pToolBar->SetStateChangedHdl( Link<StateChangedType const *, void>() );
+ m_pToolBar->SetDataChangedHdl( Link<DataChangedEvent const *, void>() );
+
+ m_pToolBar.disposeAndClear();
+ }
+
+ virtual css::uno::Reference<css::awt::XWindow> GetInterface() override
+ {
+ return VCLUnoHelper::GetInterface(m_pToolBar);
+ }
+
+ virtual css::uno::Reference<css::frame::XStatusListener> CreateToolBoxController(
+ const css::uno::Reference<css::frame::XFrame>& rFrame,
+ ToolBoxItemId nId,
+ const OUString& rCommandURL ) override
+ {
+ rtl::Reference<svt::ToolboxController> pController
+ = ::framework::CreateToolBoxController( rFrame, m_pToolBar, nId, rCommandURL );
+ css::uno::Reference<css::frame::XStatusListener> xListener;
+ if (pController)
+ xListener = pController;
+ return xListener;
+ }
+
+ virtual void ConnectCallbacks(ToolBarManager* pManager) override
+ {
+ m_pManager = pManager;
+ m_pToolBar->SetSelectHdl( LINK( pManager, ToolBarManager, Select) );
+ m_pToolBar->SetClickHdl( LINK( this, VclToolBarManager, Click ) );
+ m_pToolBar->SetDropdownClickHdl( LINK( pManager, ToolBarManager, DropdownClick ) );
+ m_pToolBar->SetDoubleClickHdl( LINK( pManager, ToolBarManager, DoubleClick ) );
+ m_pToolBar->SetStateChangedHdl( LINK( pManager, ToolBarManager, StateChanged ) );
+ m_pToolBar->SetDataChangedHdl( LINK( pManager, ToolBarManager, DataChanged ) );
+
+ m_pToolBar->SetMenuButtonHdl( LINK( pManager, ToolBarManager, MenuButton ) );
+ m_pToolBar->SetMenuExecuteHdl( LINK( pManager, ToolBarManager, MenuPreExecute ) );
+ m_pToolBar->GetMenu()->SetSelectHdl( LINK( pManager, ToolBarManager, MenuSelect ) );
+ }
+
+ virtual void InsertItem(ToolBoxItemId nId,
+ const OUString& rString,
+ const OUString& rCommandURL,
+ const OUString& rTooltip,
+ const OUString& rLabel,
+ ToolBoxItemBits nItemBits) override
+ {
+ m_pToolBar->InsertItem( nId, rString, nItemBits );
+ m_pToolBar->SetItemCommand( nId, rCommandURL );
+ m_pToolBar->SetQuickHelpText(nId, rTooltip);
+ m_pToolBar->SetItemText( nId, rLabel );
+ m_pToolBar->EnableItem( nId );
+ m_pToolBar->SetItemState( nId, TRISTATE_FALSE );
+ }
+
+ virtual void InsertSeparator() override
+ {
+ m_pToolBar->InsertSeparator();
+ }
+
+ virtual void InsertSpace() override
+ {
+ m_pToolBar->InsertSpace();
+ }
+
+ virtual void InsertBreak() override
+ {
+ m_pToolBar->InsertBreak();
+ }
+
+ virtual ToolBoxItemId GetItemId(sal_uInt16 nPos) override
+ {
+ return m_pToolBar->GetItemId(nPos);
+ }
+
+ virtual ToolBoxItemId GetCurItemId() override
+ {
+ return m_pToolBar->GetCurItemId();
+ }
+
+ virtual OUString GetItemCommand(ToolBoxItemId nId) override
+ {
+ return m_pToolBar->GetItemCommand(nId);
+ }
+
+ virtual sal_uInt16 GetItemCount() override
+ {
+ return m_pToolBar->GetItemCount();
+ }
+
+ virtual void SetItemCheckable(ToolBoxItemId nId) override
+ {
+ m_pToolBar->SetItemBits( nId, m_pToolBar->GetItemBits( nId ) | ToolBoxItemBits::CHECKABLE );
+ }
+
+ virtual void HideItem(ToolBoxItemId nId, const OUString& /*rCommandURL*/) override
+ {
+ m_pToolBar->HideItem( nId );
+ }
+
+ virtual bool IsItemVisible(ToolBoxItemId nId, const OUString& /*rCommandURL*/) override
+ {
+ return m_pToolBar->IsItemVisible(nId);
+ }
+
+ virtual void Clear() override
+ {
+ m_pToolBar->Clear();
+ }
+
+ virtual void SetName(const OUString& rName) override
+ {
+ m_pToolBar->SetText( rName );
+ }
+
+ virtual void SetHelpId(const OString& rHelpId) override
+ {
+ m_pToolBar->SetHelpId( rHelpId );
+ }
+
+ virtual bool WillUsePopupMode() override
+ {
+ return m_pToolBar->WillUsePopupMode();
+ }
+
+ virtual bool IsReallyVisible() override
+ {
+ return m_pToolBar->IsReallyVisible();
+ }
+
+ virtual void SetIconSize(ToolBoxButtonSize eSize) override
+ {
+ m_pToolBar->SetToolboxButtonSize(eSize);
+ }
+
+ virtual vcl::ImageType GetImageSize() override
+ {
+ return m_pToolBar->GetImageSize();
+ }
+
+ virtual void SetMenuType(ToolBoxMenuType eType) override
+ {
+ m_pToolBar->SetMenuType( eType );
+ }
+
+ virtual void MergeToolbar(ToolBoxItemId & rItemId,
+ const OUString& rModuleIdentifier,
+ CommandToInfoMap& rCommandMap,
+ MergeToolbarInstruction& rInstruction) override
+ {
+ ReferenceToolbarPathInfo aRefPoint = ToolBarMerger::FindReferencePoint( m_pToolBar, rInstruction.aMergePoint );
+
+ // convert the sequence< sequence< propertyvalue > > structure to
+ // something we can better handle. A vector with item data
+ AddonToolbarItemContainer aItems;
+ ToolBarMerger::ConvertSeqSeqToVector( rInstruction.aMergeToolbarItems, aItems );
+
+ if ( aRefPoint.bResult )
+ {
+ ToolBarMerger::ProcessMergeOperation( m_pToolBar,
+ aRefPoint.nPos,
+ rItemId,
+ rCommandMap,
+ rModuleIdentifier,
+ rInstruction.aMergeCommand,
+ rInstruction.aMergeCommandParameter,
+ aItems );
+ }
+ else
+ {
+ ToolBarMerger::ProcessMergeFallback( m_pToolBar,
+ rItemId,
+ rCommandMap,
+ rModuleIdentifier,
+ rInstruction.aMergeCommand,
+ rInstruction.aMergeFallback,
+ aItems );
+ }
+ }
+
+ virtual void SetItemImage(ToolBoxItemId nId,
+ const OUString& /*rCommandURL*/,
+ const Image& rImage) override
+ {
+ m_pToolBar->SetItemImage(nId, rImage);
+ }
+
+ virtual void UpdateSize() override
+ {
+ ::Size aSize = m_pToolBar->CalcWindowSizePixel();
+ m_pToolBar->SetOutputSizePixel( aSize );
+ }
+
+ virtual void SetItemWindow(ToolBoxItemId nItemId, vcl::Window* pNewWindow) override
+ {
+ m_pToolBar->SetItemWindow( nItemId, pNewWindow );
+ }
+
+private:
+ VclPtr<ToolBox> m_pToolBar;
+ bool m_bAddedToTaskPaneList;
+ ToolBarManager* m_pManager;
+};
+
+IMPL_LINK_NOARG(VclToolBarManager, Click, ToolBox*, void)
+{
+ m_pManager->OnClick();
+}
+
+class WeldToolBarManager : public ToolBarManagerImpl
+{
+ DECL_LINK(Click, const OString&, void);
+ DECL_LINK(ToggleMenuHdl, const OString&, void);
+
+public:
+ WeldToolBarManager(weld::Toolbar* pToolbar,
+ weld::Builder* pBuilder)
+ : m_pWeldedToolBar(pToolbar)
+ , m_pBuilder(pBuilder)
+ , m_pManager(nullptr)
+ , m_nCurrentId(0)
+ {}
+
+ virtual void Init() override {}
+
+ virtual void Destroy() override {}
+
+ virtual css::uno::Reference<css::awt::XWindow> GetInterface() override
+ {
+ return new weld::TransportAsXWindow(m_pWeldedToolBar);
+ }
+
+ virtual css::uno::Reference<css::frame::XStatusListener> CreateToolBoxController(
+ const css::uno::Reference<css::frame::XFrame>& rFrame,
+ ToolBoxItemId /*nId*/,
+ const OUString& rCommandURL ) override
+ {
+ css::uno::Reference<css::frame::XToolbarController> xController
+ = ::framework::CreateWeldToolBoxController(rFrame, m_pWeldedToolBar, m_pBuilder, rCommandURL);
+ css::uno::Reference<css::frame::XStatusListener> xListener;
+ if (xController.is())
+ xListener = css::uno::Reference<css::frame::XStatusListener>( xController, UNO_QUERY );
+ return xListener;
+ }
+
+ virtual void ConnectCallbacks(ToolBarManager* pManager) override
+ {
+ m_pManager = pManager;
+ m_pWeldedToolBar->connect_clicked(LINK(this, WeldToolBarManager, Click));
+ m_pWeldedToolBar->connect_menu_toggled(LINK(this, WeldToolBarManager, ToggleMenuHdl));
+ }
+
+ virtual void InsertItem(ToolBoxItemId nId,
+ const OUString& /*rString*/,
+ const OUString& rCommandURL,
+ const OUString& rTooltip,
+ const OUString& rLabel,
+ ToolBoxItemBits /*nItemBits*/) override
+ {
+ OString sId = OUStringToOString(rCommandURL, RTL_TEXTENCODING_UTF8);
+ m_aCommandToId[sId] = nId;
+ m_aIdToCommand[nId] = sId;
+ m_aCommandOrder.push_back(sId);
+
+ m_pWeldedToolBar->insert_item(m_aCommandOrder.size(), rCommandURL);
+ m_pWeldedToolBar->set_item_tooltip_text(sId, rTooltip);
+ m_pWeldedToolBar->set_item_label( sId, rLabel );
+ m_pWeldedToolBar->set_item_sensitive( sId, true );
+ m_pWeldedToolBar->set_item_active( sId, false );
+ }
+
+ virtual void InsertSeparator() override
+ {
+ m_pWeldedToolBar->append_separator("");
+ }
+
+ virtual void InsertSpace() override {}
+
+ virtual void InsertBreak() override {}
+
+ virtual ToolBoxItemId GetItemId(sal_uInt16 nPos) override
+ {
+ return m_aCommandToId[m_aCommandOrder[nPos]];
+ }
+
+ virtual ToolBoxItemId GetCurItemId() override
+ {
+ return m_nCurrentId;
+ }
+
+ virtual OUString GetItemCommand(ToolBoxItemId nId) override
+ {
+ return OStringToOUString(m_aIdToCommand[nId], RTL_TEXTENCODING_UTF8);
+ }
+
+ virtual sal_uInt16 GetItemCount() override
+ {
+ return m_aCommandOrder.size();
+ }
+
+ virtual void SetItemCheckable(ToolBoxItemId /*nId*/) override {}
+
+ virtual void HideItem(ToolBoxItemId /*nId*/, const OUString& rCommandURL) override
+ {
+ OString sId = OUStringToOString(rCommandURL, RTL_TEXTENCODING_UTF8);
+ m_pWeldedToolBar->set_item_visible(sId, false);
+ }
+
+ virtual bool IsItemVisible(ToolBoxItemId /*nId*/, const OUString& rCommandURL) override
+ {
+ OString sId = OUStringToOString(rCommandURL, RTL_TEXTENCODING_UTF8);
+ return m_pWeldedToolBar->get_item_visible(sId);
+ }
+
+ virtual void Clear() override {}
+
+ virtual void SetName(const OUString& /*rName*/) override {}
+
+ virtual void SetHelpId(const OString& /*rHelpId*/) override {}
+
+ virtual bool WillUsePopupMode() override { return true; }
+
+ virtual bool IsReallyVisible() override { return true; }
+
+ virtual void SetIconSize(ToolBoxButtonSize /*eSize*/) override {}
+
+ virtual vcl::ImageType GetImageSize() override
+ {
+ return vcl::ImageType::Size32;
+ }
+
+ virtual void SetMenuType(ToolBoxMenuType /*eType*/) override {}
+
+ virtual void MergeToolbar(ToolBoxItemId & /*rItemId*/,
+ const OUString& /*rModuleIdentifier*/,
+ CommandToInfoMap& /*rCommandMap*/,
+ MergeToolbarInstruction& /*rInstruction*/) override {}
+
+ virtual void SetItemImage(ToolBoxItemId /*nId*/,
+ const OUString& rCommandURL,
+ const Image& rImage) override
+ {
+ OString sId = OUStringToOString(rCommandURL, RTL_TEXTENCODING_UTF8);
+ m_pWeldedToolBar->set_item_image(sId, Graphic(rImage).GetXGraphic());
+ }
+
+ virtual void UpdateSize() override {}
+
+ virtual void SetItemWindow(ToolBoxItemId /*nItemId*/, vcl::Window* /*pNewWindow*/) override {}
+
+private:
+ weld::Toolbar* m_pWeldedToolBar;
+ weld::Builder* m_pBuilder;
+ ToolBarManager* m_pManager;
+ ToolBoxItemId m_nCurrentId;
+ std::map<const OString, ToolBoxItemId> m_aCommandToId;
+ std::map<ToolBoxItemId, OString> m_aIdToCommand;
+ std::vector<OString> m_aCommandOrder;
+};
+
+IMPL_LINK(WeldToolBarManager, Click, const OString&, rCommand, void)
+{
+ m_nCurrentId = m_aCommandToId[rCommand];
+ m_pManager->OnClick(true);
+}
+
+IMPL_LINK(WeldToolBarManager, ToggleMenuHdl, const OString&, rCommand, void)
+{
+ m_nCurrentId = m_aCommandToId[rCommand];
+ m_pManager->OnDropdownClick(false);
+}
+
+} // end anonymous namespace
+
+// XInterface, XTypeProvider, XServiceInfo
+
+ToolBarManager::ToolBarManager( const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ OUString aResourceName,
+ ToolBox* pToolBar ) :
+ m_bDisposed( false ),
+ m_bFrameActionRegistered( false ),
+ m_bUpdateControllers( false ),
+ m_eSymbolSize(SvtMiscOptions().GetCurrentSymbolsSize()),
+ m_pImpl( new VclToolBarManager( pToolBar ) ),
+ m_pToolBar( pToolBar ),
+ m_aResourceName(std::move( aResourceName )),
+ m_xFrame( rFrame ),
+ m_xContext( rxContext ),
+ m_aAsyncUpdateControllersTimer( "framework::ToolBarManager m_aAsyncUpdateControllersTimer" ),
+ m_sIconTheme( SvtMiscOptions().GetIconTheme() )
+{
+ Init();
+}
+
+ToolBarManager::ToolBarManager( const Reference< XComponentContext >& rxContext,
+ const Reference< XFrame >& rFrame,
+ OUString aResourceName,
+ weld::Toolbar* pToolBar,
+ weld::Builder* pBuilder ) :
+ m_bDisposed( false ),
+ m_bFrameActionRegistered( false ),
+ m_bUpdateControllers( false ),
+ m_eSymbolSize( SvtMiscOptions().GetCurrentSymbolsSize() ),
+ m_pImpl( new WeldToolBarManager( pToolBar, pBuilder ) ),
+ m_pToolBar( nullptr ),
+ m_aResourceName(std::move( aResourceName )),
+ m_xFrame( rFrame ),
+ m_xContext( rxContext ),
+ m_aAsyncUpdateControllersTimer( "framework::ToolBarManager m_aAsyncUpdateControllersTimer" ),
+ m_sIconTheme( SvtMiscOptions().GetIconTheme() )
+{
+ Init();
+}
+
+void ToolBarManager::Init()
+{
+ OSL_ASSERT( m_xContext.is() );
+
+ m_pImpl->Init();
+
+ m_xToolbarControllerFactory = frame::theToolbarControllerFactory::get( m_xContext );
+ m_xURLTransformer = URLTransformer::create( m_xContext );
+
+ m_pImpl->ConnectCallbacks(this);
+
+ if (m_eSymbolSize == SFX_SYMBOLS_SIZE_LARGE)
+ m_pImpl->SetIconSize(ToolBoxButtonSize::Large);
+ else if (m_eSymbolSize == SFX_SYMBOLS_SIZE_32)
+ m_pImpl->SetIconSize(ToolBoxButtonSize::Size32);
+ else
+ m_pImpl->SetIconSize(ToolBoxButtonSize::Small);
+
+ // enables a menu for clipped items and customization
+ SvtCommandOptions aCmdOptions;
+ ToolBoxMenuType nMenuType = ToolBoxMenuType::ClippedItems;
+ if ( !aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, "CreateDialog"))
+ nMenuType |= ToolBoxMenuType::Customize;
+
+ m_pImpl->SetMenuType( nMenuType );
+
+ // set name for testtool, the useful part is after the last '/'
+ sal_Int32 idx = m_aResourceName.lastIndexOf('/');
+ idx++; // will become 0 if '/' not found: use full string
+ std::u16string_view aToolbarName = m_aResourceName.subView( idx );
+ OString aHelpIdAsString = ".HelpId:" + OUStringToOString( aToolbarName, RTL_TEXTENCODING_UTF8 );
+ m_pImpl->SetHelpId( aHelpIdAsString );
+
+ m_aAsyncUpdateControllersTimer.SetTimeout( 50 );
+ m_aAsyncUpdateControllersTimer.SetInvokeHandler( LINK( this, ToolBarManager, AsyncUpdateControllersHdl ) );
+
+ SvtMiscOptions().AddListenerLink( LINK( this, ToolBarManager, MiscOptionsChanged ) );
+}
+
+ToolBarManager::~ToolBarManager()
+{
+ assert(!m_aAsyncUpdateControllersTimer.IsActive());
+ assert(!m_pToolBar); // must be disposed by ToolbarLayoutManager
+}
+
+void ToolBarManager::Destroy()
+{
+ m_pImpl->Destroy();
+
+ SvtMiscOptions().RemoveListenerLink( LINK( this, ToolBarManager, MiscOptionsChanged ) );
+}
+
+ToolBox* ToolBarManager::GetToolBar() const
+{
+ SolarMutexGuard g;
+ return m_pToolBar;
+}
+
+void ToolBarManager::CheckAndUpdateImages()
+{
+ SolarMutexGuard g;
+ bool bRefreshImages = false;
+
+ SvtMiscOptions aMiscOptions;
+ sal_Int16 eNewSymbolSize = aMiscOptions.GetCurrentSymbolsSize();
+
+ if (m_eSymbolSize != eNewSymbolSize )
+ {
+ bRefreshImages = true;
+ m_eSymbolSize = eNewSymbolSize;
+ }
+
+ const OUString& sCurrentIconTheme = aMiscOptions.GetIconTheme();
+ if ( m_sIconTheme != sCurrentIconTheme )
+ {
+ bRefreshImages = true;
+ m_sIconTheme = sCurrentIconTheme;
+ }
+
+ // Refresh images if requested
+ if ( bRefreshImages )
+ RefreshImages();
+}
+
+void ToolBarManager::RefreshImages()
+{
+ SolarMutexGuard g;
+
+ if (m_eSymbolSize == SFX_SYMBOLS_SIZE_LARGE)
+ m_pImpl->SetIconSize(ToolBoxButtonSize::Large);
+ else if (m_eSymbolSize == SFX_SYMBOLS_SIZE_32)
+ m_pImpl->SetIconSize(ToolBoxButtonSize::Size32);
+ else
+ m_pImpl->SetIconSize(ToolBoxButtonSize::Small);
+
+ for ( auto const& it : m_aControllerMap )
+ {
+ Reference< XSubToolbarController > xController( it.second, UNO_QUERY );
+ if ( xController.is() && xController->opensSubToolbar() )
+ {
+ // The button should show the last function that was selected from the
+ // dropdown. The controller should know better than us what it was.
+ xController->updateImage();
+ }
+ else
+ {
+ OUString aCommandURL = m_pImpl->GetItemCommand( it.first );
+ vcl::ImageType eImageType = m_pImpl->GetImageSize();
+ Image aImage = vcl::CommandInfoProvider::GetImageForCommand(aCommandURL, m_xFrame, eImageType);
+ // Try also to query for add-on images before giving up and use an
+ // empty image.
+ bool bBigImages = eImageType != vcl::ImageType::Size16;
+ if ( !aImage )
+ aImage = Image(framework::AddonsOptions().GetImageFromURL(aCommandURL, bBigImages));
+ m_pImpl->SetItemImage( it.first, aCommandURL, aImage );
+ }
+ }
+
+ m_pImpl->UpdateSize();
+}
+
+void ToolBarManager::UpdateControllers()
+{
+
+ if( officecfg::Office::Common::Misc::DisableUICustomization::get() )
+ {
+ Any a;
+ Reference< XLayoutManager > xLayoutManager;
+ Reference< XPropertySet > xFramePropSet( m_xFrame, UNO_QUERY );
+ if ( xFramePropSet.is() )
+ a = xFramePropSet->getPropertyValue("LayoutManager");
+ a >>= xLayoutManager;
+ Reference< XDockableWindow > xDockable( m_pImpl->GetInterface(), UNO_QUERY );
+ if ( xLayoutManager.is() && xDockable.is() )
+ {
+ css::awt::Point aPoint;
+ aPoint.X = aPoint.Y = SAL_MAX_INT32;
+ xLayoutManager->dockWindow( m_aResourceName, DockingArea_DOCKINGAREA_DEFAULT, aPoint );
+ xLayoutManager->lockWindow( m_aResourceName );
+ }
+ }
+
+ if ( !m_bUpdateControllers )
+ {
+ m_bUpdateControllers = true;
+ for (auto const& controller : m_aControllerMap)
+ {
+ try
+ {
+ Reference< XUpdatable > xUpdatable( controller.second, UNO_QUERY );
+ if ( xUpdatable.is() )
+ xUpdatable->update();
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+ }
+ m_bUpdateControllers = false;
+}
+
+//for update toolbar controller via Support Visible
+void ToolBarManager::UpdateController( const css::uno::Reference< css::frame::XToolbarController >& xController)
+{
+
+ if ( !m_bUpdateControllers )
+ {
+ m_bUpdateControllers = true;
+ try
+ { if(xController.is())
+ {
+ Reference< XUpdatable > xUpdatable( xController, UNO_QUERY );
+ if ( xUpdatable.is() )
+ xUpdatable->update();
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+
+ }
+ m_bUpdateControllers = false;
+}
+
+void ToolBarManager::frameAction( const FrameActionEvent& Action )
+{
+ SolarMutexGuard g;
+ if ( Action.Action == FrameAction_CONTEXT_CHANGED && !m_bDisposed )
+ {
+ m_aAsyncUpdateControllersTimer.Start();
+ }
+}
+
+void SAL_CALL ToolBarManager::disposing( const EventObject& Source )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ RemoveControllers();
+
+ if ( m_xDocImageManager.is() )
+ {
+ try
+ {
+ m_xDocImageManager->removeConfigurationListener(
+ Reference< XUIConfigurationListener >(this) );
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ if ( m_xModuleImageManager.is() )
+ {
+ try
+ {
+ m_xModuleImageManager->removeConfigurationListener(
+ Reference< XUIConfigurationListener >(this) );
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ m_xDocImageManager.clear();
+ m_xModuleImageManager.clear();
+
+ if ( Source.Source == Reference< XInterface >( m_xFrame, UNO_QUERY ))
+ m_xFrame.clear();
+
+ m_xContext.clear();
+}
+
+// XComponent
+void SAL_CALL ToolBarManager::dispose()
+{
+ Reference< XComponent > xThis(this);
+
+ {
+ EventObject aEvent( xThis );
+ std::unique_lock aGuard(m_mutex);
+ m_aListenerContainer.disposeAndClear( aGuard, aEvent );
+ }
+ {
+ SolarMutexGuard g;
+
+ if (m_bDisposed)
+ {
+ return;
+ }
+
+ RemoveControllers();
+
+ if ( m_xDocImageManager.is() )
+ {
+ try
+ {
+ m_xDocImageManager->removeConfigurationListener(
+ Reference< XUIConfigurationListener >(this) );
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+ m_xDocImageManager.clear();
+ if ( m_xModuleImageManager.is() )
+ {
+ try
+ {
+ m_xModuleImageManager->removeConfigurationListener(
+ Reference< XUIConfigurationListener >(this) );
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+ m_xModuleImageManager.clear();
+
+ if ( m_aOverflowManager.is() )
+ {
+ m_aOverflowManager->dispose();
+ m_aOverflowManager.clear();
+ }
+
+ // We have to destroy our toolbar instance now.
+ Destroy();
+ m_pToolBar.clear();
+
+ if ( m_bFrameActionRegistered && m_xFrame.is() )
+ {
+ try
+ {
+ m_xFrame->removeFrameActionListener( Reference< XFrameActionListener >(this) );
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ m_xFrame.clear();
+ m_xContext.clear();
+
+ // stop timer to prevent timer events after dispose
+ // do it last because other calls could restart timer in StateChanged()
+ m_aAsyncUpdateControllersTimer.Stop();
+
+ m_bDisposed = true;
+ }
+}
+
+void SAL_CALL ToolBarManager::addEventListener( const Reference< XEventListener >& xListener )
+{
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ std::unique_lock aGuard(m_mutex);
+ m_aListenerContainer.addInterface( aGuard, xListener );
+}
+
+void SAL_CALL ToolBarManager::removeEventListener( const Reference< XEventListener >& xListener )
+{
+ std::unique_lock aGuard(m_mutex);
+ m_aListenerContainer.removeInterface( aGuard, xListener );
+}
+
+// XUIConfigurationListener
+void SAL_CALL ToolBarManager::elementInserted( const css::ui::ConfigurationEvent& Event )
+{
+ impl_elementChanged(false,Event);
+}
+
+void SAL_CALL ToolBarManager::elementRemoved( const css::ui::ConfigurationEvent& Event )
+{
+ impl_elementChanged(true,Event);
+}
+void ToolBarManager::impl_elementChanged(bool const isRemove,
+ const css::ui::ConfigurationEvent& Event)
+{
+ SolarMutexGuard g;
+
+ /* SAFE AREA ----------------------------------------------------------------------------------------------- */
+ if ( m_bDisposed )
+ return;
+
+ Reference< XNameAccess > xNameAccess;
+ sal_Int16 nImageType = sal_Int16();
+ sal_Int16 nCurrentImageType = getCurrentImageType();
+
+ if (!(( Event.aInfo >>= nImageType ) &&
+ ( nImageType == nCurrentImageType ) &&
+ ( Event.Element >>= xNameAccess )))
+ return;
+
+ sal_Int16 nImageInfo( 1 );
+ Reference< XInterface > xIfacDocImgMgr( m_xDocImageManager, UNO_QUERY );
+ if ( xIfacDocImgMgr == Event.Source )
+ nImageInfo = 0;
+
+ const Sequence< OUString > aSeq = xNameAccess->getElementNames();
+ for ( OUString const & commandName : aSeq )
+ {
+ CommandToInfoMap::iterator pIter = m_aCommandMap.find( commandName );
+ if ( pIter != m_aCommandMap.end() && ( pIter->second.nImageInfo >= nImageInfo ))
+ {
+ if (isRemove)
+ {
+ Image aImage;
+ if (( pIter->second.nImageInfo == 0 ) && ( pIter->second.nImageInfo == nImageInfo ))
+ {
+ // Special case: An image from the document image manager has been removed.
+ // It is possible that we have an image at our module image manager. Before
+ // we can remove our image we have to ask our module image manager.
+ Sequence< OUString > aCmdURLSeq{ pIter->first };
+ Sequence< Reference< XGraphic > > aGraphicSeq;
+ aGraphicSeq = m_xModuleImageManager->getImages( nImageType, aCmdURLSeq );
+ aImage = Image( aGraphicSeq[0] );
+ }
+
+ setToolBarImage(aImage,pIter);
+ } // if (isRemove)
+ else
+ {
+ Reference< XGraphic > xGraphic;
+ if ( xNameAccess->getByName( commandName ) >>= xGraphic )
+ {
+ Image aImage( xGraphic );
+ setToolBarImage(aImage,pIter);
+ }
+ pIter->second.nImageInfo = nImageInfo;
+ }
+ }
+ }
+}
+void ToolBarManager::setToolBarImage(const Image& rImage,
+ const CommandToInfoMap::const_iterator& rIter)
+{
+ const ::std::vector<ToolBoxItemId>& rIDs = rIter->second.aIds;
+ m_pImpl->SetItemImage( rIter->second.nId, rIter->first, rImage );
+ for (auto const& it : rIDs)
+ {
+ m_pImpl->SetItemImage(it, rIter->first, rImage);
+ }
+}
+
+void SAL_CALL ToolBarManager::elementReplaced( const css::ui::ConfigurationEvent& Event )
+{
+ impl_elementChanged(false,Event);
+}
+
+void ToolBarManager::RemoveControllers()
+{
+ DBG_TESTSOLARMUTEX();
+ assert(!m_bDisposed);
+
+ m_aSubToolBarControllerMap.clear();
+
+ // i90033
+ // Remove item window pointers from the toolbar. They were
+ // destroyed by the dispose() at the XComponent. This is needed
+ // as VCL code later tries to access the item window data in certain
+ // dtors where the item window is already invalid!
+ for ( ToolBox::ImplToolItems::size_type i = 0; i < m_pImpl->GetItemCount(); i++ )
+ {
+ ToolBoxItemId nItemId = m_pImpl->GetItemId( i );
+ if ( nItemId > ToolBoxItemId(0) )
+ {
+ Reference< XComponent > xComponent( m_aControllerMap[ nItemId ], UNO_QUERY );
+ if ( xComponent.is() )
+ {
+ try
+ {
+ xComponent->dispose();
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+ m_pImpl->SetItemWindow(nItemId, nullptr);
+ }
+ }
+ m_aControllerMap.clear();
+}
+
+void ToolBarManager::CreateControllers()
+{
+ Reference< XWindow > xToolbarWindow = m_pImpl->GetInterface();
+
+ css::util::URL aURL;
+ bool bHasDisabledEntries = SvtCommandOptions().HasEntries( SvtCommandOptions::CMDOPTION_DISABLED );
+ SvtCommandOptions aCmdOptions;
+
+ for ( ToolBox::ImplToolItems::size_type i = 0; i < m_pImpl->GetItemCount(); i++ )
+ {
+ ToolBoxItemId nId = m_pImpl->GetItemId( i );
+ if ( nId == ToolBoxItemId(0) )
+ continue;
+
+ bool bInit( true );
+ bool bCreate( true );
+ Reference< XStatusListener > xController;
+
+ rtl::Reference<svt::ToolboxController> pController;
+
+ OUString aCommandURL( m_pImpl->GetItemCommand( nId ) );
+ // Command can be just an alias to another command.
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, m_aModuleIdentifier);
+ OUString aRealCommandURL( vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties) );
+ if ( !aRealCommandURL.isEmpty() )
+ aCommandURL = aRealCommandURL;
+
+ if ( bHasDisabledEntries )
+ {
+ aURL.Complete = aCommandURL;
+ m_xURLTransformer->parseStrict( aURL );
+ if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, aURL.Path ))
+ {
+ m_aControllerMap[ nId ] = xController;
+ m_pImpl->HideItem( nId, aCommandURL );
+ continue;
+ }
+ }
+
+ if ( m_xToolbarControllerFactory.is() &&
+ m_xToolbarControllerFactory->hasController( aCommandURL, m_aModuleIdentifier ))
+ {
+ PropertyValue aPropValue;
+ std::vector< Any > aPropertyVector;
+
+ aPropValue.Name = "ModuleIdentifier";
+ aPropValue.Value <<= m_aModuleIdentifier;
+ aPropertyVector.push_back( Any( aPropValue ));
+ aPropValue.Name = "Frame";
+ aPropValue.Value <<= m_xFrame;
+ aPropertyVector.push_back( Any( aPropValue ));
+ aPropValue.Name = "ServiceManager";
+ Reference<XMultiServiceFactory> xMSF(m_xContext->getServiceManager(), UNO_QUERY_THROW);
+ aPropValue.Value <<= xMSF;
+ aPropertyVector.push_back( Any( aPropValue ));
+ aPropValue.Name = "ParentWindow";
+ aPropValue.Value <<= xToolbarWindow;
+ aPropertyVector.push_back( Any( aPropValue ));
+ aPropValue.Name = "Identifier";
+ aPropValue.Value <<= sal_uInt16(nId);
+ aPropertyVector.push_back( uno::Any( aPropValue ) );
+
+ Sequence< Any > aArgs( comphelper::containerToSequence( aPropertyVector ));
+ xController.set( m_xToolbarControllerFactory->createInstanceWithArgumentsAndContext( aCommandURL, aArgs, m_xContext ),
+ UNO_QUERY );
+ bInit = false; // Initialization is done through the factory service
+ }
+
+ if (( aCommandURL == ".uno:OpenUrl" ) && ( !m_pImpl->IsItemVisible(nId, aCommandURL)))
+ bCreate = false;
+
+ if ( !xController.is() && bCreate )
+ {
+ xController = m_pImpl->CreateToolBoxController( m_xFrame, nId, aCommandURL );
+ if ( !xController )
+ {
+ if ( aCommandURL.startsWith( ".uno:StyleApply?" ) )
+ {
+ xController.set( new StyleToolbarController( m_xContext, m_xFrame, aCommandURL ));
+ m_pImpl->SetItemCheckable( nId );
+ }
+ else if ( aCommandURL.startsWith( "private:resource/" ) )
+ {
+ xController.set( m_xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.comp.framework.GenericPopupToolbarController", m_xContext ), UNO_QUERY );
+ }
+ else if ( m_pToolBar && m_pToolBar->GetItemData( nId ) != nullptr )
+ {
+ // retrieve additional parameters
+ OUString aControlType = static_cast< AddonsParams* >( m_pToolBar->GetItemData( nId ))->aControlType;
+ sal_uInt16 nWidth = static_cast< AddonsParams* >( m_pToolBar->GetItemData( nId ))->nWidth;
+
+ Reference< XStatusListener > xStatusListener(
+ ToolBarMerger::CreateController( m_xContext,
+ m_xFrame,
+ m_pToolBar,
+ aCommandURL,
+ nId,
+ nWidth,
+ aControlType ).get(), UNO_QUERY );
+
+ xController = xStatusListener;
+ }
+ else if (m_pToolBar)
+ {
+ xController.set(
+ new GenericToolbarController( m_xContext, m_xFrame, m_pToolBar, nId, aCommandURL ));
+
+ // Accessibility support: Set toggle button role for specific commands
+ sal_Int32 nProps = vcl::CommandInfoProvider::GetPropertiesForCommand(aCommandURL, m_aModuleIdentifier);
+ if ( nProps & UICOMMANDDESCRIPTION_PROPERTIES_TOGGLEBUTTON )
+ m_pImpl->SetItemCheckable( nId );
+ }
+ }
+ }
+
+ // Associate ID and controller to be able to retrieve
+ // the controller from the ID later.
+ m_aControllerMap[ nId ] = xController;
+
+ // Fill sub-toolbars into our hash-map
+ Reference< XSubToolbarController > xSubToolBar( xController, UNO_QUERY );
+ if ( xSubToolBar.is() && xSubToolBar->opensSubToolbar() )
+ {
+ OUString aSubToolBarName = xSubToolBar->getSubToolbarName();
+ if ( !aSubToolBarName.isEmpty() )
+ {
+ SubToolBarToSubToolBarControllerMap::iterator pIter =
+ m_aSubToolBarControllerMap.find( aSubToolBarName );
+ if ( pIter == m_aSubToolBarControllerMap.end() )
+ {
+ SubToolBarControllerVector aSubToolBarVector;
+ aSubToolBarVector.push_back( xSubToolBar );
+ m_aSubToolBarControllerMap.emplace(
+ aSubToolBarName, aSubToolBarVector );
+ }
+ else
+ pIter->second.push_back( xSubToolBar );
+ }
+ }
+
+ Reference< XInitialization > xInit( xController, UNO_QUERY );
+ if ( xInit.is() )
+ {
+ if ( bInit )
+ {
+ Reference<XMultiServiceFactory> xMSF(m_xContext->getServiceManager(), UNO_QUERY_THROW);
+ Sequence< Any > aArgs {
+ Any( comphelper::makePropertyValue("Frame", m_xFrame) ),
+ Any( comphelper::makePropertyValue("CommandURL", aCommandURL) ),
+ Any( comphelper::makePropertyValue("ServiceManager", xMSF) ),
+ Any( comphelper::makePropertyValue("ParentWindow", xToolbarWindow) ),
+ Any( comphelper::makePropertyValue("ModuleIdentifier", m_aModuleIdentifier) ),
+ Any( comphelper::makePropertyValue("Identifier", sal_uInt16(nId)) ),
+ };
+
+ xInit->initialize( aArgs );
+
+ if (pController)
+ {
+ if (aCommandURL == ".uno:SwitchXFormsDesignMode" || aCommandURL == ".uno:ViewDataSourceBrowser")
+ pController->setFastPropertyValue_NoBroadcast(1, Any(true));
+ }
+ }
+
+ // Request an item window from the toolbar controller and set it at the VCL toolbar
+ Reference< XToolbarController > xTbxController( xController, UNO_QUERY );
+ if ( xTbxController.is() && xToolbarWindow.is() )
+ {
+ Reference< XWindow > xWindow = xTbxController->createItemWindow( xToolbarWindow );
+ if ( xWindow.is() )
+ {
+ VclPtr<vcl::Window> pItemWin = VCLUnoHelper::GetWindow( xWindow );
+ if ( pItemWin )
+ {
+ WindowType nType = pItemWin->GetType();
+ if ( m_pToolBar && (nType == WindowType::LISTBOX || nType == WindowType::MULTILISTBOX || nType == WindowType::COMBOBOX) )
+ pItemWin->SetAccessibleName( m_pToolBar->GetItemText( nId ) );
+ m_pImpl->SetItemWindow( nId, pItemWin );
+ }
+ }
+ }
+ }
+
+ //for update Controller via support visible state
+ Reference< XPropertySet > xPropSet( xController, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ bool bSupportVisible = true;
+ Any a( xPropSet->getPropertyValue("SupportsVisible") );
+ a >>= bSupportVisible;
+ if (bSupportVisible)
+ {
+ Reference< XToolbarController > xTbxController( xController, UNO_QUERY );
+ UpdateController(xTbxController);
+ }
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+ }
+
+ AddFrameActionListener();
+}
+
+void ToolBarManager::AddFrameActionListener()
+{
+ if ( !m_bFrameActionRegistered && m_xFrame.is() )
+ {
+ m_bFrameActionRegistered = true;
+ m_xFrame->addFrameActionListener( Reference< XFrameActionListener >(this) );
+ }
+}
+
+ToolBoxItemBits ToolBarManager::ConvertStyleToToolboxItemBits( sal_Int32 nStyle )
+{
+ ToolBoxItemBits nItemBits( ToolBoxItemBits::NONE );
+ if ( nStyle & css::ui::ItemStyle::RADIO_CHECK )
+ nItemBits |= ToolBoxItemBits::RADIOCHECK;
+ if ( nStyle & css::ui::ItemStyle::ALIGN_LEFT )
+ nItemBits |= ToolBoxItemBits::LEFT;
+ if ( nStyle & css::ui::ItemStyle::AUTO_SIZE )
+ nItemBits |= ToolBoxItemBits::AUTOSIZE;
+ if ( nStyle & css::ui::ItemStyle::DROP_DOWN )
+ nItemBits |= ToolBoxItemBits::DROPDOWN;
+ if ( nStyle & css::ui::ItemStyle::REPEAT )
+ nItemBits |= ToolBoxItemBits::REPEAT;
+ if ( nStyle & css::ui::ItemStyle::DROPDOWN_ONLY )
+ nItemBits |= ToolBoxItemBits::DROPDOWNONLY;
+ if ( nStyle & css::ui::ItemStyle::TEXT )
+ nItemBits |= ToolBoxItemBits::TEXT_ONLY;
+ if ( nStyle & css::ui::ItemStyle::ICON )
+ nItemBits |= ToolBoxItemBits::ICON_ONLY;
+
+ return nItemBits;
+}
+
+void ToolBarManager::InitImageManager()
+{
+ Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext );
+ if ( !m_xDocImageManager.is() )
+ {
+ Reference< XModel > xModel( GetModelFromFrame() );
+ if ( xModel.is() )
+ {
+ Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY );
+ if ( xSupplier.is() )
+ {
+ Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager();
+ m_xDocImageManager.set( xDocUICfgMgr->getImageManager(), UNO_QUERY );
+ m_xDocImageManager->addConfigurationListener(
+ Reference< XUIConfigurationListener >(this) );
+ }
+ }
+ }
+
+ try
+ {
+ m_aModuleIdentifier = xModuleManager->identify( Reference< XInterface >( m_xFrame, UNO_QUERY ) );
+ }
+ catch (const Exception&)
+ {
+ }
+
+ if ( !m_xModuleImageManager.is() )
+ {
+ Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier =
+ theModuleUIConfigurationManagerSupplier::get( m_xContext );
+ Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier );
+ m_xModuleImageManager.set( xUICfgMgr->getImageManager(), UNO_QUERY );
+ m_xModuleImageManager->addConfigurationListener( Reference< XUIConfigurationListener >(this) );
+ }
+}
+
+void ToolBarManager::FillToolbar( const Reference< XIndexAccess >& rItemContainer )
+{
+ OString aTbxName = OUStringToOString( m_aResourceName, RTL_TEXTENCODING_ASCII_US );
+ SAL_INFO( "fwk.uielement", "framework (cd100003) ::ToolBarManager::FillToolbar " << aTbxName );
+
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ InitImageManager();
+
+ RemoveControllers();
+
+ // reset and fill command map
+ m_pImpl->Clear();
+ m_aControllerMap.clear();
+ m_aCommandMap.clear();
+
+ ToolBoxItemId nId( 1 );
+ CommandInfo aCmdInfo;
+ for ( sal_Int32 n = 0; n < rItemContainer->getCount(); n++ )
+ {
+ Sequence< PropertyValue > aProps;
+ OUString aCommandURL;
+ OUString aLabel;
+ OUString aTooltip;
+ sal_uInt16 nType( css::ui::ItemType::DEFAULT );
+ sal_uInt32 nStyle( 0 );
+
+ try
+ {
+ if ( rItemContainer->getByIndex( n ) >>= aProps )
+ {
+ bool bIsVisible( true );
+ for ( PropertyValue const & prop : std::as_const(aProps) )
+ {
+ if ( prop.Name == ITEM_DESCRIPTOR_COMMANDURL )
+ prop.Value >>= aCommandURL;
+ else if ( prop.Name == "Label" )
+ prop.Value >>= aLabel;
+ else if ( prop.Name == "Tooltip" )
+ prop.Value >>= aTooltip;
+ else if ( prop.Name == "Type" )
+ prop.Value >>= nType;
+ else if ( prop.Name == ITEM_DESCRIPTOR_VISIBLE )
+ prop.Value >>= bIsVisible;
+ else if ( prop.Name == "Style" )
+ prop.Value >>= nStyle;
+ }
+
+ if (!aCommandURL.isEmpty() && vcl::CommandInfoProvider::IsExperimental(aCommandURL, m_aModuleIdentifier) &&
+ !officecfg::Office::Common::Misc::ExperimentalMode::get())
+ {
+ continue;
+ }
+
+ if (( nType == css::ui::ItemType::DEFAULT ) && !aCommandURL.isEmpty() )
+ {
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, m_aModuleIdentifier);
+ if (!aProperties.hasElements()) // E.g., user-provided macro command?
+ aProperties = aProps; // Use existing info, including user-provided Label
+ OUString aString(vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
+
+ ToolBoxItemBits nItemBits = ConvertStyleToToolboxItemBits( nStyle );
+
+ if ( aTooltip.isEmpty() )
+ aTooltip = vcl::CommandInfoProvider::GetTooltipForCommand(aCommandURL, aProperties, m_xFrame);
+
+ if ( aLabel.isEmpty() )
+ aLabel = aString;
+
+ m_pImpl->InsertItem(nId, aString, aCommandURL, aTooltip, aLabel, nItemBits);
+
+ // Fill command map. It stores all our commands and from what
+ // image manager we got our image. So we can decide if we have to use an
+ // image from a notification message.
+ auto pIter = m_aCommandMap.emplace( aCommandURL, aCmdInfo );
+ if ( pIter.second )
+ {
+ aCmdInfo.nId = nId;
+ pIter.first->second.nId = nId;
+ }
+ else
+ {
+ pIter.first->second.aIds.push_back( nId );
+ }
+
+ if ( !bIsVisible )
+ m_pImpl->HideItem( nId, aCommandURL );
+
+ ++nId;
+ }
+ else if ( nType == css::ui::ItemType::SEPARATOR_LINE )
+ {
+ m_pImpl->InsertSeparator();
+ }
+ else if ( nType == css::ui::ItemType::SEPARATOR_SPACE )
+ {
+ m_pImpl->InsertSpace();
+ }
+ else if ( nType == css::ui::ItemType::SEPARATOR_LINEBREAK )
+ {
+ m_pImpl->InsertBreak();
+ }
+ }
+ }
+ catch (const css::lang::IndexOutOfBoundsException&)
+ {
+ break;
+ }
+ }
+
+ // Support add-on toolbar merging here. Working directly on the toolbar object is much
+ // simpler and faster.
+ constexpr sal_uInt16 TOOLBAR_ITEM_STARTID = 1000;
+
+ MergeToolbarInstructionContainer aMergeInstructionContainer;
+
+ // Retrieve the toolbar name from the resource name
+ OUString aToolbarName( m_aResourceName );
+ sal_Int32 nIndex = aToolbarName.lastIndexOf( '/' );
+ if (( nIndex > 0 ) && ( nIndex < aToolbarName.getLength() ))
+ aToolbarName = aToolbarName.copy( nIndex+1 );
+
+ AddonsOptions().GetMergeToolbarInstructions( aToolbarName, aMergeInstructionContainer );
+
+ if ( !aMergeInstructionContainer.empty() )
+ {
+ ToolBoxItemId nItemId( TOOLBAR_ITEM_STARTID );
+ const sal_uInt32 nCount = aMergeInstructionContainer.size();
+ for ( sal_uInt32 i=0; i < nCount; i++ )
+ {
+ MergeToolbarInstruction& rInstruction = aMergeInstructionContainer[i];
+ if ( ToolBarMerger::IsCorrectContext( rInstruction.aMergeContext, m_aModuleIdentifier ))
+ {
+ m_pImpl->MergeToolbar(nItemId, m_aModuleIdentifier, m_aCommandMap, rInstruction);
+ }
+ }
+ }
+
+ // Request images for all toolbar items. Must be done before CreateControllers as
+ // some controllers need access to the image.
+ RequestImages();
+
+ // Create controllers after we set the images. There are controllers which needs
+ // an image at the toolbar at creation time!
+ CreateControllers();
+
+ // Notify controllers that they are now correctly initialized and can start listening
+ // toolbars that will open in popup mode will be updated immediately to avoid flickering
+ if( m_pImpl->WillUsePopupMode() )
+ UpdateControllers();
+ else if ( m_pImpl->IsReallyVisible() )
+ {
+ m_aAsyncUpdateControllersTimer.Start();
+ }
+
+ // Try to retrieve UIName from the container property set and set it as the title
+ // if it is not empty.
+ Reference< XPropertySet > xPropSet( rItemContainer, UNO_QUERY );
+ if ( !xPropSet.is() )
+ return;
+
+ try
+ {
+ OUString aUIName;
+ xPropSet->getPropertyValue("UIName") >>= aUIName;
+ if ( !aUIName.isEmpty() )
+ m_pImpl->SetName( aUIName );
+ }
+ catch (const Exception&)
+ {
+ }
+}
+
+void ToolBarManager::FillAddonToolbar( const Sequence< Sequence< PropertyValue > >& rAddonToolbar )
+{
+ if (!m_pToolBar)
+ return;
+
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ InitImageManager();
+
+ RemoveControllers();
+
+ // reset and fill command map
+ m_pToolBar->Clear();
+ m_aControllerMap.clear();
+ m_aCommandMap.clear();
+
+ ToolBoxItemId nId( 1 );
+ CommandInfo aCmdInfo;
+ for ( const Sequence< PropertyValue >& rSeq : rAddonToolbar )
+ {
+ OUString aURL;
+ OUString aTitle;
+ OUString aContext;
+ OUString aTarget;
+ OUString aControlType;
+ sal_uInt16 nWidth( 0 );
+
+ ToolBarMerger::ConvertSequenceToValues( rSeq, aURL, aTitle, aTarget, aContext, aControlType, nWidth );
+
+ if ( ToolBarMerger::IsCorrectContext( aContext, m_aModuleIdentifier ) )
+ {
+ if ( aURL == "private:separator" )
+ {
+ ToolBox::ImplToolItems::size_type nCount = m_pToolBar->GetItemCount();
+ if ( nCount > 0 && m_pToolBar->GetItemType( nCount-1 ) != ToolBoxItemType::SEPARATOR )
+ m_pToolBar->InsertSeparator();
+ }
+ else
+ {
+ m_pToolBar->InsertItem( nId, aTitle );
+
+ OUString aShortcut(vcl::CommandInfoProvider::GetCommandShortcut(aURL, m_xFrame));
+ if (!aShortcut.isEmpty())
+ m_pToolBar->SetQuickHelpText(nId, aTitle + " (" + aShortcut + ")");
+
+ // Create AddonsParams to hold additional information we will need in the future
+ AddonsParams* pRuntimeItemData = new AddonsParams;
+ pRuntimeItemData->aControlType = aControlType;
+ pRuntimeItemData->nWidth = nWidth;
+ m_pToolBar->SetItemData( nId, pRuntimeItemData );
+ m_pToolBar->SetItemCommand( nId, aURL );
+
+ // Fill command map. It stores all our commands and from what
+ // image manager we got our image. So we can decide if we have to use an
+ // image from a notification message.
+ auto pIter = m_aCommandMap.emplace( aURL, aCmdInfo );
+ if ( pIter.second )
+ {
+ aCmdInfo.nId = nId;
+ pIter.first->second.nId = nId;
+ }
+ else
+ {
+ pIter.first->second.aIds.push_back( nId );
+ }
+ ++nId;
+ }
+ }
+ }
+
+ // Don't setup images yet, AddonsToolbarWrapper::populateImages does that.
+ // (But some controllers might need an image at the toolbar at creation time!)
+ CreateControllers();
+
+ // Notify controllers that they are now correctly initialized and can start listening.
+ UpdateControllers();
+}
+
+void ToolBarManager::FillOverflowToolbar( ToolBox const * pParent )
+{
+ if (!m_pToolBar)
+ return;
+
+ CommandInfo aCmdInfo;
+ bool bInsertSeparator = false;
+ for ( ToolBox::ImplToolItems::size_type i = 0; i < pParent->GetItemCount(); ++i )
+ {
+ ToolBoxItemId nId = pParent->GetItemId( i );
+ if ( pParent->IsItemClipped( nId ) )
+ {
+ if ( bInsertSeparator )
+ {
+ m_pToolBar->InsertSeparator();
+ bInsertSeparator = false;
+ }
+
+ const OUString aCommandURL( pParent->GetItemCommand( nId ) );
+ m_pToolBar->InsertItem( nId, pParent->GetItemText( nId ) );
+ m_pToolBar->SetItemCommand( nId, aCommandURL );
+ m_pToolBar->SetQuickHelpText( nId, pParent->GetQuickHelpText( nId ) );
+
+ // Handle possible add-on controls.
+ AddonsParams* pAddonParams = static_cast< AddonsParams* >( pParent->GetItemData( nId ) );
+ if ( pAddonParams )
+ m_pToolBar->SetItemData( nId, new AddonsParams( *pAddonParams ) );
+
+ // Fill command map. It stores all our commands and from what
+ // image manager we got our image. So we can decide if we have to use an
+ // image from a notification message.
+ auto pIter = m_aCommandMap.emplace( aCommandURL, aCmdInfo );
+ if ( pIter.second )
+ {
+ aCmdInfo.nId = nId;
+ pIter.first->second.nId = nId;
+ }
+ else
+ {
+ pIter.first->second.aIds.push_back( nId );
+ }
+ }
+ else
+ {
+ ToolBoxItemType eType = pParent->GetItemType( i );
+ if ( m_pToolBar->GetItemCount() &&
+ ( eType == ToolBoxItemType::SEPARATOR || eType == ToolBoxItemType::BREAK ) )
+ bInsertSeparator = true;
+ }
+ }
+
+ InitImageManager();
+
+ // Request images for all toolbar items. Must be done before CreateControllers as
+ // some controllers need access to the image.
+ RequestImages();
+
+ // Create controllers after we set the images. There are controllers which needs
+ // an image at the toolbar at creation time!
+ CreateControllers();
+
+ // Notify controllers that they are now correctly initialized and can start listening
+ // toolbars that will open in popup mode will be updated immediately to avoid flickering
+ UpdateControllers();
+}
+
+void ToolBarManager::RequestImages()
+{
+
+ // Request images from image manager
+ Sequence< OUString > aCmdURLSeq( comphelper::mapKeysToSequence(m_aCommandMap) );
+ Sequence< Reference< XGraphic > > aDocGraphicSeq;
+ Sequence< Reference< XGraphic > > aModGraphicSeq;
+
+ SvtMiscOptions aMiscOptions;
+
+ sal_Int16 nImageType = getCurrentImageType();
+
+ if ( m_xDocImageManager.is() )
+ aDocGraphicSeq = m_xDocImageManager->getImages(nImageType, aCmdURLSeq);
+ aModGraphicSeq = m_xModuleImageManager->getImages(nImageType, aCmdURLSeq);
+
+ sal_uInt32 i = 0;
+ CommandToInfoMap::iterator pIter = m_aCommandMap.begin();
+ CommandToInfoMap::iterator pEnd = m_aCommandMap.end();
+ while ( pIter != pEnd )
+ {
+ Image aImage;
+ if ( aDocGraphicSeq.hasElements() )
+ aImage = Image( aDocGraphicSeq[i] );
+ if ( !aImage )
+ {
+ aImage = Image( aModGraphicSeq[i] );
+ // Try also to query for add-on images before giving up and use an
+ // empty image.
+ if ( !aImage )
+ aImage = Image(framework::AddonsOptions().GetImageFromURL(aCmdURLSeq[i], aMiscOptions.AreCurrentSymbolsLarge()));
+
+ pIter->second.nImageInfo = 1; // mark image as module based
+ }
+ else
+ {
+ pIter->second.nImageInfo = 0; // mark image as document based
+ }
+ setToolBarImage(aImage,pIter);
+ ++pIter;
+ ++i;
+ }
+}
+
+void ToolBarManager::notifyRegisteredControllers( const OUString& aUIElementName, const OUString& aCommand )
+{
+ SolarMutexClearableGuard aGuard;
+ if ( m_aSubToolBarControllerMap.empty() )
+ return;
+
+ SubToolBarToSubToolBarControllerMap::const_iterator pIter =
+ m_aSubToolBarControllerMap.find( aUIElementName );
+
+ if ( pIter == m_aSubToolBarControllerMap.end() )
+ return;
+
+ const SubToolBarControllerVector& rSubToolBarVector = pIter->second;
+ if ( rSubToolBarVector.empty() )
+ return;
+
+ SubToolBarControllerVector aNotifyVector = rSubToolBarVector;
+ aGuard.clear();
+
+ const sal_uInt32 nCount = aNotifyVector.size();
+ for ( sal_uInt32 i=0; i < nCount; i++ )
+ {
+ try
+ {
+ Reference< XSubToolbarController > xController = aNotifyVector[i];
+ if ( xController.is() )
+ xController->functionSelected( aCommand );
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+}
+
+void ToolBarManager::HandleClick(ClickAction eClickAction)
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ ToolBoxItemId nId( m_pImpl->GetCurItemId() );
+ ToolBarControllerMap::const_iterator pIter = m_aControllerMap.find( nId );
+ if ( pIter == m_aControllerMap.end() )
+ return;
+
+ Reference< XToolbarController > xController( pIter->second, UNO_QUERY );
+
+ if ( xController.is() )
+ {
+ switch (eClickAction)
+ {
+ case ClickAction::Click:
+ xController->click();
+ break;
+
+ case ClickAction::DblClick:
+ xController->doubleClick();
+ break;
+
+ case ClickAction::Execute:
+ xController->execute(0);
+ break;
+ }
+ }
+}
+
+void ToolBarManager::OnClick(bool bUseExecute)
+{
+ if (bUseExecute)
+ HandleClick(ClickAction::Execute);
+ else
+ HandleClick(ClickAction::Click);
+}
+
+IMPL_LINK_NOARG(ToolBarManager, DropdownClick, ToolBox *, void)
+{
+ OnDropdownClick(true);
+}
+
+void ToolBarManager::OnDropdownClick(bool bCreatePopupWindow)
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ ToolBoxItemId nId( m_pImpl->GetCurItemId() );
+ ToolBarControllerMap::const_iterator pIter = m_aControllerMap.find( nId );
+ if ( pIter == m_aControllerMap.end() )
+ return;
+
+ Reference< XToolbarController > xController( pIter->second, UNO_QUERY );
+
+ if ( xController.is() )
+ {
+ if (bCreatePopupWindow)
+ {
+ Reference< XWindow > xWin = xController->createPopupWindow();
+ if ( xWin.is() )
+ xWin->setFocus();
+ }
+ else
+ {
+ xController->click();
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ToolBarManager, DoubleClick, ToolBox *, void)
+{
+ HandleClick(ClickAction::DblClick);
+}
+
+Reference< XModel > ToolBarManager::GetModelFromFrame() const
+{
+ Reference< XController > xController = m_xFrame->getController();
+ Reference< XModel > xModel;
+ if ( xController.is() )
+ xModel = xController->getModel();
+
+ return xModel;
+}
+
+bool ToolBarManager::IsPluginMode() const
+{
+ bool bPluginMode( false );
+
+ if ( m_xFrame.is() )
+ {
+ Reference< XModel > xModel = GetModelFromFrame();
+ if ( xModel.is() )
+ {
+ Sequence< PropertyValue > aSeq = xModel->getArgs();
+ utl::MediaDescriptor aMediaDescriptor( aSeq );
+ bPluginMode = aMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_VIEWONLY, false );
+ }
+ }
+
+ return bPluginMode;
+}
+
+void ToolBarManager::AddCustomizeMenuItems(ToolBox const * pToolBar)
+{
+ if (!m_pToolBar)
+ return;
+
+ // No config menu entries if command ".uno:ConfigureDialog" is not enabled
+ Reference< XDispatch > xDisp;
+ css::util::URL aURL;
+ if ( m_xFrame.is() )
+ {
+ Reference< XDispatchProvider > xProv( m_xFrame, UNO_QUERY );
+ aURL.Complete = ".uno:ConfigureDialog";
+ m_xURLTransformer->parseStrict( aURL );
+ if ( xProv.is() )
+ xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
+
+ if ( !xDisp.is() || IsPluginMode() )
+ return;
+ }
+
+ // popup menu for quick customization
+ bool bHideDisabledEntries = !officecfg::Office::Common::View::Menu::DontHideDisabledEntry::get();
+
+ ::PopupMenu *pMenu = pToolBar->GetMenu();
+
+ // copy all menu items 'Visible buttons, Customize toolbar, Dock toolbar,
+ // Dock all Toolbars) from the loaded resource into the toolbar menu
+ sal_uInt16 nGroupLen = pMenu->GetItemCount();
+ if (nGroupLen)
+ pMenu->InsertSeparator();
+
+ VclPtr<PopupMenu> xVisibleItemsPopupMenu;
+
+ if (!m_aResourceName.startsWith("private:resource/toolbar/addon_"))
+ {
+ pMenu->InsertItem(MENUITEM_TOOLBAR_VISIBLEBUTTON, FwkResId(STR_TOOLBAR_VISIBLE_BUTTONS));
+ xVisibleItemsPopupMenu = VclPtr<PopupMenu>::Create();
+ pMenu->SetPopupMenu(MENUITEM_TOOLBAR_VISIBLEBUTTON, xVisibleItemsPopupMenu);
+
+ if (m_pToolBar->IsCustomize())
+ {
+ pMenu->InsertItem(MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR, FwkResId(STR_TOOLBAR_CUSTOMIZE_TOOLBAR));
+ pMenu->SetItemCommand(MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR, ".uno:ConfigureToolboxVisible");
+ }
+ pMenu->InsertSeparator();
+ }
+
+ if (pToolBar->IsFloatingMode())
+ {
+ pMenu->InsertItem(MENUITEM_TOOLBAR_DOCKTOOLBAR, FwkResId(STR_TOOLBAR_DOCK_TOOLBAR));
+ pMenu->SetAccelKey(MENUITEM_TOOLBAR_DOCKTOOLBAR, vcl::KeyCode(KEY_F10, true, true, false, false));
+ }
+ else
+ {
+ pMenu->InsertItem(MENUITEM_TOOLBAR_UNDOCKTOOLBAR, FwkResId(STR_TOOLBAR_UNDOCK_TOOLBAR));
+ pMenu->SetAccelKey(MENUITEM_TOOLBAR_UNDOCKTOOLBAR, vcl::KeyCode(KEY_F10, true, true, false, false));
+ }
+
+ pMenu->InsertItem(MENUITEM_TOOLBAR_DOCKALLTOOLBAR, FwkResId(STR_TOOLBAR_DOCK_ALL_TOOLBARS));
+ pMenu->InsertSeparator();
+ pMenu->InsertItem(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION, FwkResId(STR_TOOLBAR_LOCK_TOOLBAR), MenuItemBits::CHECKABLE);
+ pMenu->InsertItem(MENUITEM_TOOLBAR_CLOSE, FwkResId(STR_TOOLBAR_CLOSE_TOOLBAR));
+
+ if (m_pToolBar->IsCustomize())
+ {
+ bool bIsFloating( false );
+
+ DockingManager* pDockMgr = vcl::Window::GetDockingManager();
+ if ( pDockMgr )
+ bIsFloating = pDockMgr->IsFloating( m_pToolBar );
+
+ if ( !bIsFloating )
+ {
+ pMenu->EnableItem(MENUITEM_TOOLBAR_DOCKALLTOOLBAR, false);
+ Reference< XDockableWindow > xDockable( VCLUnoHelper::GetInterface( m_pToolBar ), UNO_QUERY );
+ if( xDockable.is() )
+ pMenu->CheckItem(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION, xDockable->isLocked());
+ }
+ else
+ pMenu->EnableItem(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION, false);
+
+ if (officecfg::Office::Common::Misc::DisableUICustomization::get())
+ {
+ pMenu->EnableItem(MENUITEM_TOOLBAR_VISIBLEBUTTON, false);
+ pMenu->EnableItem(MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR, false);
+ pMenu->EnableItem(MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION, false);
+ }
+
+ // Disable menu item CLOSE if the toolbar has no closer
+ if( !(pToolBar->GetFloatStyle() & WB_CLOSEABLE) )
+ pMenu->EnableItem(MENUITEM_TOOLBAR_CLOSE, false);
+
+ // Temporary stores a Command --> Url map to update contextual menu with the
+ // correct icons. The popup icons are by default the same as those in the
+ // toolbar. They are not correct for contextual popup menu.
+ std::map< OUString, Image > commandToImage;
+
+ if (xVisibleItemsPopupMenu)
+ {
+ // Go through all toolbar items and add them to the context menu
+ for ( ToolBox::ImplToolItems::size_type nPos = 0; nPos < m_pToolBar->GetItemCount(); ++nPos )
+ {
+ if ( m_pToolBar->GetItemType(nPos) == ToolBoxItemType::BUTTON )
+ {
+ ToolBoxItemId nId = m_pToolBar->GetItemId(nPos);
+ OUString aCommandURL = m_pToolBar->GetItemCommand( nId );
+ xVisibleItemsPopupMenu->InsertItem( STARTID_CUSTOMIZE_POPUPMENU+nPos, m_pToolBar->GetItemText( nId ), MenuItemBits::CHECKABLE );
+ xVisibleItemsPopupMenu->CheckItem( STARTID_CUSTOMIZE_POPUPMENU+nPos, m_pToolBar->IsItemVisible( nId ) );
+ xVisibleItemsPopupMenu->SetItemCommand( STARTID_CUSTOMIZE_POPUPMENU+nPos, aCommandURL );
+ Image aImage(vcl::CommandInfoProvider::GetImageForCommand(aCommandURL, m_xFrame));
+ commandToImage[aCommandURL] = aImage;
+ xVisibleItemsPopupMenu->SetItemImage( STARTID_CUSTOMIZE_POPUPMENU+nPos, aImage );
+ vcl::KeyCode aKeyCodeShortCut = vcl::CommandInfoProvider::GetCommandKeyCodeShortcut( aCommandURL, m_xFrame );
+ xVisibleItemsPopupMenu->SetAccelKey( STARTID_CUSTOMIZE_POPUPMENU+nPos, aKeyCodeShortCut );
+ }
+ else
+ {
+ xVisibleItemsPopupMenu->InsertSeparator();
+ }
+ }
+ }
+
+ // Now we go through all the contextual menu to update the icons
+ // and accelerator key shortcuts
+ std::map< OUString, Image >::iterator it;
+ for ( sal_uInt16 nPos = 0; nPos < pMenu->GetItemCount(); ++nPos )
+ {
+ sal_uInt16 nId = pMenu->GetItemId( nPos );
+ OUString cmdUrl = pMenu->GetItemCommand( nId );
+ it = commandToImage.find( cmdUrl );
+ if (it != commandToImage.end()) {
+ pMenu->SetItemImage( nId, it->second );
+ }
+ vcl::KeyCode aKeyCodeShortCut = vcl::CommandInfoProvider::GetCommandKeyCodeShortcut( cmdUrl, m_xFrame );
+ if ( aKeyCodeShortCut.GetFullCode() != 0 )
+ pMenu->SetAccelKey( nId, aKeyCodeShortCut );
+ }
+ }
+
+ // Set the title of the menu
+ pMenu->SetText( pToolBar->GetText() );
+
+ if ( bHideDisabledEntries )
+ pMenu->RemoveDisabledEntries();
+}
+
+IMPL_LINK( ToolBarManager, MenuButton, ToolBox*, pToolBar, void )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ assert( !m_aOverflowManager.is() );
+
+ VclPtrInstance<ToolBox> pOverflowToolBar( pToolBar, WB_BORDER | WB_SCROLL );
+ pOverflowToolBar->SetLineSpacing(true);
+ m_aOverflowManager.set( new ToolBarManager( m_xContext, m_xFrame, OUString(), pOverflowToolBar ) );
+ m_aOverflowManager->FillOverflowToolbar( pToolBar );
+
+ ::Size aActSize( pOverflowToolBar->GetSizePixel() );
+ ::Size aSize( pOverflowToolBar->CalcWindowSizePixel() );
+ aSize.setWidth( aActSize.Width() );
+ pOverflowToolBar->SetOutputSizePixel( aSize );
+
+ aSize = pOverflowToolBar->CalcPopupWindowSizePixel();
+ pOverflowToolBar->SetSizePixel( aSize );
+
+ pOverflowToolBar->EnableDocking();
+ pOverflowToolBar->AddEventListener( LINK( this, ToolBarManager, OverflowEventListener ) );
+ vcl::Window::GetDockingManager()->StartPopupMode( pToolBar, pOverflowToolBar, FloatWinPopupFlags::AllMouseButtonClose );
+
+ // send HOME key to subtoolbar in order to select first item if keyboard activated
+ if(pToolBar->IsKeyEvent() )
+ {
+ ::KeyEvent aEvent( 0, vcl::KeyCode( KEY_HOME ) );
+ pOverflowToolBar->KeyInput(aEvent);
+ }
+}
+
+IMPL_LINK( ToolBarManager, OverflowEventListener, VclWindowEvent&, rWindowEvent, void )
+{
+ if ( rWindowEvent.GetId() != VclEventId::WindowEndPopupMode )
+ return;
+
+ if ( m_aOverflowManager.is() )
+ {
+ m_aOverflowManager->dispose();
+ m_aOverflowManager.clear();
+ }
+}
+
+IMPL_LINK( ToolBarManager, MenuPreExecute, ToolBox*, pToolBar, void )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ AddCustomizeMenuItems( pToolBar );
+}
+
+IMPL_LINK( ToolBarManager, MenuSelect, Menu*, pMenu, bool )
+{
+ // We have to hold a reference to ourself as it is possible that we will be disposed and
+ // our refcount could be zero (destruction) otherwise.
+ Reference< XInterface > xKeepAlive( static_cast< OWeakObject* >( this ), UNO_QUERY );
+
+ {
+ // The guard must be in its own context as the we can get destroyed when our
+ // own xInterface reference get destroyed!
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return true;
+
+ switch ( pMenu->GetCurItemId() )
+ {
+ case MENUITEM_TOOLBAR_CUSTOMIZETOOLBAR:
+ {
+ Reference< XDispatch > xDisp;
+ css::util::URL aURL;
+ if ( m_xFrame.is() )
+ {
+ Reference< XDispatchProvider > xProv( m_xFrame, UNO_QUERY );
+ aURL.Complete = ".uno:ConfigureDialog";
+ m_xURLTransformer->parseStrict( aURL );
+ if ( xProv.is() )
+ xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
+ }
+
+ if ( xDisp.is() )
+ {
+ Sequence< PropertyValue > aPropSeq{ comphelper::makePropertyValue(
+ "ResourceURL", m_aResourceName) };
+
+ xDisp->dispatch( aURL, aPropSeq );
+ }
+ break;
+ }
+
+ case MENUITEM_TOOLBAR_UNDOCKTOOLBAR:
+ {
+ ExecuteInfo* pExecuteInfo = new ExecuteInfo;
+
+ pExecuteInfo->aToolbarResName = m_aResourceName;
+ pExecuteInfo->nCmd = EXEC_CMD_UNDOCKTOOLBAR;
+ pExecuteInfo->xLayoutManager = getLayoutManagerFromFrame( m_xFrame );
+
+ Application::PostUserEvent( LINK(nullptr, ToolBarManager, ExecuteHdl_Impl), pExecuteInfo );
+ break;
+ }
+
+ case MENUITEM_TOOLBAR_DOCKTOOLBAR:
+ {
+ ExecuteInfo* pExecuteInfo = new ExecuteInfo;
+
+ pExecuteInfo->aToolbarResName = m_aResourceName;
+ pExecuteInfo->nCmd = EXEC_CMD_DOCKTOOLBAR;
+ pExecuteInfo->xLayoutManager = getLayoutManagerFromFrame( m_xFrame );
+
+ Application::PostUserEvent( LINK(nullptr, ToolBarManager, ExecuteHdl_Impl), pExecuteInfo );
+ break;
+ }
+
+ case MENUITEM_TOOLBAR_DOCKALLTOOLBAR:
+ {
+ ExecuteInfo* pExecuteInfo = new ExecuteInfo;
+
+ pExecuteInfo->aToolbarResName = m_aResourceName;
+ pExecuteInfo->nCmd = EXEC_CMD_DOCKALLTOOLBARS;
+ pExecuteInfo->xLayoutManager = getLayoutManagerFromFrame( m_xFrame );
+
+ Application::PostUserEvent( LINK(nullptr, ToolBarManager, ExecuteHdl_Impl), pExecuteInfo );
+ break;
+ }
+
+ case MENUITEM_TOOLBAR_LOCKTOOLBARPOSITION:
+ {
+ Reference< XLayoutManager > xLayoutManager = getLayoutManagerFromFrame( m_xFrame );
+ if ( xLayoutManager.is() )
+ {
+ Reference< XDockableWindow > xDockable( VCLUnoHelper::GetInterface( m_pToolBar ), UNO_QUERY );
+
+ if( xDockable->isLocked() )
+ xLayoutManager->unlockWindow( m_aResourceName );
+ else
+ xLayoutManager->lockWindow( m_aResourceName );
+ }
+ break;
+ }
+
+ case MENUITEM_TOOLBAR_CLOSE:
+ {
+ ExecuteInfo* pExecuteInfo = new ExecuteInfo;
+
+ pExecuteInfo->aToolbarResName = m_aResourceName;
+ pExecuteInfo->nCmd = EXEC_CMD_CLOSETOOLBAR;
+ pExecuteInfo->xLayoutManager = getLayoutManagerFromFrame( m_xFrame );
+ pExecuteInfo->xWindow = VCLUnoHelper::GetInterface( m_pToolBar );
+
+ Application::PostUserEvent( LINK(nullptr, ToolBarManager, ExecuteHdl_Impl), pExecuteInfo );
+ break;
+ }
+
+ default:
+ {
+ sal_uInt16 nId = pMenu->GetCurItemId();
+ if(( nId > 0 ) && ( nId < TOOLBOX_MENUITEM_START ))
+ // Items in the "enable/disable" sub-menu
+ {
+ // toggle toolbar button visibility
+ OUString aCommand = pMenu->GetItemCommand( nId );
+
+ Reference< XLayoutManager > xLayoutManager = getLayoutManagerFromFrame( m_xFrame );
+ if ( xLayoutManager.is() )
+ {
+ Reference< XUIElementSettings > xUIElementSettings( xLayoutManager->getElement( m_aResourceName ), UNO_QUERY );
+ if ( xUIElementSettings.is() )
+ {
+ Reference< XIndexContainer > xItemContainer( xUIElementSettings->getSettings( true ), UNO_QUERY );
+ sal_Int32 nCount = xItemContainer->getCount();
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ Sequence< PropertyValue > aProp;
+ sal_Int32 nVisibleIndex( -1 );
+ OUString aCommandURL;
+ bool bVisible( false );
+
+ if ( xItemContainer->getByIndex( i ) >>= aProp )
+ {
+ for ( sal_Int32 j = 0; j < aProp.getLength(); j++ )
+ {
+ if ( aProp[j].Name == ITEM_DESCRIPTOR_COMMANDURL )
+ {
+ aProp[j].Value >>= aCommandURL;
+ }
+ else if ( aProp[j].Name == ITEM_DESCRIPTOR_VISIBLE )
+ {
+ aProp[j].Value >>= bVisible;
+ nVisibleIndex = j;
+ }
+ }
+
+ if (( aCommandURL == aCommand ) && ( nVisibleIndex >= 0 ))
+ {
+ // We have found the requested item, toggle the visible flag
+ // and write back the configuration settings to the toolbar
+ aProp.getArray()[nVisibleIndex].Value <<= !bVisible;
+ try
+ {
+ xItemContainer->replaceByIndex( i, Any( aProp ));
+ xUIElementSettings->setSettings( xItemContainer );
+ Reference< XPropertySet > xPropSet( xUIElementSettings, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ Reference< XUIConfigurationPersistence > xUICfgMgr;
+ if (( xPropSet->getPropertyValue("ConfigurationSource") >>= xUICfgMgr ) && ( xUICfgMgr.is() ))
+ xUICfgMgr->store();
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+IMPL_LINK_NOARG(ToolBarManager, Select, ToolBox *, void)
+{
+ if ( m_bDisposed )
+ return;
+
+ sal_Int16 nKeyModifier( static_cast<sal_Int16>(m_pToolBar->GetModifier()) );
+ ToolBoxItemId nId( m_pToolBar->GetCurItemId() );
+
+ ToolBarControllerMap::const_iterator pIter = m_aControllerMap.find( nId );
+ if ( pIter != m_aControllerMap.end() )
+ {
+ Reference< XToolbarController > xController( pIter->second, UNO_QUERY );
+
+ if ( xController.is() )
+ xController->execute( nKeyModifier );
+ }
+}
+
+IMPL_LINK( ToolBarManager, StateChanged, StateChangedType const *, pStateChangedType, void )
+{
+ if ( m_bDisposed )
+ return;
+
+ if ( *pStateChangedType == StateChangedType::ControlBackground )
+ {
+ CheckAndUpdateImages();
+ }
+ else if ( *pStateChangedType == StateChangedType::Visible )
+ {
+ if ( m_pToolBar->IsReallyVisible() )
+ {
+ m_aAsyncUpdateControllersTimer.Start();
+ }
+ }
+ else if ( *pStateChangedType == StateChangedType::InitShow )
+ {
+ m_aAsyncUpdateControllersTimer.Start();
+ }
+}
+
+IMPL_LINK( ToolBarManager, DataChanged, DataChangedEvent const *, pDataChangedEvent, void )
+{
+ if ((( pDataChangedEvent->GetType() == DataChangedEventType::SETTINGS ) ||
+ ( pDataChangedEvent->GetType() == DataChangedEventType::DISPLAY )) &&
+ ( pDataChangedEvent->GetFlags() & AllSettingsFlags::STYLE ))
+ {
+ CheckAndUpdateImages();
+ }
+
+ for ( ToolBox::ImplToolItems::size_type nPos = 0; nPos < m_pToolBar->GetItemCount(); ++nPos )
+ {
+ const ToolBoxItemId nId = m_pToolBar->GetItemId(nPos);
+ vcl::Window* pWindow = m_pToolBar->GetItemWindow( nId );
+ if ( pWindow )
+ {
+ const DataChangedEvent& rDCEvt( *pDataChangedEvent );
+ pWindow->DataChanged( rDCEvt );
+ }
+ }
+
+ if ( !m_pToolBar->IsFloatingMode() &&
+ m_pToolBar->IsVisible() )
+ {
+ // Resize toolbar, layout manager is resize listener and will calc
+ // the layout automatically.
+ ::Size aSize( m_pToolBar->CalcWindowSizePixel() );
+ m_pToolBar->SetOutputSizePixel( aSize );
+ }
+}
+
+IMPL_LINK_NOARG(ToolBarManager, MiscOptionsChanged, LinkParamNone*, void)
+{
+ CheckAndUpdateImages();
+}
+
+IMPL_LINK_NOARG(ToolBarManager, AsyncUpdateControllersHdl, Timer *, void)
+{
+ // The guard must be in its own context as the we can get destroyed when our
+ // own xInterface reference get destroyed!
+ Reference< XComponent > xThis(this);
+
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ return;
+
+ // Request to update our controllers
+ m_aAsyncUpdateControllersTimer.Stop();
+ UpdateControllers();
+}
+
+IMPL_STATIC_LINK( ToolBarManager, ExecuteHdl_Impl, void*, p, void )
+{
+ ExecuteInfo* pExecuteInfo = static_cast<ExecuteInfo*>(p);
+ try
+ {
+ // Asynchronous execution as this can lead to our own destruction!
+ if (( pExecuteInfo->nCmd == EXEC_CMD_CLOSETOOLBAR ) &&
+ ( pExecuteInfo->xLayoutManager.is() ) &&
+ ( pExecuteInfo->xWindow.is() ))
+ {
+ // Use docking window close to close the toolbar. The toolbar layout manager is
+ // listener and will react correctly according to the context sensitive
+ // flag of our toolbar.
+ VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( pExecuteInfo->xWindow );
+ DockingWindow* pDockWin = dynamic_cast< DockingWindow* >( pWin.get() );
+ if ( pDockWin )
+ pDockWin->Close();
+ }
+ else if (( pExecuteInfo->nCmd == EXEC_CMD_UNDOCKTOOLBAR ) &&
+ ( pExecuteInfo->xLayoutManager.is() ))
+ {
+ pExecuteInfo->xLayoutManager->floatWindow( pExecuteInfo->aToolbarResName );
+ }
+ else if (( pExecuteInfo->nCmd == EXEC_CMD_DOCKTOOLBAR ) &&
+ ( pExecuteInfo->xLayoutManager.is() ))
+ {
+ css::awt::Point aPoint;
+ aPoint.X = aPoint.Y = SAL_MAX_INT32;
+ pExecuteInfo->xLayoutManager->dockWindow( pExecuteInfo->aToolbarResName,
+ DockingArea_DOCKINGAREA_DEFAULT,
+ aPoint );
+ }
+ else if (( pExecuteInfo->nCmd == EXEC_CMD_DOCKALLTOOLBARS ) &&
+ ( pExecuteInfo->xLayoutManager.is() ))
+ {
+ pExecuteInfo->xLayoutManager->dockAllWindows( UIElementType::TOOLBAR );
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+
+ delete pExecuteInfo;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/toolbarmerger.cxx b/framework/source/uielement/toolbarmerger.cxx
new file mode 100644
index 000000000..2657873c3
--- /dev/null
+++ b/framework/source/uielement/toolbarmerger.cxx
@@ -0,0 +1,645 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <uielement/toolbarmerger.hxx>
+#include <framework/generictoolbarcontroller.hxx>
+
+#include <uielement/buttontoolbarcontroller.hxx>
+#include <uielement/comboboxtoolbarcontroller.hxx>
+#include <uielement/dropdownboxtoolbarcontroller.hxx>
+#include <uielement/edittoolbarcontroller.hxx>
+#include <uielement/imagebuttontoolbarcontroller.hxx>
+#include <uielement/spinfieldtoolbarcontroller.hxx>
+#include <uielement/togglebuttontoolbarcontroller.hxx>
+#include <uielement/FixedTextToolbarController.hxx>
+#include <uielement/FixedImageToolbarController.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace framework
+{
+
+const char MERGE_TOOLBAR_URL[] = "URL";
+const char MERGE_TOOLBAR_TITLE[] = "Title";
+const char MERGE_TOOLBAR_CONTEXT[] = "Context";
+const char MERGE_TOOLBAR_TARGET[] = "Target";
+const char MERGE_TOOLBAR_CONTROLTYPE[] = "ControlType";
+const char MERGE_TOOLBAR_WIDTH[] = "Width";
+
+const char16_t MERGECOMMAND_ADDAFTER[] = u"AddAfter";
+const char16_t MERGECOMMAND_ADDBEFORE[] = u"AddBefore";
+const char16_t MERGECOMMAND_REPLACE[] = u"Replace";
+const char16_t MERGECOMMAND_REMOVE[] = u"Remove";
+
+const char16_t MERGEFALLBACK_ADDLAST[] = u"AddLast";
+const char16_t MERGEFALLBACK_ADDFIRST[] = u"AddFirst";
+const char16_t MERGEFALLBACK_IGNORE[] = u"Ignore";
+
+const char16_t TOOLBARCONTROLLER_BUTTON[] = u"Button";
+const char16_t TOOLBARCONTROLLER_COMBOBOX[] = u"Combobox";
+const char16_t TOOLBARCONTROLLER_EDIT[] = u"Editfield";
+const char16_t TOOLBARCONTROLLER_SPINFIELD[] = u"Spinfield";
+const char16_t TOOLBARCONTROLLER_IMGBUTTON[] = u"ImageButton";
+const char16_t TOOLBARCONTROLLER_DROPDOWNBOX[] = u"Dropdownbox";
+const char16_t TOOLBARCONTROLLER_DROPDOWNBTN[] = u"DropdownButton";
+const char16_t TOOLBARCONTROLLER_TOGGLEDDBTN[] = u"ToggleDropdownButton";
+const char16_t TOOLBARCONTROLLER_FIXEDIMAGE[] = u"FixedImage";
+const char16_t TOOLBARCONTROLLER_FIXEDTEXT[] = u"FixedText";
+
+const char TOOLBOXITEM_SEPARATOR_STR[] = "private:separator";
+
+using namespace ::com::sun::star;
+
+/**
+ Check whether a module identifier is part of a context
+ defined by a colon separated list of module identifier.
+
+ @param
+ rContext
+
+ Describes a context string list where all contexts
+ are delimited by a colon. For more information about
+ the module identifier used as context strings see the
+ IDL description of css::frame::XModuleManager
+
+ @param
+ rModuleIdentifier
+
+ A string describing a module identifier. See IDL
+ description of css::frame::XModuleManager.
+
+ @result
+ The result is true if the rContext is an empty string
+ or rModuleIdentifier is part of the context string.
+
+*/
+bool ToolBarMerger::IsCorrectContext(
+ std::u16string_view rContext,
+ std::u16string_view rModuleIdentifier )
+{
+ return ( rContext.empty() || ( rContext.find( rModuleIdentifier ) != std::u16string_view::npos ));
+}
+
+/**
+ Converts a sequence, sequence of property values to
+ a vector of structs.
+
+ @param
+ rSequence
+
+ Provides a sequence, sequence of property values.
+
+ @param
+ rContainer
+
+ A vector of AddonToolbarItems which will hold the
+ conversion from the rSequence argument.
+*/
+void ToolBarMerger::ConvertSeqSeqToVector(
+ const uno::Sequence< uno::Sequence< beans::PropertyValue > >& rSequence,
+ AddonToolbarItemContainer& rContainer )
+{
+ sal_Int32 nLen( rSequence.getLength() );
+ for ( sal_Int32 i = 0; i < nLen; i++ )
+ {
+ AddonToolbarItem aAddonToolbarItem;
+ ConvertSequenceToValues( rSequence[i],
+ aAddonToolbarItem.aCommandURL,
+ aAddonToolbarItem.aLabel,
+ aAddonToolbarItem.aTarget,
+ aAddonToolbarItem.aContext,
+ aAddonToolbarItem.aControlType,
+ aAddonToolbarItem.nWidth );
+ rContainer.push_back( aAddonToolbarItem );
+ }
+}
+
+/**
+ Converts a sequence of property values to single
+ values.
+
+ @param
+ rSequence
+
+ Provides a sequence of property values.
+
+ @param
+ rCommandURL
+
+ Contains the value of the property with
+ Name="CommandURL".
+
+ @param
+ rLabel
+
+ Contains the value of the property with
+ Name="Title"
+
+ @param
+ rTarget
+
+ Contains the value of the property with
+ Name="Target"
+
+ @param
+ rContext
+
+ Contains the value of the property with
+ Name="Context"
+
+ @param
+ rControlType
+
+ Contains the value of the property with
+ Name="ControlType"
+
+ @result
+ All possible mapping between sequence of property
+ values and the single values are done.
+
+*/
+void ToolBarMerger::ConvertSequenceToValues(
+ const uno::Sequence< beans::PropertyValue >& rSequence,
+ OUString& rCommandURL,
+ OUString& rLabel,
+ OUString& rTarget,
+ OUString& rContext,
+ OUString& rControlType,
+ sal_uInt16& rWidth )
+{
+ for ( beans::PropertyValue const & prop : rSequence )
+ {
+ if ( prop.Name == MERGE_TOOLBAR_URL )
+ prop.Value >>= rCommandURL;
+ else if ( prop.Name == MERGE_TOOLBAR_TITLE )
+ prop.Value >>= rLabel;
+ else if ( prop.Name == MERGE_TOOLBAR_CONTEXT )
+ prop.Value >>= rContext;
+ else if ( prop.Name == MERGE_TOOLBAR_TARGET )
+ prop.Value >>= rTarget;
+ else if ( prop.Name == MERGE_TOOLBAR_CONTROLTYPE )
+ prop.Value >>= rControlType;
+ else if ( prop.Name == MERGE_TOOLBAR_WIDTH )
+ {
+ sal_Int32 aValue = 0;
+ prop.Value >>= aValue;
+ rWidth = sal_uInt16( aValue );
+ }
+ }
+}
+
+/**
+ Tries to find the reference point provided and delivers
+ position and result of the search process.
+
+ @param
+ pToolbar
+
+ Must be a valid pointer to a toolbar with items which
+ should be searched.
+
+ @param
+ rReferencePoint
+
+ A command URL which should be the reference point for
+ the coming merge operation.
+
+ @result
+ Provides information about the search result, the
+ position of the reference point and the toolbar used.
+*/
+ReferenceToolbarPathInfo ToolBarMerger::FindReferencePoint(
+ const ToolBox* pToolbar,
+ std::u16string_view rReferencePoint )
+{
+ ReferenceToolbarPathInfo aResult;
+ aResult.bResult = false;
+ aResult.nPos = ToolBox::ITEM_NOTFOUND;
+
+ const ToolBox::ImplToolItems::size_type nSize( pToolbar->GetItemCount() );
+
+ for ( ToolBox::ImplToolItems::size_type i = 0; i < nSize; i++ )
+ {
+ const ToolBoxItemId nItemId = pToolbar->GetItemId( i );
+ if ( nItemId > ToolBoxItemId(0) )
+ {
+ const OUString rCmd = pToolbar->GetItemCommand( nItemId );
+ if ( rCmd == rReferencePoint )
+ {
+ aResult.bResult = true;
+ aResult.nPos = i;
+ return aResult;
+ }
+ }
+ }
+
+ return aResult;
+}
+
+/**
+ Processes a merge operation.
+
+ @param
+ pToolbar
+
+ A valid pointer to the toolbar where the merge
+ operation is applied to.
+
+ @param
+ nPos
+
+ The reference position of the toolbar item for
+ the merge operation. Value must be between
+ 0 and number of toolbar items - 1.
+
+ @param
+ rItemId
+
+ A unique item ID.
+
+ @param
+ rModuleIdentifier
+
+ The current application module context.
+
+ @param
+ rMergeCommand
+
+ A merge command.
+
+ @param
+ rMergeCommandParameter.
+
+ An optional argument for the merge command.
+
+ @param
+ rItems
+
+ Toolbar items which are associated to the merge
+ command.
+
+ @result
+ Returns true for a successful operation otherwise
+ false.
+*/
+bool ToolBarMerger::ProcessMergeOperation(
+ ToolBox* pToolbar,
+ ToolBox::ImplToolItems::size_type nPos,
+ ToolBoxItemId& rItemId,
+ CommandToInfoMap& rCommandMap,
+ std::u16string_view rModuleIdentifier,
+ std::u16string_view rMergeCommand,
+ std::u16string_view rMergeCommandParameter,
+ const AddonToolbarItemContainer& rItems )
+{
+ if ( rMergeCommand == MERGECOMMAND_ADDAFTER )
+ MergeItems( pToolbar, nPos, 1, rItemId, rCommandMap, rModuleIdentifier, rItems );
+ else if ( rMergeCommand == MERGECOMMAND_ADDBEFORE )
+ MergeItems( pToolbar, nPos, 0, rItemId, rCommandMap, rModuleIdentifier, rItems );
+ else if ( rMergeCommand == MERGECOMMAND_REPLACE )
+ ReplaceItem( pToolbar, nPos, rItemId, rCommandMap, rModuleIdentifier, rItems );
+ else if ( rMergeCommand == MERGECOMMAND_REMOVE )
+ RemoveItems( pToolbar, nPos, rMergeCommandParameter );
+ else
+ return false;
+ return true;
+}
+
+/**
+ Processes a merge fallback operation.
+
+ @param
+ pToolbar
+
+ A valid pointer to the toolbar where the merge
+ fall back operation is applied to.
+
+ @param
+ nPos
+
+ The reference position of the toolbar item for
+ the merge operation. Value must be between
+ 0 and number of toolbar items - 1.
+
+ @param
+ rItemId
+
+ A unique item ID.
+
+ @param
+ rModuleIdentifier
+
+ The current application module context.
+
+ @param
+ rMergeCommand
+
+ A merge command.
+
+ @param
+ rItems
+
+ Toolbar items which are associated to the merge
+ command.
+
+ @result
+ Returns true for a successful operation otherwise
+ false.
+*/
+bool ToolBarMerger::ProcessMergeFallback(
+ ToolBox* pToolbar,
+ ToolBoxItemId& rItemId,
+ CommandToInfoMap& rCommandMap,
+ std::u16string_view rModuleIdentifier,
+ std::u16string_view rMergeCommand,
+ std::u16string_view rMergeFallback,
+ const AddonToolbarItemContainer& rItems )
+{
+ if (( rMergeFallback == MERGEFALLBACK_IGNORE ) ||
+ ( rMergeCommand == MERGECOMMAND_REPLACE ) ||
+ ( rMergeCommand == MERGECOMMAND_REMOVE ) )
+ {
+ return true;
+ }
+ else if (( rMergeCommand == MERGECOMMAND_ADDBEFORE ) ||
+ ( rMergeCommand == MERGECOMMAND_ADDAFTER ) )
+ {
+ if ( rMergeFallback == MERGEFALLBACK_ADDFIRST )
+ MergeItems( pToolbar, 0, 0, rItemId, rCommandMap, rModuleIdentifier, rItems );
+ else if ( rMergeFallback == MERGEFALLBACK_ADDLAST )
+ MergeItems( pToolbar, ToolBox::APPEND, 0, rItemId, rCommandMap, rModuleIdentifier, rItems );
+ else
+ return false;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ Merges (adds) toolbar items into an existing toolbar.
+
+ @param
+ pToolbar
+
+ A valid pointer to the toolbar where the merge
+ fall back operation is applied to.
+
+ @param
+ nPos
+
+ The reference position of the toolbar item for
+ the merge operation. Value must be between
+ 0 and number of toolbar items - 1.
+
+ @param
+ rItemId
+
+ A unique item ID.
+
+ @param
+ rModuleIdentifier
+
+ The current application module context.
+
+ @param
+ rItems
+
+ Toolbar items which are associated to the merge
+ command.
+*/
+void ToolBarMerger::MergeItems(
+ ToolBox* pToolbar,
+ ToolBox::ImplToolItems::size_type nPos,
+ sal_uInt16 nModIndex,
+ ToolBoxItemId& rItemId,
+ CommandToInfoMap& rCommandMap,
+ std::u16string_view rModuleIdentifier,
+ const AddonToolbarItemContainer& rAddonToolbarItems )
+{
+ const sal_Int32 nSize( rAddonToolbarItems.size() );
+
+ for ( sal_Int32 i = 0; i < nSize; i++ )
+ {
+ const AddonToolbarItem& rItem = rAddonToolbarItems[i];
+ if ( IsCorrectContext( rItem.aContext, rModuleIdentifier ))
+ {
+ ToolBox::ImplToolItems::size_type nInsPos = nPos;
+ if (nInsPos != ToolBox::APPEND)
+ {
+ nInsPos += nModIndex+i;
+ if ( nInsPos > pToolbar->GetItemCount() )
+ nInsPos = ToolBox::APPEND;
+ }
+
+ if ( rItem.aCommandURL == TOOLBOXITEM_SEPARATOR_STR )
+ pToolbar->InsertSeparator( nInsPos );
+ else
+ {
+ CommandToInfoMap::iterator pIter = rCommandMap.find( rItem.aCommandURL );
+ if ( pIter == rCommandMap.end())
+ {
+ CommandInfo aCmdInfo;
+ aCmdInfo.nId = rItemId;
+ const CommandToInfoMap::value_type aValue( rItem.aCommandURL, aCmdInfo );
+ rCommandMap.insert( aValue );
+ }
+ else
+ {
+ pIter->second.aIds.push_back( rItemId );
+ }
+
+ ToolBarMerger::CreateToolbarItem( pToolbar, nInsPos, rItemId, rItem );
+ }
+
+ ++rItemId;
+ }
+ }
+}
+
+/**
+ Replaces a toolbar item with new items for an
+ existing toolbar.
+
+ @param
+ pToolbar
+
+ A valid pointer to the toolbar where the merge
+ fall back operation is applied to.
+
+ @param
+ nPos
+
+ The reference position of the toolbar item for
+ the merge operation. Value must be between
+ 0 and number of toolbar items - 1.
+
+ @param
+ rItemId
+
+ A unique item ID.
+
+ @param
+ rModuleIdentifier
+
+ The current application module context.
+
+ @param
+ rItems
+
+ Toolbar items which are associated to the merge
+ command.
+*/
+void ToolBarMerger::ReplaceItem(
+ ToolBox* pToolbar,
+ ToolBox::ImplToolItems::size_type nPos,
+ ToolBoxItemId& rItemId,
+ CommandToInfoMap& rCommandMap,
+ std::u16string_view rModuleIdentifier,
+ const AddonToolbarItemContainer& rAddonToolbarItems )
+{
+ pToolbar->RemoveItem( nPos );
+ MergeItems( pToolbar, nPos, 0, rItemId, rCommandMap, rModuleIdentifier, rAddonToolbarItems );
+}
+
+/**
+ Removes toolbar items from an existing toolbar.
+
+ @param
+ pToolbar
+
+ A valid pointer to the toolbar where the merge
+ fall back operation is applied to.
+
+ @param
+ nPos
+
+ The reference position of the toolbar item for
+ the merge operation. Value must be between
+ 0 and number of toolbar items - 1.
+
+ @param
+ rMergeCommandParameter.
+
+ An optional argument for the merge command.
+*/
+void ToolBarMerger::RemoveItems(
+ ToolBox* pToolbar,
+ ToolBox::ImplToolItems::size_type nPos,
+ std::u16string_view rMergeCommandParameter )
+{
+ sal_Int32 nCount = o3tl::toInt32(rMergeCommandParameter);
+ if ( nCount > 0 )
+ {
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ if ( nPos < pToolbar->GetItemCount() )
+ pToolbar->RemoveItem( nPos );
+ }
+ }
+}
+
+/**
+ Removes toolbar items from an existing toolbar.
+
+ @param
+ pToolbar
+
+ A valid pointer to the toolbar where the merge
+ fall back operation is applied to.
+
+ @param
+ nPos
+
+ The reference position of the toolbar item for
+ the merge operation. Value must be between
+ 0 and number of toolbar items - 1.
+
+ @param
+ rMergeCommandParameter.
+
+ An optional argument for the merge command.
+
+ @result
+ Returns true for a successful operation otherwise
+ false.
+*/
+rtl::Reference<::cppu::OWeakObject> ToolBarMerger::CreateController(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< frame::XFrame > & xFrame,
+ ToolBox* pToolbar,
+ const OUString& rCommandURL,
+ ToolBoxItemId nId,
+ sal_uInt16 nWidth,
+ std::u16string_view rControlType )
+{
+ rtl::Reference<::cppu::OWeakObject> pResult;
+
+ if ( rControlType == TOOLBARCONTROLLER_BUTTON )
+ pResult = new ButtonToolbarController( rxContext, pToolbar, rCommandURL );
+ else if ( rControlType == TOOLBARCONTROLLER_COMBOBOX )
+ pResult = new ComboboxToolbarController( rxContext, xFrame, pToolbar, nId, nWidth, rCommandURL );
+ else if ( rControlType == TOOLBARCONTROLLER_EDIT )
+ pResult = new EditToolbarController( rxContext, xFrame, pToolbar, nId, nWidth, rCommandURL );
+ else if ( rControlType == TOOLBARCONTROLLER_SPINFIELD )
+ pResult = new SpinfieldToolbarController( rxContext, xFrame, pToolbar, nId, nWidth, rCommandURL );
+ else if ( rControlType == TOOLBARCONTROLLER_IMGBUTTON )
+ pResult = new ImageButtonToolbarController( rxContext, xFrame, pToolbar, nId, rCommandURL );
+ else if ( rControlType == TOOLBARCONTROLLER_DROPDOWNBOX )
+ pResult = new DropdownToolbarController( rxContext, xFrame, pToolbar, nId, nWidth, rCommandURL );
+ else if ( rControlType == TOOLBARCONTROLLER_DROPDOWNBTN )
+ pResult = new ToggleButtonToolbarController( rxContext, xFrame, pToolbar, nId,
+ ToggleButtonToolbarController::Style::DropDownButton, rCommandURL );
+ else if ( rControlType == TOOLBARCONTROLLER_FIXEDIMAGE )
+ pResult = new FixedImageToolbarController( rxContext, xFrame, pToolbar, nId, rCommandURL );
+ else if ( rControlType == TOOLBARCONTROLLER_FIXEDTEXT )
+ pResult = new FixedTextToolbarController( rxContext, xFrame, pToolbar, nId, rCommandURL );
+ else if ( rControlType == TOOLBARCONTROLLER_TOGGLEDDBTN )
+ pResult = new ToggleButtonToolbarController( rxContext, xFrame, pToolbar, nId,
+ ToggleButtonToolbarController::Style::ToggleDropDownButton, rCommandURL );
+ else
+ pResult = new GenericToolbarController( rxContext, xFrame, pToolbar, nId, rCommandURL );
+
+ return pResult;
+}
+
+void ToolBarMerger::CreateToolbarItem( ToolBox* pToolbar, ToolBox::ImplToolItems::size_type nPos, ToolBoxItemId nItemId, const AddonToolbarItem& rItem )
+{
+ assert(pToolbar->GetItemData(nItemId) == nullptr); // that future would contain a double free
+
+ pToolbar->InsertItem( nItemId, rItem.aLabel, ToolBoxItemBits::NONE, nPos );
+ pToolbar->SetItemCommand( nItemId, rItem.aCommandURL );
+ pToolbar->SetQuickHelpText( nItemId, rItem.aLabel );
+ pToolbar->SetItemText( nItemId, rItem.aLabel );
+ pToolbar->EnableItem( nItemId );
+ pToolbar->SetItemState( nItemId, TRISTATE_FALSE );
+
+ // Use the user data to store add-on specific data with the toolbar item
+ AddonsParams* pAddonParams = new AddonsParams;
+ pAddonParams->aControlType = rItem.aControlType;
+ pAddonParams->nWidth = rItem.nWidth;
+ pToolbar->SetItemData( nItemId, pAddonParams );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/toolbarmodemenucontroller.cxx b/framework/source/uielement/toolbarmodemenucontroller.cxx
new file mode 100644
index 000000000..1740bf1f1
--- /dev/null
+++ b/framework/source/uielement/toolbarmodemenucontroller.cxx
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/toolbarmodemenucontroller.hxx>
+#include <services.h>
+
+#include <com/sun/star/awt/MenuItemStyle.hpp>
+#include <com/sun/star/ui/UIElementType.hpp>
+#include <com/sun/star/frame/XModuleManager.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+
+
+#include <officecfg/Office/Common.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/EnumContext.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/types.hxx>
+#include <unotools/confignode.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+// Defines
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::ui;
+
+namespace framework
+{
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL ToolbarModeMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.ToolbarModeMenuController";
+}
+
+sal_Bool SAL_CALL ToolbarModeMenuController::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL ToolbarModeMenuController::getSupportedServiceNames()
+{
+ return { SERVICENAME_POPUPMENUCONTROLLER };
+}
+
+
+ToolbarModeMenuController::ToolbarModeMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
+ svt::PopupMenuControllerBase( xContext ),
+ m_xContext( xContext )
+{
+}
+
+ToolbarModeMenuController::~ToolbarModeMenuController()
+{
+}
+
+void ToolbarModeMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ if ( officecfg::Office::Common::Misc::DisableUICustomization::get() )
+ return;
+
+ SolarMutexGuard aSolarMutexGuard;
+ resetPopupMenu( rPopupMenu );
+
+ const Reference<XComponentContext> xContext (::comphelper::getProcessComponentContext() );
+ const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( xContext );
+ vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(m_xFrame));
+
+ OUStringBuffer aPath("org.openoffice.Office.UI.ToolbarMode/Applications/");
+ switch ( eApp )
+ {
+ case vcl::EnumContext::Application::Writer:
+ aPath.append("Writer");
+ break;
+ case vcl::EnumContext::Application::Calc:
+ aPath.append("Calc");
+ break;
+ case vcl::EnumContext::Application::Impress:
+ aPath.append("Impress");
+ break;
+ case vcl::EnumContext::Application::Draw:
+ aPath.append("Draw");
+ break;
+ case vcl::EnumContext::Application::Formula:
+ aPath.append("Formula");
+ break;
+ case vcl::EnumContext::Application::Base:
+ aPath.append("Base");
+ break;
+ default:
+ break;
+ }
+ aPath.append("/Modes");
+
+ const utl::OConfigurationTreeRoot aModesNode(
+ m_xContext,
+ aPath.makeStringAndClear(),
+ false);
+ if ( !aModesNode.isValid() )
+ return;
+
+ const Sequence<OUString> aModeNodeNames (aModesNode.getNodeNames());
+ const sal_Int32 nCount(aModeNodeNames.getLength());
+ tools::Long nCountToolbar = 0;
+
+ for ( sal_Int32 nReadIndex = 0; nReadIndex < nCount; ++nReadIndex )
+ {
+ const utl::OConfigurationNode aModeNode(aModesNode.openNode(aModeNodeNames[nReadIndex]));
+ if ( !aModeNode.isValid() )
+ continue;
+
+ OUString aLabel = comphelper::getString( aModeNode.getNodeValue( "Label" ) );
+ OUString aCommandArg = comphelper::getString( aModeNode.getNodeValue( "CommandArg" ) );
+ tools::Long nPosition = comphelper::getINT32( aModeNode.getNodeValue( "MenuPosition" ) );
+ bool isExperimental = comphelper::getBOOL( aModeNode.getNodeValue( "IsExperimental" ) );
+ bool hasNotebookbar = comphelper::getBOOL( aModeNode.getNodeValue( "HasNotebookbar" ) );
+
+ // Allow Notebookbar only in experimental mode
+ if ( isExperimental && !officecfg::Office::Common::Misc::ExperimentalMode::get() )
+ continue;
+ if (!hasNotebookbar)
+ nCountToolbar++;
+
+ m_xPopupMenu->insertItem( nReadIndex+1, aLabel, css::awt::MenuItemStyle::RADIOCHECK, nPosition );
+ rPopupMenu->setCommand( nReadIndex+1, aCommandArg );
+ }
+ rPopupMenu->insertSeparator(nCountToolbar);
+}
+
+// XEventListener
+void SAL_CALL ToolbarModeMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(this);
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) );
+ m_xPopupMenu.clear();
+}
+
+// XStatusListener
+void SAL_CALL ToolbarModeMenuController::statusChanged( const FeatureStateEvent& Event )
+{
+ OUString aFeatureURL( Event.FeatureURL.Complete );
+
+ // All other status events will be processed here
+ osl::ClearableMutexGuard aLock( m_aMutex );
+ Reference< css::awt::XPopupMenu > xPopupMenu( m_xPopupMenu );
+ aLock.clear();
+
+ if ( !xPopupMenu.is() )
+ return;
+
+ SolarMutexGuard aGuard;
+
+ bool bSetCheckmark = false;
+ bool bCheckmark = false;
+ for (sal_Int16 i = 0, nCount = xPopupMenu->getItemCount(); i < nCount; ++i)
+ {
+ sal_Int16 nId = xPopupMenu->getItemId(i);
+ if ( nId == 0 )
+ continue;
+
+ OUString aCmd = xPopupMenu->getCommand(nId);
+ if ( aCmd == aFeatureURL )
+ {
+ // Enable/disable item
+ xPopupMenu->enableItem(nId, Event.IsEnabled);
+
+ // Checkmark
+ if ( Event.State >>= bCheckmark )
+ bSetCheckmark = true;
+
+ if ( bSetCheckmark )
+ xPopupMenu->checkItem(nId, bCheckmark);
+ else
+ {
+ OUString aItemText;
+
+ if ( Event.State >>= aItemText )
+ xPopupMenu->setItemText(nId, aItemText);
+ }
+ }
+ }
+}
+
+// XMenuListener
+void SAL_CALL ToolbarModeMenuController::itemSelected( const css::awt::MenuEvent& rEvent )
+{
+ auto aArgs(comphelper::InitPropertySequence({{"Mode", Any(m_xPopupMenu->getCommand(rEvent.MenuId))}}));
+ dispatchCommand(m_aCommandURL, aArgs);
+}
+
+void SAL_CALL ToolbarModeMenuController::itemActivated( const css::awt::MenuEvent& )
+{
+ const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( m_xContext );
+ vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(m_xFrame));
+
+ OUStringBuffer aPath("org.openoffice.Office.UI.ToolbarMode/Applications/");
+ switch ( eApp )
+ {
+ case vcl::EnumContext::Application::Writer:
+ aPath.append("Writer");
+ break;
+ case vcl::EnumContext::Application::Calc:
+ aPath.append("Calc");
+ break;
+ case vcl::EnumContext::Application::Impress:
+ aPath.append("Impress");
+ break;
+ case vcl::EnumContext::Application::Draw:
+ aPath.append("Draw");
+ break;
+ case vcl::EnumContext::Application::Formula:
+ aPath.append("Formula");
+ break;
+ case vcl::EnumContext::Application::Base:
+ aPath.append("Base");
+ break;
+ default:
+ break;
+ }
+
+ const utl::OConfigurationTreeRoot aModesNode(
+ m_xContext,
+ aPath.makeStringAndClear(),
+ false);
+ if ( !aModesNode.isValid() )
+ return;
+
+ OUString aMode = comphelper::getString( aModesNode.getNodeValue( "Active" ) );
+
+ for ( int i = 0; i < m_xPopupMenu->getItemCount(); ++i )
+ {
+ sal_Int16 nItemId(m_xPopupMenu->getItemId(i));
+ m_xPopupMenu->checkItem(nItemId, aMode == m_xPopupMenu->getCommand(nItemId));
+ }
+}
+
+// XPopupMenuController
+void SAL_CALL ToolbarModeMenuController::setPopupMenu( const Reference< css::awt::XPopupMenu >& xPopupMenu )
+{
+ osl::MutexGuard aLock( m_aMutex );
+
+ throwIfDisposed();
+
+ if ( m_xFrame.is() && !m_xPopupMenu.is() )
+ {
+ // Create popup menu on demand
+ SolarMutexGuard aSolarMutexGuard;
+
+ m_xPopupMenu = xPopupMenu;
+ m_xPopupMenu->addMenuListener( Reference< css::awt::XMenuListener >(this) );
+ fillPopupMenu( m_xPopupMenu );
+ }
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_ToolbarModeMenuController_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::ToolbarModeMenuController(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/toolbarsmenucontroller.cxx b/framework/source/uielement/toolbarsmenucontroller.cxx
new file mode 100644
index 000000000..730190240
--- /dev/null
+++ b/framework/source/uielement/toolbarsmenucontroller.cxx
@@ -0,0 +1,794 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/toolbarsmenucontroller.hxx>
+
+#include <algorithm>
+#include <string_view>
+#include <unordered_map>
+
+#include <services.h>
+#include <strings.hrc>
+#include <classes/fwkresid.hxx>
+#include <framework/sfxhelperfunctions.hxx>
+#include <uiconfiguration/windowstateproperties.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/awt/MenuItemStyle.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/UIElementType.hpp>
+#include <com/sun/star/ui/theWindowStateConfiguration.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/window.hxx>
+#include <unotools/cmdoptions.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+// Defines
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::ui;
+
+constexpr OUStringLiteral CMD_RESTOREVISIBILITY = u".cmd:RestoreVisibility";
+constexpr OUStringLiteral CMD_LOCKTOOLBARS = u".uno:ToolbarLock";
+
+constexpr OUStringLiteral STATIC_CMD_PART = u".uno:AvailableToolbars?Toolbar:string=";
+const char STATIC_INTERNAL_CMD_PART[] = ".cmd:";
+
+namespace framework
+{
+
+typedef std::unordered_map< OUString, OUString > ToolbarHashMap;
+
+namespace {
+
+struct ToolBarEntry
+{
+ OUString aUIName;
+ OUString aCommand;
+ bool bVisible;
+ const CollatorWrapper* pCollatorWrapper;
+};
+
+}
+
+static bool CompareToolBarEntry( const ToolBarEntry& aOne, const ToolBarEntry& aTwo )
+{
+ sal_Int32 nComp = aOne.pCollatorWrapper->compareString( aOne.aUIName, aTwo.aUIName );
+
+ return nComp < 0;
+}
+
+static Reference< XLayoutManager > getLayoutManagerFromFrame( const Reference< XFrame >& rFrame )
+{
+ Reference< XPropertySet > xPropSet( rFrame, UNO_QUERY );
+ Reference< XLayoutManager > xLayoutManager;
+
+ try
+ {
+ xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
+ }
+ catch ( const UnknownPropertyException& )
+ {
+ }
+
+ return xLayoutManager;
+}
+
+namespace {
+
+struct ToolBarInfo
+{
+ OUString aToolBarResName;
+ OUString aToolBarUIName;
+};
+
+}
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL ToolbarsMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.framework.ToolBarsMenuController";
+}
+
+sal_Bool SAL_CALL ToolbarsMenuController::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL ToolbarsMenuController::getSupportedServiceNames()
+{
+ return { SERVICENAME_POPUPMENUCONTROLLER };
+}
+
+constexpr OUStringLiteral g_aPropUIName( u"UIName" );
+constexpr OUStringLiteral g_aPropResourceURL( u"ResourceURL" );
+
+ToolbarsMenuController::ToolbarsMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
+ svt::PopupMenuControllerBase( xContext ),
+ m_xContext( xContext ),
+ m_bResetActive( false ),
+ m_aIntlWrapper(SvtSysLocale().GetUILanguageTag())
+{
+}
+
+ToolbarsMenuController::~ToolbarsMenuController()
+{
+}
+
+void ToolbarsMenuController::addCommand(
+ Reference< css::awt::XPopupMenu > const & rPopupMenu, const OUString& rCommandURL, const OUString& rLabel )
+{
+ sal_uInt16 nItemId = m_xPopupMenu->getItemCount()+1;
+
+ OUString aLabel;
+ if ( rLabel.isEmpty() )
+ {
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommandURL, m_aModuleName);
+ aLabel = vcl::CommandInfoProvider::GetMenuLabelForCommand(aProperties);
+ }
+ else
+ aLabel = rLabel;
+
+ rPopupMenu->insertItem( nItemId, aLabel, 0, nItemId );
+ rPopupMenu->setCommand( nItemId, rCommandURL );
+
+ bool bInternal = rCommandURL.startsWith( STATIC_INTERNAL_CMD_PART );
+ if ( !bInternal )
+ {
+ if ( !getDispatchFromCommandURL( rCommandURL ).is() )
+ m_xPopupMenu->enableItem( nItemId, false );
+ }
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ css::uno::Reference<css::graphic::XGraphic> xGraphic;
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+
+ if ( rSettings.GetUseImagesInMenus() )
+ xGraphic = vcl::CommandInfoProvider::GetXGraphicForCommand(rCommandURL, m_xFrame);
+
+ if (xGraphic.is())
+ rPopupMenu->setItemImage(nItemId, xGraphic, false);
+
+ m_aCommandVector.push_back( rCommandURL );
+}
+
+Reference< XDispatch > ToolbarsMenuController::getDispatchFromCommandURL( const OUString& rCommandURL )
+{
+ URL aTargetURL;
+ Reference< XURLTransformer > xURLTransformer;
+ Reference< XFrame > xFrame;
+
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ xURLTransformer = m_xURLTransformer;
+ xFrame = m_xFrame;
+ }
+
+ aTargetURL.Complete = rCommandURL;
+ xURLTransformer->parseStrict( aTargetURL );
+ Reference< XDispatchProvider > xDispatchProvider( xFrame, UNO_QUERY );
+ if ( xDispatchProvider.is() )
+ return xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+ else
+ return Reference< XDispatch >();
+}
+
+static void fillHashMap( const Sequence< Sequence< css::beans::PropertyValue > >& rSeqToolBars,
+ ToolbarHashMap& rHashMap )
+{
+ for ( Sequence< css::beans::PropertyValue > const & props : rSeqToolBars )
+ {
+ OUString aResourceURL;
+ OUString aUIName;
+ for ( css::beans::PropertyValue const & prop : props )
+ {
+ if ( prop.Name == "ResourceURL" )
+ prop.Value >>= aResourceURL;
+ else if ( prop.Name == "UIName" )
+ prop.Value >>= aUIName;
+ }
+
+ if ( !aResourceURL.isEmpty() &&
+ rHashMap.find( aResourceURL ) == rHashMap.end() )
+ rHashMap.emplace( aResourceURL, aUIName );
+ }
+}
+
+// private function
+Sequence< Sequence< css::beans::PropertyValue > > ToolbarsMenuController::getLayoutManagerToolbars( const Reference< css::frame::XLayoutManager >& rLayoutManager )
+{
+ std::vector< ToolBarInfo > aToolBarArray;
+ const Sequence< Reference< XUIElement > > aUIElements = rLayoutManager->getElements();
+ for ( Reference< XUIElement > const & xUIElement : aUIElements )
+ {
+ Reference< XPropertySet > xPropSet( xUIElement, UNO_QUERY );
+ if ( xPropSet.is() && xUIElement.is() )
+ {
+ try
+ {
+ OUString aResName;
+ sal_Int16 nType( -1 );
+ xPropSet->getPropertyValue("Type") >>= nType;
+ xPropSet->getPropertyValue("ResourceURL") >>= aResName;
+
+ if (( nType == css::ui::UIElementType::TOOLBAR ) &&
+ !aResName.isEmpty() )
+ {
+ ToolBarInfo aToolBarInfo;
+
+ aToolBarInfo.aToolBarResName = aResName;
+
+ SolarMutexGuard aGuard;
+ Reference< css::awt::XWindow > xWindow( xUIElement->getRealInterface(), UNO_QUERY );
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow )
+ aToolBarInfo.aToolBarUIName = pWindow->GetText();
+
+ aToolBarArray.push_back( aToolBarInfo );
+ }
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+ }
+
+ Sequence< Sequence< css::beans::PropertyValue > > aSeq( aToolBarArray.size() );
+ auto pSeq = aSeq.getArray();
+ const sal_uInt32 nCount = aToolBarArray.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ Sequence< css::beans::PropertyValue > aTbSeq{
+ comphelper::makePropertyValue(g_aPropUIName, aToolBarArray[i].aToolBarUIName),
+ comphelper::makePropertyValue(g_aPropResourceURL, aToolBarArray[i].aToolBarResName)
+ };
+ pSeq[i] = aTbSeq;
+ }
+
+ return aSeq;
+}
+
+
+void ToolbarsMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu )
+{
+ if( officecfg::Office::Common::Misc::DisableUICustomization::get() )
+ return;
+
+ SolarMutexGuard aSolarMutexGuard;
+ resetPopupMenu( rPopupMenu );
+
+ m_aCommandVector.clear();
+
+ // Retrieve layout manager for additional information
+ Reference< XLayoutManager > xLayoutManager( getLayoutManagerFromFrame( m_xFrame ));
+
+ m_bResetActive = false;
+ if ( !xLayoutManager.is() )
+ return;
+
+ ToolbarHashMap aToolbarHashMap;
+
+ if ( m_xDocCfgMgr.is() )
+ {
+ Sequence< Sequence< css::beans::PropertyValue > > aSeqDocToolBars =
+ m_xDocCfgMgr->getUIElementsInfo( UIElementType::TOOLBAR );
+ fillHashMap( aSeqDocToolBars, aToolbarHashMap );
+ }
+
+ if ( m_xModuleCfgMgr.is() )
+ {
+ Sequence< Sequence< css::beans::PropertyValue > > aSeqToolBars =
+ m_xModuleCfgMgr->getUIElementsInfo( UIElementType::TOOLBAR );
+ fillHashMap( aSeqToolBars, aToolbarHashMap );
+ }
+
+ std::vector< ToolBarEntry > aSortedTbs;
+ OUString aStaticCmdPart( STATIC_CMD_PART );
+
+ Sequence< Sequence< css::beans::PropertyValue > > aSeqFrameToolBars = getLayoutManagerToolbars( xLayoutManager );
+ fillHashMap( aSeqFrameToolBars, aToolbarHashMap );
+
+ for (auto const& toolbar : aToolbarHashMap)
+ {
+ OUString aUIName = toolbar.second;
+ bool bHideFromMenu( false );
+ bool bContextSensitive( false );
+ if ( aUIName.isEmpty() &&
+ m_xPersistentWindowState.is() )
+ {
+ bool bVisible( false );
+
+ try
+ {
+ Sequence< PropertyValue > aWindowState;
+ Any a( m_xPersistentWindowState->getByName( toolbar.first ));
+
+ if ( a >>= aWindowState )
+ {
+ for ( PropertyValue const & prop : std::as_const(aWindowState) )
+ {
+ if ( prop.Name == WINDOWSTATE_PROPERTY_UINAME )
+ prop.Value >>= aUIName;
+ else if ( prop.Name == WINDOWSTATE_PROPERTY_HIDEFROMENU )
+ prop.Value >>= bHideFromMenu;
+ else if ( prop.Name == WINDOWSTATE_PROPERTY_CONTEXT )
+ prop.Value >>= bContextSensitive;
+ else if ( prop.Name == WINDOWSTATE_PROPERTY_VISIBLE )
+ prop.Value >>= bVisible;
+ }
+ }
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ // Check if we have to enable/disable "Reset" menu item
+ if ( bContextSensitive && !bVisible )
+ m_bResetActive = true;
+
+ }
+
+ if ( !aUIName.isEmpty() && !bHideFromMenu )
+ {
+ ToolBarEntry aTbEntry;
+ aTbEntry.aUIName = aUIName;
+ aTbEntry.aCommand = toolbar.first;
+ aTbEntry.bVisible = xLayoutManager->isElementVisible( toolbar.first );
+ aTbEntry.pCollatorWrapper = m_aIntlWrapper.getCaseCollator();
+ aSortedTbs.push_back( aTbEntry );
+ }
+ }
+
+ // sort toolbars
+ std::sort( aSortedTbs.begin(), aSortedTbs.end(), CompareToolBarEntry );
+
+ sal_Int16 nIndex( 1 );
+ const sal_uInt32 nCount = aSortedTbs.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ sal_uInt16 nItemCount = m_xPopupMenu->getItemCount();
+ m_xPopupMenu->insertItem( nIndex, aSortedTbs[i].aUIName, css::awt::MenuItemStyle::CHECKABLE, nItemCount );
+ if ( aSortedTbs[i].bVisible )
+ m_xPopupMenu->checkItem( nIndex, true );
+
+ OUStringBuffer aStrBuf( aStaticCmdPart );
+
+ sal_Int32 n = aSortedTbs[i].aCommand.lastIndexOf( '/' );
+ if (( n > 0 ) && (( n+1 ) < aSortedTbs[i].aCommand.getLength() ))
+ aStrBuf.append( aSortedTbs[i].aCommand.subView(n+1) );
+
+ OUString aCmd( aStrBuf.makeStringAndClear() );
+
+ // Store complete uno-command so it can also be dispatched. This is necessary to support
+ // the test tool!
+ rPopupMenu->setCommand( nIndex, aCmd );
+ ++nIndex;
+ }
+
+ // Create commands for non-toolbars
+
+ bool bAddCommand( true );
+ SvtCommandOptions aCmdOptions;
+
+ if ( aCmdOptions.HasEntries( SvtCommandOptions::CMDOPTION_DISABLED ))
+ {
+ if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED,
+ "ConfigureDialog"))
+ bAddCommand = false;
+ }
+
+ if ( bAddCommand )
+ {
+ // Create command for configure
+ if ( m_xPopupMenu->getItemCount() > 0 )
+ {
+ sal_uInt16 nItemCount = m_xPopupMenu->getItemCount();
+ m_xPopupMenu->insertSeparator( nItemCount+1 );
+ }
+
+ addCommand( m_xPopupMenu, ".uno:ConfigureDialog", "" );
+ }
+
+ // Add separator if no configure has been added
+ if ( !bAddCommand )
+ {
+ // Create command for configure
+ if ( m_xPopupMenu->getItemCount() > 0 )
+ {
+ sal_uInt16 nItemCount = m_xPopupMenu->getItemCount();
+ m_xPopupMenu->insertSeparator( nItemCount+1 );
+ }
+ }
+
+ OUString aLabelStr(FwkResId(STR_RESTORE_TOOLBARS));
+ addCommand( m_xPopupMenu, CMD_RESTOREVISIBILITY, aLabelStr );
+ aLabelStr = FwkResId(STR_LOCK_TOOLBARS);
+ addCommand( m_xPopupMenu, CMD_LOCKTOOLBARS, aLabelStr );
+}
+
+// XEventListener
+void SAL_CALL ToolbarsMenuController::disposing( const EventObject& )
+{
+ Reference< css::awt::XMenuListener > xHolder(this);
+
+ osl::MutexGuard aLock( m_aMutex );
+ m_xFrame.clear();
+ m_xDispatch.clear();
+ m_xDocCfgMgr.clear();
+ m_xModuleCfgMgr.clear();
+ m_xContext.clear();
+
+ if ( m_xPopupMenu.is() )
+ m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(this) );
+ m_xPopupMenu.clear();
+}
+
+// XStatusListener
+void SAL_CALL ToolbarsMenuController::statusChanged( const FeatureStateEvent& Event )
+{
+ OUString aFeatureURL( Event.FeatureURL.Complete );
+
+ // All other status events will be processed here
+ osl::ClearableMutexGuard aLock( m_aMutex );
+ Reference< css::awt::XPopupMenu > xPopupMenu( m_xPopupMenu );
+ aLock.clear();
+
+ if ( !xPopupMenu.is() )
+ return;
+
+ SolarMutexGuard aGuard;
+
+ bool bSetCheckmark = false;
+ bool bCheckmark = false;
+ for (sal_Int16 i = 0, nCount = xPopupMenu->getItemCount(); i < nCount; ++i)
+ {
+ sal_Int16 nId = xPopupMenu->getItemId(i);
+ if ( nId == 0 )
+ continue;
+
+ OUString aCmd = xPopupMenu->getCommand(nId);
+ if ( aCmd == aFeatureURL )
+ {
+ // Enable/disable item
+ xPopupMenu->enableItem(nId, Event.IsEnabled);
+
+ // Checkmark
+ if ( Event.State >>= bCheckmark )
+ bSetCheckmark = true;
+
+ if ( bSetCheckmark )
+ xPopupMenu->checkItem(nId, bCheckmark);
+ else
+ {
+ OUString aItemText;
+
+ if ( Event.State >>= aItemText )
+ xPopupMenu->setItemText(nId, aItemText);
+ }
+ }
+ }
+}
+
+// XMenuListener
+void SAL_CALL ToolbarsMenuController::itemSelected( const css::awt::MenuEvent& rEvent )
+{
+ Reference< css::awt::XPopupMenu > xPopupMenu;
+ Reference< XComponentContext > xContext;
+ Reference< XURLTransformer > xURLTransformer;
+ Reference< XFrame > xFrame;
+ Reference< XNameAccess > xPersistentWindowState;
+
+ {
+ osl::MutexGuard aLock(m_aMutex);
+ xPopupMenu = m_xPopupMenu;
+ xContext = m_xContext;
+ xURLTransformer = m_xURLTransformer;
+ xFrame = m_xFrame;
+ xPersistentWindowState = m_xPersistentWindowState;
+ }
+
+ if ( !xPopupMenu.is() )
+ return;
+
+ SolarMutexGuard aSolarMutexGuard;
+
+ OUString aCmd(xPopupMenu->getCommand(rEvent.MenuId));
+ if ( aCmd.startsWith( STATIC_INTERNAL_CMD_PART ) )
+ {
+ // Command to restore the visibility of all context sensitive toolbars
+ Reference< XNameReplace > xNameReplace( xPersistentWindowState, UNO_QUERY );
+ if ( xPersistentWindowState.is() && xNameReplace.is() )
+ {
+ try
+ {
+ Sequence< OUString > aElementNames = xPersistentWindowState->getElementNames();
+ sal_Int32 nCount = aElementNames.getLength();
+ bool bRefreshToolbars( false );
+
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ try
+ {
+ OUString aElementName = aElementNames[i];
+ Sequence< PropertyValue > aWindowState;
+
+ if ( xPersistentWindowState->getByName( aElementName ) >>= aWindowState )
+ {
+ bool bVisible( false );
+ bool bContextSensitive( false );
+ sal_Int32 nVisibleIndex( -1 );
+ for ( sal_Int32 j = 0; j < aWindowState.getLength(); j++ )
+ {
+ if ( aWindowState[j].Name == WINDOWSTATE_PROPERTY_VISIBLE )
+ {
+ aWindowState[j].Value >>= bVisible;
+ nVisibleIndex = j;
+ }
+ else if ( aWindowState[j].Name == WINDOWSTATE_PROPERTY_CONTEXT )
+ aWindowState[j].Value >>= bContextSensitive;
+ }
+
+ if ( !bVisible && bContextSensitive && nVisibleIndex >= 0 )
+ {
+ // Default is: Every context sensitive toolbar is visible
+ aWindowState.getArray()[nVisibleIndex].Value <<= true;
+ xNameReplace->replaceByName( aElementName, Any( aWindowState ));
+ bRefreshToolbars = true;
+ }
+ }
+ }
+ catch ( const NoSuchElementException& )
+ {
+ }
+ }
+
+ if ( bRefreshToolbars )
+ {
+ Reference< XLayoutManager > xLayoutManager( getLayoutManagerFromFrame( xFrame ));
+ if ( xLayoutManager.is() )
+ {
+ Reference< XPropertySet > xPropSet( xLayoutManager, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ xPropSet->setPropertyValue("RefreshContextToolbarVisibility", Any( true ));
+ }
+ catch ( const RuntimeException& )
+ {
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+ }
+ RefreshToolbars( xFrame );
+ }
+ }
+ catch ( const RuntimeException& )
+ {
+ throw;
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+ }
+ else if ( aCmd.indexOf( STATIC_CMD_PART ) < 0 )
+ {
+ URL aTargetURL;
+ Sequence<PropertyValue> aArgs;
+
+ aTargetURL.Complete = aCmd;
+ xURLTransformer->parseStrict( aTargetURL );
+ Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
+ if ( xDispatchProvider.is() )
+ {
+ Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch(
+ aTargetURL, OUString(), 0 );
+
+ ExecuteInfo* pExecuteInfo = new ExecuteInfo;
+ pExecuteInfo->xDispatch = xDispatch;
+ pExecuteInfo->aTargetURL = aTargetURL;
+ pExecuteInfo->aArgs = aArgs;
+ Application::PostUserEvent( LINK(nullptr, ToolbarsMenuController, ExecuteHdl_Impl), pExecuteInfo );
+ }
+ }
+ else
+ {
+ Reference< XLayoutManager > xLayoutManager( getLayoutManagerFromFrame( xFrame ));
+ if ( xLayoutManager.is() )
+ {
+ // Extract toolbar name from the combined uno-command.
+ sal_Int32 nIndex = aCmd.indexOf( '=' );
+ if (( nIndex > 0 ) && (( nIndex+1 ) < aCmd.getLength() ))
+ {
+ OUString aToolBarResName = OUString::Concat("private:resource/toolbar/") + aCmd.subView(nIndex+1);
+
+ const bool bShow(!xPopupMenu->isItemChecked(rEvent.MenuId));
+ if ( bShow )
+ {
+ xLayoutManager->createElement( aToolBarResName );
+ xLayoutManager->showElement( aToolBarResName );
+ }
+ else
+ {
+ // closing means:
+ // hide and destroy element
+ xLayoutManager->hideElement( aToolBarResName );
+ xLayoutManager->destroyElement( aToolBarResName );
+ }
+ }
+ }
+ }
+}
+
+void SAL_CALL ToolbarsMenuController::itemActivated( const css::awt::MenuEvent& )
+{
+ std::vector< OUString > aCmdVector;
+ Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
+ Reference< XURLTransformer > xURLTransformer( m_xURLTransformer );
+ {
+ osl::MutexGuard aLock( m_aMutex );
+ fillPopupMenu( m_xPopupMenu );
+ aCmdVector = m_aCommandVector;
+ }
+
+ // Update status for all commands inside our toolbars popup menu
+ const sal_uInt32 nCount = aCmdVector.size();
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ bool bInternal = aCmdVector[i].startsWith( STATIC_INTERNAL_CMD_PART );
+
+ if ( !bInternal )
+ {
+ URL aTargetURL;
+ aTargetURL.Complete = aCmdVector[i];
+ xURLTransformer->parseStrict( aTargetURL );
+ Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+ if ( xDispatch.is() )
+ {
+ xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
+ xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
+ }
+ }
+ else if ( aCmdVector[i] == CMD_RESTOREVISIBILITY )
+ {
+ // Special code to determine the enable/disable state of this command
+ FeatureStateEvent aFeatureStateEvent;
+ aFeatureStateEvent.FeatureURL.Complete = aCmdVector[i];
+ aFeatureStateEvent.IsEnabled = m_bResetActive; // is context sensitive toolbar non visible
+ statusChanged( aFeatureStateEvent );
+ }
+ }
+}
+
+// XPopupMenuController
+void SAL_CALL ToolbarsMenuController::setPopupMenu( const Reference< css::awt::XPopupMenu >& xPopupMenu )
+{
+ osl::MutexGuard aLock( m_aMutex );
+
+ throwIfDisposed();
+
+ if ( m_xFrame.is() && !m_xPopupMenu.is() )
+ {
+ // Create popup menu on demand
+ SolarMutexGuard aSolarMutexGuard;
+
+ m_xPopupMenu = xPopupMenu;
+ m_xPopupMenu->addMenuListener( Reference< css::awt::XMenuListener >(this) );
+ fillPopupMenu( m_xPopupMenu );
+ }
+}
+
+// XInitialization
+void SAL_CALL ToolbarsMenuController::initialize( const Sequence< Any >& aArguments )
+{
+ osl::MutexGuard aLock( m_aMutex );
+ bool bInitialized( m_bInitialized );
+ if ( bInitialized )
+ return;
+
+ svt::PopupMenuControllerBase::initialize(aArguments);
+
+ if ( !m_bInitialized )
+ return;
+
+ Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext );
+ Reference< XNameAccess > xPersistentWindowStateSupplier = css::ui::theWindowStateConfiguration::get( m_xContext );
+
+ // Retrieve persistent window state reference for our module
+ try
+ {
+ OUString aModuleIdentifier = xModuleManager->identify( m_xFrame );
+ xPersistentWindowStateSupplier->getByName( aModuleIdentifier ) >>= m_xPersistentWindowState;
+
+ Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgSupplier =
+ theModuleUIConfigurationManagerSupplier::get( m_xContext );
+ m_xModuleCfgMgr = xModuleCfgSupplier->getUIConfigurationManager( aModuleIdentifier );
+
+ Reference< XController > xController = m_xFrame->getController();
+ Reference< XModel > xModel;
+ if ( xController.is() )
+ xModel = xController->getModel();
+ if ( xModel.is() )
+ {
+ Reference< XUIConfigurationManagerSupplier > xUIConfigurationManagerSupplier( xModel, UNO_QUERY );
+ if ( xUIConfigurationManagerSupplier.is() )
+ m_xDocCfgMgr = xUIConfigurationManagerSupplier->getUIConfigurationManager();
+ }
+ }
+ catch ( const Exception& )
+ {
+ }
+}
+
+IMPL_STATIC_LINK( ToolbarsMenuController, ExecuteHdl_Impl, void*, p, void )
+{
+ ExecuteInfo* pExecuteInfo = static_cast<ExecuteInfo*>(p);
+ try
+ {
+ // Asynchronous execution as this can lead to our own destruction!
+ // Framework can recycle our current frame and the layout manager disposes all user interface
+ // elements if a component gets detached from its frame!
+ if ( pExecuteInfo->xDispatch.is() )
+ {
+ pExecuteInfo->xDispatch->dispatch( pExecuteInfo->aTargetURL, pExecuteInfo->aArgs );
+ }
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ delete pExecuteInfo;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_ToolbarsMenuController_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::ToolbarsMenuController(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/toolbarwrapper.cxx b/framework/source/uielement/toolbarwrapper.cxx
new file mode 100644
index 000000000..9bf9a4bd7
--- /dev/null
+++ b/framework/source/uielement/toolbarwrapper.cxx
@@ -0,0 +1,328 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uielement/toolbarwrapper.hxx>
+#include <uielement/toolbarmanager.hxx>
+
+#include <com/sun/star/ui/UIElementType.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <cppuhelper/queryinterface.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/weldutils.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+using namespace com::sun::star::awt;
+using namespace ::com::sun::star::ui;
+
+namespace framework
+{
+
+ToolBarWrapper::ToolBarWrapper( const Reference< XComponentContext >& rxContext ) :
+ UIConfigElementWrapperBase( UIElementType::TOOLBAR ),
+ m_xContext( rxContext )
+{
+}
+
+ToolBarWrapper::~ToolBarWrapper()
+{
+ m_xWeldedToolbar.reset(nullptr);
+ m_xTopLevel.reset(nullptr);
+ m_xBuilder.reset(nullptr);
+}
+
+// XInterface
+void SAL_CALL ToolBarWrapper::acquire() noexcept
+{
+ UIConfigElementWrapperBase::acquire();
+}
+
+void SAL_CALL ToolBarWrapper::release() noexcept
+{
+ UIConfigElementWrapperBase::release();
+}
+
+uno::Any SAL_CALL ToolBarWrapper::queryInterface( const uno::Type & rType )
+{
+ Any a = ::cppu::queryInterface(
+ rType ,
+ static_cast< css::ui::XUIFunctionListener* >(this) );
+
+ if( a.hasValue() )
+ return a;
+
+ return UIConfigElementWrapperBase::queryInterface( rType );
+}
+
+// XComponent
+void SAL_CALL ToolBarWrapper::dispose()
+{
+ Reference< XComponent > xThis(this);
+
+ {
+ SolarMutexGuard g;
+ if ( m_bDisposed )
+ return;
+ }
+
+ css::lang::EventObject aEvent( xThis );
+ m_aListenerContainer.disposeAndClear( aEvent );
+
+ SolarMutexGuard g;
+
+ if ( m_xToolBarManager.is() )
+ m_xToolBarManager->dispose();
+ m_xToolBarManager.clear();
+ m_xConfigSource.clear();
+ m_xConfigData.clear();
+
+ m_bDisposed = true;
+}
+
+// XInitialization
+void SAL_CALL ToolBarWrapper::initialize( const Sequence< Any >& aArguments )
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( m_bInitialized )
+ return;
+
+ UIConfigElementWrapperBase::initialize( aArguments );
+
+ bool bPopupMode( false );
+ Reference< XWindow > xParentWindow;
+ for ( Any const & arg : aArguments )
+ {
+ PropertyValue aPropValue;
+ if ( arg >>= aPropValue )
+ {
+ if ( aPropValue.Name == "PopupMode" )
+ aPropValue.Value >>= bPopupMode;
+ else if ( aPropValue.Name == "ParentWindow" )
+ xParentWindow.set( aPropValue.Value, UNO_QUERY );
+ }
+ }
+
+ Reference< XFrame > xFrame( m_xWeakFrame );
+ if ( !(xFrame.is() && m_xConfigSource.is()) )
+ return;
+
+ // Create VCL based toolbar which will be filled with settings data
+ VclPtr<ToolBox> pToolBar;
+ rtl::Reference<ToolBarManager> pToolBarManager;
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ if ( !xParentWindow.is() )
+ xParentWindow.set( xFrame->getContainerWindow() );
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xParentWindow );
+ if ( pWindow )
+ {
+ sal_uLong nStyles = WB_BORDER | WB_SCROLL | WB_MOVEABLE | WB_3DLOOK | WB_DOCKABLE | WB_SIZEABLE | WB_CLOSEABLE;
+
+ pToolBar = VclPtr<ToolBox>::Create( pWindow, nStyles );
+ pToolBar->SetLineSpacing(true);
+ pToolBarManager = new ToolBarManager( m_xContext, xFrame, m_aResourceURL, pToolBar );
+ m_xToolBarManager = pToolBarManager;
+ pToolBar->WillUsePopupMode( bPopupMode );
+ }
+ else if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xParentWindow.get()))
+ {
+ m_xBuilder = Application::CreateBuilder(pTunnel->getWidget(), "svt/ui/managedtoolbar.ui");
+ m_xTopLevel = m_xBuilder->weld_container("toolbarcontainer");
+ m_xWeldedToolbar = m_xBuilder->weld_toolbar("managedtoolbar");
+ if ( m_xWeldedToolbar )
+ {
+ pToolBarManager = new ToolBarManager( m_xContext, xFrame, m_aResourceURL, m_xWeldedToolbar.get(), m_xBuilder.get() );
+ m_xToolBarManager = pToolBarManager;
+ }
+ }
+ }
+
+ try
+ {
+ m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false );
+ if ( m_xConfigData.is() && (pToolBar || m_xWeldedToolbar) && pToolBarManager )
+ {
+ // Fill toolbar with container contents
+ pToolBarManager->FillToolbar( m_xConfigData );
+ if (pToolBar)
+ {
+ pToolBar->EnableCustomize();
+ ::Size aActSize( pToolBar->GetSizePixel() );
+ ::Size aSize( pToolBar->CalcWindowSizePixel() );
+ aSize.setWidth( aActSize.Width() );
+ pToolBar->SetOutputSizePixel( aSize );
+ }
+ }
+ }
+ catch ( const NoSuchElementException& )
+ {
+ // No settings in our configuration manager. This means we are
+ // a transient toolbar which has no persistent settings.
+ m_bPersistent = false;
+ if ( pToolBar && pToolBarManager )
+ {
+ pToolBar->EnableCustomize();
+ ::Size aActSize( pToolBar->GetSizePixel() );
+ ::Size aSize( pToolBar->CalcWindowSizePixel() );
+ aSize.setWidth( aActSize.Width() );
+ pToolBar->SetOutputSizePixel( aSize );
+ }
+ }
+}
+
+// XEventListener
+void SAL_CALL ToolBarWrapper::disposing( const css::lang::EventObject& )
+{
+ // nothing todo
+}
+
+// XUpdatable
+void SAL_CALL ToolBarWrapper::update()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() );
+ if ( pToolBarManager )
+ pToolBarManager->CheckAndUpdateImages();
+}
+
+// XUIElementSettings
+void SAL_CALL ToolBarWrapper::updateSettings()
+{
+ SolarMutexGuard g;
+
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ if ( !m_xToolBarManager.is() )
+ return;
+
+ if ( m_xConfigSource.is() && m_bPersistent )
+ {
+ try
+ {
+ ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() );
+
+ m_xConfigData = m_xConfigSource->getSettings( m_aResourceURL, false );
+ if ( m_xConfigData.is() )
+ pToolBarManager->FillToolbar( m_xConfigData );
+ }
+ catch ( const NoSuchElementException& )
+ {
+ }
+ }
+ else if ( !m_bPersistent )
+ {
+ // Transient toolbar: do nothing
+ }
+}
+
+void ToolBarWrapper::impl_fillNewData()
+{
+ // Transient toolbar => Fill toolbar with new data
+ ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() );
+ if ( pToolBarManager )
+ pToolBarManager->FillToolbar( m_xConfigData );
+}
+
+// XUIElement interface
+Reference< XInterface > SAL_CALL ToolBarWrapper::getRealInterface( )
+{
+ SolarMutexGuard g;
+
+ if ( m_xToolBarManager.is() )
+ {
+ ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() );
+ if ( pToolBarManager )
+ {
+ vcl::Window* pWindow = pToolBarManager->GetToolBar();
+ return Reference< XInterface >( VCLUnoHelper::GetInterface( pWindow ), UNO_QUERY );
+ }
+ }
+
+ return Reference< XInterface >();
+}
+
+//XUIFunctionExecute
+void SAL_CALL ToolBarWrapper::functionExecute(
+ const OUString& aUIElementName,
+ const OUString& aCommand )
+{
+ SolarMutexGuard g;
+
+ if ( m_xToolBarManager.is() )
+ {
+ ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() );
+ if ( pToolBarManager )
+ pToolBarManager->notifyRegisteredControllers( aUIElementName, aCommand );
+ }
+}
+
+void SAL_CALL ToolBarWrapper::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& aValue )
+{
+ SolarMutexResettableGuard aLock;
+ bool bNoClose( m_bNoClose );
+ aLock.clear();
+
+ UIConfigElementWrapperBase::setFastPropertyValue_NoBroadcast( nHandle, aValue );
+
+ aLock.reset();
+
+ bool bNewNoClose( m_bNoClose );
+ if ( !(m_xToolBarManager.is() && !m_bDisposed && ( bNewNoClose != bNoClose )))
+ return;
+
+ ToolBarManager* pToolBarManager = static_cast< ToolBarManager *>( m_xToolBarManager.get() );
+ if ( !pToolBarManager )
+ return;
+
+ ToolBox* pToolBox = pToolBarManager->GetToolBar();
+ if ( !pToolBox )
+ return;
+
+ if ( bNewNoClose )
+ {
+ pToolBox->SetStyle( pToolBox->GetStyle() & ~WB_CLOSEABLE );
+ pToolBox->SetFloatStyle( pToolBox->GetFloatStyle() & ~WB_CLOSEABLE );
+ }
+ else
+ {
+ pToolBox->SetStyle( pToolBox->GetStyle() | WB_CLOSEABLE );
+ pToolBox->SetFloatStyle( pToolBox->GetFloatStyle() | WB_CLOSEABLE );
+ }
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uielement/uicommanddescription.cxx b/framework/source/uielement/uicommanddescription.cxx
new file mode 100644
index 000000000..8054cf4bf
--- /dev/null
+++ b/framework/source/uielement/uicommanddescription.cxx
@@ -0,0 +1,725 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <uielement/uicommanddescription.hxx>
+
+#include <properties.h>
+
+#include <helper/mischelper.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/syslocale.hxx>
+
+#include <vcl/mnemonic.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/string.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::configuration;
+using namespace com::sun::star::container;
+using namespace ::com::sun::star::frame;
+
+// Namespace
+
+const char CONFIGURATION_ROOT_ACCESS[] = "/org.openoffice.Office.UI.";
+
+// Special resource URLs to retrieve additional information
+constexpr OUStringLiteral PRIVATE_RESOURCE_URL = u"private:";
+
+const sal_Int32 COMMAND_PROPERTY_IMAGE = 1;
+const sal_Int32 COMMAND_PROPERTY_ROTATE = 2;
+const sal_Int32 COMMAND_PROPERTY_MIRROR = 4;
+
+namespace framework
+{
+
+// Configuration access class for PopupMenuControllerFactory implementation
+
+namespace {
+
+class ConfigurationAccess_UICommand : // Order is necessary for right initialization!
+ public ::cppu::WeakImplHelper<XNameAccess,XContainerListener>
+{
+ osl::Mutex m_aMutex;
+ public:
+ ConfigurationAccess_UICommand( std::u16string_view aModuleName, const Reference< XNameAccess >& xGenericUICommands, const Reference< XComponentContext >& rxContext );
+ virtual ~ConfigurationAccess_UICommand() override;
+
+ // XNameAccess
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override;
+
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // container.XContainerListener
+ virtual void SAL_CALL elementInserted( const ContainerEvent& aEvent ) override;
+ virtual void SAL_CALL elementRemoved ( const ContainerEvent& aEvent ) override;
+ virtual void SAL_CALL elementReplaced( const ContainerEvent& aEvent ) override;
+
+ // lang.XEventListener
+ virtual void SAL_CALL disposing( const EventObject& aEvent ) override;
+
+ protected:
+ css::uno::Any getByNameImpl( const OUString& aName );
+
+ struct CmdToInfoMap
+ {
+ CmdToInfoMap() : bPopup( false ),
+ bCommandNameCreated( false ),
+ bIsExperimental( false ),
+ nProperties( 0 ) {}
+
+ OUString aLabel;
+ OUString aContextLabel;
+ OUString aCommandName;
+ OUString aPopupLabel;
+ OUString aTooltipLabel;
+ OUString aTargetURL;
+ bool bPopup : 1,
+ bCommandNameCreated : 1;
+ bool bIsExperimental;
+ sal_Int32 nProperties;
+ };
+
+ Any getSequenceFromCache( const OUString& aCommandURL );
+ Any getInfoFromCommand( const OUString& rCommandURL );
+ void fillInfoFromResult( CmdToInfoMap& rCmdInfo, const OUString& aLabel );
+ Sequence< OUString > getAllCommands();
+ void fillCache();
+ void addGenericInfoToCache();
+ void impl_fill(const Reference< XNameAccess >& _xConfigAccess,bool _bPopup,
+ std::vector< OUString >& aImageCommandVector,
+ std::vector< OUString >& aImageRotateVector,
+ std::vector< OUString >& aImageMirrorVector);
+
+ private:
+ typedef std::unordered_map< OUString,
+ CmdToInfoMap > CommandToInfoCache;
+
+ void initializeConfigAccess();
+
+ OUString m_aConfigCmdAccess;
+ OUString m_aConfigPopupAccess;
+ OUString m_aPropProperties;
+ Reference< XNameAccess > m_xGenericUICommands;
+ Reference< XMultiServiceFactory > m_xConfigProvider;
+ Reference< XNameAccess > m_xConfigAccess;
+ Reference< XContainerListener > m_xConfigListener;
+ Reference< XNameAccess > m_xConfigAccessPopups;
+ Reference< XContainerListener > m_xConfigAccessListener;
+ Sequence< OUString > m_aCommandImageList;
+ Sequence< OUString > m_aCommandRotateImageList;
+ Sequence< OUString > m_aCommandMirrorImageList;
+ CommandToInfoCache m_aCmdInfoCache;
+ bool m_bConfigAccessInitialized;
+ bool m_bCacheFilled;
+ bool m_bGenericDataRetrieved;
+};
+
+}
+
+
+// XInterface, XTypeProvider
+
+ConfigurationAccess_UICommand::ConfigurationAccess_UICommand( std::u16string_view aModuleName, const Reference< XNameAccess >& rGenericUICommands, const Reference< XComponentContext>& rxContext ) :
+ // Create configuration hierarchical access name
+ m_aConfigCmdAccess(
+ OUString::Concat(CONFIGURATION_ROOT_ACCESS) + aModuleName + "/UserInterface/Commands"),
+ m_aConfigPopupAccess(
+ OUString::Concat(CONFIGURATION_ROOT_ACCESS) + aModuleName + "/UserInterface/Popups"),
+ m_aPropProperties( "Properties" ),
+ m_xGenericUICommands( rGenericUICommands ),
+ m_xConfigProvider( theDefaultProvider::get( rxContext ) ),
+ m_bConfigAccessInitialized( false ),
+ m_bCacheFilled( false ),
+ m_bGenericDataRetrieved( false )
+{
+}
+
+ConfigurationAccess_UICommand::~ConfigurationAccess_UICommand()
+{
+ // SAFE
+ osl::MutexGuard g(m_aMutex);
+ Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY );
+ if ( xContainer.is() )
+ xContainer->removeContainerListener(m_xConfigListener);
+ xContainer.set( m_xConfigAccessPopups, UNO_QUERY );
+ if ( xContainer.is() )
+ xContainer->removeContainerListener(m_xConfigAccessListener);
+}
+
+// XNameAccess
+Any ConfigurationAccess_UICommand::getByNameImpl( const OUString& rCommandURL )
+{
+ osl::MutexGuard g(m_aMutex);
+ if ( !m_bConfigAccessInitialized )
+ {
+ initializeConfigAccess();
+ m_bConfigAccessInitialized = true;
+ fillCache();
+ }
+
+ if ( rCommandURL.startsWith( PRIVATE_RESOURCE_URL ) )
+ {
+ // special keys to retrieve information about a set of commands
+ // SAFE
+ addGenericInfoToCache();
+
+ if ( rCommandURL.equalsIgnoreAsciiCase( UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDIMAGELIST ))
+ return Any( m_aCommandImageList );
+ else if ( rCommandURL.equalsIgnoreAsciiCase( UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDROTATEIMAGELIST ))
+ return Any( m_aCommandRotateImageList );
+ else if ( rCommandURL.equalsIgnoreAsciiCase( UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDMIRRORIMAGELIST ))
+ return Any( m_aCommandMirrorImageList );
+ else
+ return Any();
+ }
+ else
+ {
+ // SAFE
+ return getInfoFromCommand( rCommandURL );
+ }
+}
+
+Any SAL_CALL ConfigurationAccess_UICommand::getByName( const OUString& rCommandURL )
+{
+ Any aRet( getByNameImpl( rCommandURL ) );
+ if( !aRet.hasValue() )
+ throw NoSuchElementException();
+
+ return aRet;
+}
+
+Sequence< OUString > SAL_CALL ConfigurationAccess_UICommand::getElementNames()
+{
+ return getAllCommands();
+}
+
+sal_Bool SAL_CALL ConfigurationAccess_UICommand::hasByName( const OUString& rCommandURL )
+{
+ return getByNameImpl( rCommandURL ).hasValue();
+}
+
+// XElementAccess
+Type SAL_CALL ConfigurationAccess_UICommand::getElementType()
+{
+ return cppu::UnoType<Sequence< PropertyValue >>::get();
+}
+
+sal_Bool SAL_CALL ConfigurationAccess_UICommand::hasElements()
+{
+ // There must are global commands!
+ return true;
+}
+
+void ConfigurationAccess_UICommand::fillInfoFromResult( CmdToInfoMap& rCmdInfo, const OUString& aLabel )
+{
+ OUString aStr(aLabel.replaceAll("%PRODUCTNAME", utl::ConfigManager::getProductName()));
+ rCmdInfo.aLabel = aStr;
+ aStr = comphelper::string::stripEnd(aStr, '.'); // Remove "..." from string
+ rCmdInfo.aCommandName = MnemonicGenerator::EraseAllMnemonicChars(aStr);
+ rCmdInfo.bCommandNameCreated = true;
+}
+
+Any ConfigurationAccess_UICommand::getSequenceFromCache( const OUString& aCommandURL )
+{
+ CommandToInfoCache::iterator pIter = m_aCmdInfoCache.find( aCommandURL );
+ if ( pIter != m_aCmdInfoCache.end() )
+ {
+ if ( !pIter->second.bCommandNameCreated )
+ fillInfoFromResult( pIter->second, pIter->second.aLabel );
+
+ static constexpr OUStringLiteral sLabel = u"Label";
+ static constexpr OUStringLiteral sName = u"Name";
+ static constexpr OUStringLiteral sPopup = u"Popup";
+ static constexpr OUStringLiteral sPopupLabel = u"PopupLabel";
+ static constexpr OUStringLiteral sTooltipLabel = u"TooltipLabel";
+ static constexpr OUStringLiteral sTargetURL = u"TargetURL";
+ static constexpr OUStringLiteral sIsExperimental = u"IsExperimental";
+ Sequence< PropertyValue > aPropSeq{
+ comphelper::makePropertyValue(sLabel, !pIter->second.aContextLabel.isEmpty()
+ ? Any(pIter->second.aContextLabel)
+ : Any(pIter->second.aLabel)),
+ comphelper::makePropertyValue(sName, pIter->second.aCommandName),
+ comphelper::makePropertyValue(sPopup, pIter->second.bPopup),
+ comphelper::makePropertyValue(m_aPropProperties, pIter->second.nProperties),
+ comphelper::makePropertyValue(sPopupLabel, pIter->second.aPopupLabel),
+ comphelper::makePropertyValue(sTooltipLabel, pIter->second.aTooltipLabel),
+ comphelper::makePropertyValue(sTargetURL, pIter->second.aTargetURL),
+ comphelper::makePropertyValue(sIsExperimental, pIter->second.bIsExperimental)
+ };
+ return Any( aPropSeq );
+ }
+
+ return Any();
+}
+void ConfigurationAccess_UICommand::impl_fill(const Reference< XNameAccess >& _xConfigAccess,bool _bPopup,
+ std::vector< OUString >& aImageCommandVector,
+ std::vector< OUString >& aImageRotateVector,
+ std::vector< OUString >& aImageMirrorVector)
+{
+ if ( !_xConfigAccess.is() )
+ return;
+
+ Sequence< OUString> aNameSeq = _xConfigAccess->getElementNames();
+ const sal_Int32 nCount = aNameSeq.getLength();
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ try
+ {
+ Reference< XNameAccess > xNameAccess(_xConfigAccess->getByName( aNameSeq[i] ),UNO_QUERY);
+ if ( xNameAccess.is() )
+ {
+ CmdToInfoMap aCmdToInfo;
+
+ aCmdToInfo.bPopup = _bPopup;
+ xNameAccess->getByName( "Label" ) >>= aCmdToInfo.aLabel;
+ xNameAccess->getByName( "ContextLabel" ) >>= aCmdToInfo.aContextLabel;
+ xNameAccess->getByName( "PopupLabel" ) >>= aCmdToInfo.aPopupLabel;
+ xNameAccess->getByName( "TooltipLabel" ) >>= aCmdToInfo.aTooltipLabel;
+ xNameAccess->getByName( "TargetURL" ) >>= aCmdToInfo.aTargetURL;
+ xNameAccess->getByName( "IsExperimental" ) >>= aCmdToInfo.bIsExperimental;
+ xNameAccess->getByName( m_aPropProperties ) >>= aCmdToInfo.nProperties;
+
+ m_aCmdInfoCache.emplace( aNameSeq[i], aCmdToInfo );
+
+ if ( aCmdToInfo.nProperties & COMMAND_PROPERTY_IMAGE )
+ aImageCommandVector.push_back( aNameSeq[i] );
+ if ( aCmdToInfo.nProperties & COMMAND_PROPERTY_ROTATE )
+ aImageRotateVector.push_back( aNameSeq[i] );
+ if ( aCmdToInfo.nProperties & COMMAND_PROPERTY_MIRROR )
+ aImageMirrorVector.push_back( aNameSeq[i] );
+ }
+ }
+ catch (const css::lang::WrappedTargetException&)
+ {
+ }
+ catch (const css::container::NoSuchElementException&)
+ {
+ }
+ }
+}
+void ConfigurationAccess_UICommand::fillCache()
+{
+
+ if ( m_bCacheFilled )
+ return;
+
+ std::vector< OUString > aImageCommandVector;
+ std::vector< OUString > aImageRotateVector;
+ std::vector< OUString > aImageMirrorVector;
+
+ impl_fill(m_xConfigAccess,false,aImageCommandVector,aImageRotateVector,aImageMirrorVector);
+ impl_fill(m_xConfigAccessPopups,true,aImageCommandVector,aImageRotateVector,aImageMirrorVector);
+ // Create cached sequences for fast retrieving
+ m_aCommandImageList = comphelper::containerToSequence( aImageCommandVector );
+ m_aCommandRotateImageList = comphelper::containerToSequence( aImageRotateVector );
+ m_aCommandMirrorImageList = comphelper::containerToSequence( aImageMirrorVector );
+
+ m_bCacheFilled = true;
+}
+
+void ConfigurationAccess_UICommand::addGenericInfoToCache()
+{
+ if ( !m_xGenericUICommands.is() || m_bGenericDataRetrieved )
+ return;
+
+ Sequence< OUString > aCommandNameSeq;
+ try
+ {
+ if ( m_xGenericUICommands->getByName(
+ UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDROTATEIMAGELIST ) >>= aCommandNameSeq )
+ m_aCommandRotateImageList = comphelper::concatSequences< OUString >( m_aCommandRotateImageList, aCommandNameSeq );
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ }
+
+ try
+ {
+ if ( m_xGenericUICommands->getByName(
+ UICOMMANDDESCRIPTION_NAMEACCESS_COMMANDMIRRORIMAGELIST ) >>= aCommandNameSeq )
+ m_aCommandMirrorImageList = comphelper::concatSequences< OUString >( m_aCommandMirrorImageList, aCommandNameSeq );
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ }
+
+ m_bGenericDataRetrieved = true;
+}
+
+Any ConfigurationAccess_UICommand::getInfoFromCommand( const OUString& rCommandURL )
+{
+ Any a;
+
+ try
+ {
+ a = getSequenceFromCache( rCommandURL );
+ if ( !a.hasValue() )
+ {
+ // First try to ask our global commands configuration access. It also caches maybe
+ // we find the entry in its cache first.
+ if ( m_xGenericUICommands.is() && m_xGenericUICommands->hasByName( rCommandURL ) )
+ {
+ try
+ {
+ return m_xGenericUICommands->getByName( rCommandURL );
+ }
+ catch (const css::lang::WrappedTargetException&)
+ {
+ }
+ catch (const css::container::NoSuchElementException&)
+ {
+ }
+ }
+ }
+ }
+ catch (const css::container::NoSuchElementException&)
+ {
+ }
+ catch (const css::lang::WrappedTargetException&)
+ {
+ }
+
+ return a;
+}
+
+Sequence< OUString > ConfigurationAccess_UICommand::getAllCommands()
+{
+ // SAFE
+ osl::MutexGuard g(m_aMutex);
+
+ if ( !m_bConfigAccessInitialized )
+ {
+ initializeConfigAccess();
+ m_bConfigAccessInitialized = true;
+ fillCache();
+ }
+
+ if ( m_xConfigAccess.is() )
+ {
+ try
+ {
+ Sequence< OUString > aNameSeq = m_xConfigAccess->getElementNames();
+
+ if ( m_xGenericUICommands.is() )
+ {
+ // Create concat list of supported user interface commands of the module
+ Sequence< OUString > aGenericNameSeq = m_xGenericUICommands->getElementNames();
+ sal_uInt32 nCount1 = aNameSeq.getLength();
+ sal_uInt32 nCount2 = aGenericNameSeq.getLength();
+
+ aNameSeq.realloc( nCount1 + nCount2 );
+ OUString* pNameSeq = aNameSeq.getArray();
+ const OUString* pGenericSeq = aGenericNameSeq.getConstArray();
+ for ( sal_uInt32 i = 0; i < nCount2; i++ )
+ pNameSeq[nCount1+i] = pGenericSeq[i];
+ }
+
+ return aNameSeq;
+ }
+ catch (const css::container::NoSuchElementException&)
+ {
+ }
+ catch (const css::lang::WrappedTargetException&)
+ {
+ }
+ }
+
+ return Sequence< OUString >();
+}
+
+void ConfigurationAccess_UICommand::initializeConfigAccess()
+{
+ try
+ {
+ Sequence<Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", Any(m_aConfigCmdAccess)}
+ }));
+ m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess", aArgs ),UNO_QUERY );
+ if ( m_xConfigAccess.is() )
+ {
+ // Add as container listener
+ Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY );
+ if ( xContainer.is() )
+ {
+ m_xConfigListener = new WeakContainerListener(this);
+ xContainer->addContainerListener(m_xConfigListener);
+ }
+ }
+
+ Sequence<Any> aArgs2(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", Any(m_aConfigPopupAccess)}
+ }));
+ m_xConfigAccessPopups.set( m_xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess", aArgs2 ),UNO_QUERY );
+ if ( m_xConfigAccessPopups.is() )
+ {
+ // Add as container listener
+ Reference< XContainer > xContainer( m_xConfigAccessPopups, UNO_QUERY );
+ if ( xContainer.is() )
+ {
+ m_xConfigAccessListener = new WeakContainerListener(this);
+ xContainer->addContainerListener(m_xConfigAccessListener);
+ }
+ }
+ }
+ catch (const WrappedTargetException&)
+ {
+ }
+ catch (const Exception&)
+ {
+ }
+}
+
+// container.XContainerListener
+void SAL_CALL ConfigurationAccess_UICommand::elementInserted( const ContainerEvent& )
+{
+ osl::MutexGuard g(m_aMutex);
+ m_bCacheFilled = false;
+ fillCache();
+}
+
+void SAL_CALL ConfigurationAccess_UICommand::elementRemoved( const ContainerEvent& )
+{
+ osl::MutexGuard g(m_aMutex);
+ m_bCacheFilled = false;
+ fillCache();
+}
+
+void SAL_CALL ConfigurationAccess_UICommand::elementReplaced( const ContainerEvent& )
+{
+ osl::MutexGuard g(m_aMutex);
+ m_bCacheFilled = false;
+ fillCache();
+}
+
+// lang.XEventListener
+void SAL_CALL ConfigurationAccess_UICommand::disposing( const EventObject& aEvent )
+{
+ // SAFE
+ // remove our reference to the config access
+ osl::MutexGuard g(m_aMutex);
+
+ Reference< XInterface > xIfac1( aEvent.Source, UNO_QUERY );
+ Reference< XInterface > xIfac2( m_xConfigAccess, UNO_QUERY );
+ if ( xIfac1 == xIfac2 )
+ m_xConfigAccess.clear();
+ else
+ {
+ xIfac1.set( m_xConfigAccessPopups, UNO_QUERY );
+ if ( xIfac1 == xIfac2 )
+ m_xConfigAccessPopups.clear();
+ }
+}
+
+void UICommandDescription::ensureGenericUICommandsForLanguage(const LanguageTag& rLanguage)
+{
+ auto xGenericUICommands = m_xGenericUICommands.find(rLanguage);
+ if (xGenericUICommands == m_xGenericUICommands.end())
+ {
+ Reference< XNameAccess > xEmpty;
+ m_xGenericUICommands[rLanguage] = new ConfigurationAccess_UICommand( u"GenericCommands", xEmpty, m_xContext );
+ }
+}
+
+UICommandDescription::UICommandDescription(const Reference< XComponentContext >& rxContext)
+ : m_aPrivateResourceURL(PRIVATE_RESOURCE_URL)
+ , m_xContext(rxContext)
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+
+ ensureGenericUICommandsForLanguage(rCurrentLanguage);
+
+ impl_fillElements("ooSetupFactoryCommandConfigRef");
+
+ // insert generic commands
+ auto& rMap = m_aUICommandsHashMap[rCurrentLanguage];
+ UICommandsHashMap::iterator pIter = rMap.find( "GenericCommands" );
+ if ( pIter != rMap.end() )
+ pIter->second = m_xGenericUICommands[rCurrentLanguage];
+}
+
+UICommandDescription::UICommandDescription(const Reference< XComponentContext >& rxContext, bool)
+ : m_xContext(rxContext)
+{
+}
+
+UICommandDescription::~UICommandDescription()
+{
+ std::unique_lock g(m_aMutex);
+ m_aModuleToCommandFileMap.clear();
+ m_aUICommandsHashMap.clear();
+ m_xGenericUICommands.clear();
+}
+void UICommandDescription::impl_fillElements(const char* _pName)
+{
+ m_xModuleManager.set( ModuleManager::create( m_xContext ) );
+ const Sequence< OUString > aElementNames = m_xModuleManager->getElementNames();
+
+ SvtSysLocale aSysLocale;
+
+ for ( OUString const & aModuleIdentifier : aElementNames )
+ {
+ Sequence< PropertyValue > aSeq;
+ if ( m_xModuleManager->getByName( aModuleIdentifier ) >>= aSeq )
+ {
+ OUString aCommandStr;
+ for ( PropertyValue const & prop : std::as_const(aSeq) )
+ {
+ if ( prop.Name.equalsAscii(_pName) )
+ {
+ prop.Value >>= aCommandStr;
+ break;
+ }
+ }
+
+ // Create first mapping ModuleIdentifier ==> Command File
+ m_aModuleToCommandFileMap.emplace( aModuleIdentifier, aCommandStr );
+
+ // Create second mapping Command File ==> commands instance
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ auto& rMap = m_aUICommandsHashMap[rCurrentLanguage];
+ UICommandsHashMap::iterator pIter = rMap.find( aCommandStr );
+ if ( pIter == rMap.end() )
+ rMap.emplace( aCommandStr, Reference< XNameAccess >() );
+ }
+ } // for ( sal_Int32 i = 0; i < aElementNames.(); i++ )
+}
+
+Any SAL_CALL UICommandDescription::getByName( const OUString& aName )
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ Any a;
+
+ std::unique_lock g(m_aMutex);
+
+ ModuleToCommandFileMap::const_iterator pM2CIter = m_aModuleToCommandFileMap.find( aName );
+ if ( pM2CIter != m_aModuleToCommandFileMap.end() )
+ {
+ OUString aCommandFile( pM2CIter->second );
+ auto pMapIter = m_aUICommandsHashMap.find( rCurrentLanguage );
+ if ( pMapIter == m_aUICommandsHashMap.end() )
+ impl_fillElements("ooSetupFactoryCommandConfigRef");
+
+ auto& rMap = m_aUICommandsHashMap[rCurrentLanguage];
+ UICommandsHashMap::iterator pIter = rMap.find( aCommandFile );
+ if ( pIter != rMap.end() )
+ {
+ if ( pIter->second.is() )
+ a <<= pIter->second;
+ else
+ {
+ ensureGenericUICommandsForLanguage(rCurrentLanguage);
+
+ Reference< XNameAccess > xUICommands = new ConfigurationAccess_UICommand( aCommandFile,
+ m_xGenericUICommands[rCurrentLanguage],
+ m_xContext );
+ pIter->second = xUICommands;
+ a <<= xUICommands;
+ }
+ }
+ }
+ else if ( !m_aPrivateResourceURL.isEmpty() && aName.startsWith( m_aPrivateResourceURL ) )
+ {
+ ensureGenericUICommandsForLanguage(rCurrentLanguage);
+
+ // special keys to retrieve information about a set of commands
+ return m_xGenericUICommands[rCurrentLanguage]->getByName( aName );
+ }
+ else
+ {
+ throw NoSuchElementException();
+ }
+
+ return a;
+}
+
+Sequence< OUString > SAL_CALL UICommandDescription::getElementNames()
+{
+ std::unique_lock g(m_aMutex);
+
+ return comphelper::mapKeysToSequence( m_aModuleToCommandFileMap );
+}
+
+sal_Bool SAL_CALL UICommandDescription::hasByName( const OUString& aName )
+{
+ std::unique_lock g(m_aMutex);
+
+ ModuleToCommandFileMap::const_iterator pIter = m_aModuleToCommandFileMap.find( aName );
+ return ( pIter != m_aModuleToCommandFileMap.end() );
+}
+
+// XElementAccess
+Type SAL_CALL UICommandDescription::getElementType()
+{
+ return cppu::UnoType<XNameAccess>::get();
+}
+
+sal_Bool SAL_CALL UICommandDescription::hasElements()
+{
+ // generic UI commands are always available!
+ return true;
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_UICommandDescription_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new framework::UICommandDescription(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uifactory/addonstoolbarfactory.cxx b/framework/source/uifactory/addonstoolbarfactory.cxx
new file mode 100644
index 000000000..b9a1c95c9
--- /dev/null
+++ b/framework/source/uifactory/addonstoolbarfactory.cxx
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <uielement/addonstoolbarwrapper.hxx>
+
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XModuleManager2.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/ui/XUIElementFactory.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+using namespace ::com::sun::star::ui;
+using namespace framework;
+
+namespace {
+
+class AddonsToolBarFactory : public ::cppu::WeakImplHelper< css::lang::XServiceInfo ,
+ css::ui::XUIElementFactory >
+{
+public:
+ explicit AddonsToolBarFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.AddonsToolBarFactory";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.ui.ToolBarFactory"};
+ }
+
+ // XUIElementFactory
+ virtual css::uno::Reference< css::ui::XUIElement > SAL_CALL createUIElement( const OUString& ResourceURL, const css::uno::Sequence< css::beans::PropertyValue >& Args ) override;
+
+ bool hasButtonsInContext( const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& rPropSeq,
+ const css::uno::Reference< css::frame::XFrame >& rFrame );
+
+private:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::frame::XModuleManager2 > m_xModuleManager;
+};
+
+AddonsToolBarFactory::AddonsToolBarFactory(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
+ m_xContext( xContext )
+ , m_xModuleManager( ModuleManager::create( xContext ) )
+{
+}
+
+bool IsCorrectContext( std::u16string_view rModuleIdentifier, std::u16string_view aContextList )
+{
+ if ( aContextList.empty() )
+ return true;
+
+ if ( !rModuleIdentifier.empty() )
+ {
+ return aContextList.find( rModuleIdentifier ) != std::u16string_view::npos;
+ }
+
+ return false;
+}
+
+bool AddonsToolBarFactory::hasButtonsInContext(
+ const Sequence< Sequence< PropertyValue > >& rPropSeqSeq,
+ const Reference< XFrame >& rFrame )
+{
+ OUString aModuleIdentifier;
+ try
+ {
+ aModuleIdentifier = m_xModuleManager->identify( rFrame );
+ }
+ catch ( const RuntimeException& )
+ {
+ throw;
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ // Check before we create a toolbar that we have at least one button in
+ // the current frame context.
+ for ( Sequence<PropertyValue> const & props : rPropSeqSeq )
+ {
+ bool bIsButton( true );
+ bool bIsCorrectContext( false );
+ sal_uInt32 nPropChecked( 0 );
+
+ for ( PropertyValue const & prop : props )
+ {
+ if ( prop.Name == "Context" )
+ {
+ OUString aContextList;
+ if ( prop.Value >>= aContextList )
+ bIsCorrectContext = IsCorrectContext( aModuleIdentifier, aContextList );
+ nPropChecked++;
+ }
+ else if ( prop.Name == "URL" )
+ {
+ OUString aURL;
+ prop.Value >>= aURL;
+ bIsButton = aURL != "private:separator";
+ nPropChecked++;
+ }
+
+ if ( nPropChecked == 2 )
+ break;
+ }
+
+ if ( bIsButton && bIsCorrectContext )
+ return true;
+ }
+
+ return false;
+}
+
+// XUIElementFactory
+Reference< XUIElement > SAL_CALL AddonsToolBarFactory::createUIElement(
+ const OUString& ResourceURL,
+ const Sequence< PropertyValue >& Args )
+{
+ SolarMutexGuard g;
+
+ Sequence< Sequence< PropertyValue > > aConfigData;
+ Reference< XFrame > xFrame;
+ OUString aResourceURL( ResourceURL );
+
+ for ( PropertyValue const & arg : Args )
+ {
+ if ( arg.Name == "ConfigurationData" )
+ arg.Value >>= aConfigData;
+ else if ( arg.Name == "Frame" )
+ arg.Value >>= xFrame;
+ else if ( arg.Name == "ResourceURL" )
+ arg.Value >>= aResourceURL;
+ }
+
+ if ( !aResourceURL.startsWith("private:resource/toolbar/addon_") )
+ throw IllegalArgumentException();
+
+ // Identify frame and determine module identifier to look for context based buttons
+ Reference< css::ui::XUIElement > xToolBar;
+ if ( xFrame.is() &&
+ aConfigData.hasElements() &&
+ hasButtonsInContext( aConfigData, xFrame ))
+ {
+ Sequence< Any > aPropSeq{ Any(comphelper::makePropertyValue("Frame", xFrame)),
+ Any(comphelper::makePropertyValue("ConfigurationData",
+ aConfigData)),
+ Any(comphelper::makePropertyValue("ResourceURL", aResourceURL)) };
+
+ SolarMutexGuard aGuard;
+ rtl::Reference<AddonsToolBarWrapper> pToolBarWrapper = new AddonsToolBarWrapper( m_xContext );
+ xToolBar = pToolBarWrapper;
+ pToolBarWrapper->initialize( aPropSeq );
+ }
+
+ return xToolBar;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_AddonsToolBarFactory_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new AddonsToolBarFactory(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uifactory/factoryconfiguration.cxx b/framework/source/uifactory/factoryconfiguration.cxx
new file mode 100644
index 000000000..047a6b6ed
--- /dev/null
+++ b/framework/source/uifactory/factoryconfiguration.cxx
@@ -0,0 +1,289 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <uifactory/factoryconfiguration.hxx>
+#include <services.h>
+
+#include <helper/mischelper.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+
+#include <comphelper/propertysequence.hxx>
+#include <utility>
+
+// Defines
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::container;
+
+// Namespace
+
+namespace framework
+{
+static OUString getHashKeyFromStrings(
+ std::u16string_view aCommandURL, std::u16string_view aModuleName )
+{
+ return OUString::Concat(aCommandURL) + "-" + aModuleName;
+}
+
+// XInterface, XTypeProvider
+
+ConfigurationAccess_ControllerFactory::ConfigurationAccess_ControllerFactory( const Reference< XComponentContext >& rxContext, OUString _sRoot ) :
+ m_aPropCommand( "Command" ),
+ m_aPropModule( "Module" ),
+ m_aPropController( "Controller" ),
+ m_aPropValue( "Value" ),
+ m_sRoot(std::move(_sRoot)),
+ m_bConfigAccessInitialized( false )
+{
+ m_xConfigProvider = configuration::theDefaultProvider::get( rxContext );
+}
+
+ConfigurationAccess_ControllerFactory::~ConfigurationAccess_ControllerFactory()
+{
+ std::unique_lock g(m_mutex);
+
+ Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY );
+ if ( xContainer.is() )
+ xContainer->removeContainerListener(m_xConfigAccessListener);
+}
+
+OUString ConfigurationAccess_ControllerFactory::getServiceFromCommandModule( std::u16string_view rCommandURL, std::u16string_view rModule ) const
+{
+ std::unique_lock g(m_mutex);
+ MenuControllerMap::const_iterator pIter = m_aMenuControllerMap.find( getHashKeyFromStrings( rCommandURL, rModule ));
+
+ if ( pIter != m_aMenuControllerMap.end() )
+ return pIter->second.m_aImplementationName;
+ else if ( !rModule.empty() )
+ {
+ // Try to detect if we have a generic popup menu controller
+ pIter = m_aMenuControllerMap.find(
+ getHashKeyFromStrings( rCommandURL, std::u16string_view() ));
+
+ if ( pIter != m_aMenuControllerMap.end() )
+ return pIter->second.m_aImplementationName;
+ }
+
+ return OUString();
+}
+OUString ConfigurationAccess_ControllerFactory::getValueFromCommandModule( std::u16string_view rCommandURL, std::u16string_view rModule ) const
+{
+ std::unique_lock g(m_mutex);
+
+ MenuControllerMap::const_iterator pIter = m_aMenuControllerMap.find( getHashKeyFromStrings( rCommandURL, rModule ));
+
+ if ( pIter != m_aMenuControllerMap.end() )
+ return pIter->second.m_aValue;
+ else if ( !rModule.empty() )
+ {
+ // Try to detect if we have a generic popup menu controller
+ pIter = m_aMenuControllerMap.find(
+ getHashKeyFromStrings( rCommandURL, std::u16string_view() ));
+
+ if ( pIter != m_aMenuControllerMap.end() )
+ return pIter->second.m_aValue;
+ }
+
+ return OUString();
+}
+
+void ConfigurationAccess_ControllerFactory::addServiceToCommandModule(
+ std::u16string_view rCommandURL,
+ std::u16string_view rModule,
+ const OUString& rServiceSpecifier )
+{
+ std::unique_lock g(m_mutex);
+
+ OUString aHashKey = getHashKeyFromStrings( rCommandURL, rModule );
+ m_aMenuControllerMap.emplace( aHashKey,ControllerInfo(rServiceSpecifier,OUString()) );
+}
+
+void ConfigurationAccess_ControllerFactory::removeServiceFromCommandModule(
+ std::u16string_view rCommandURL,
+ std::u16string_view rModule )
+{
+ std::unique_lock g(m_mutex);
+
+ OUString aHashKey = getHashKeyFromStrings( rCommandURL, rModule );
+ m_aMenuControllerMap.erase( aHashKey );
+}
+
+// container.XContainerListener
+void SAL_CALL ConfigurationAccess_ControllerFactory::elementInserted( const ContainerEvent& aEvent )
+{
+ OUString aCommand;
+ OUString aModule;
+ OUString aService;
+ OUString aValue;
+
+ std::unique_lock g(m_mutex);
+
+ if ( impl_getElementProps( aEvent.Element, aCommand, aModule, aService, aValue ))
+ {
+ // Create hash key from command and module as they are together a primary key to
+ // the UNO service that implements the popup menu controller.
+ OUString aHashKey( getHashKeyFromStrings( aCommand, aModule ));
+ ControllerInfo& rControllerInfo = m_aMenuControllerMap[ aHashKey ];
+ rControllerInfo.m_aImplementationName = aService;
+ rControllerInfo.m_aValue = aValue;
+ }
+}
+
+void SAL_CALL ConfigurationAccess_ControllerFactory::elementRemoved ( const ContainerEvent& aEvent )
+{
+ OUString aCommand;
+ OUString aModule;
+ OUString aService;
+ OUString aValue;
+
+ std::unique_lock g(m_mutex);
+
+ if ( impl_getElementProps( aEvent.Element, aCommand, aModule, aService, aValue ))
+ {
+ // Create hash key from command and module as they are together a primary key to
+ // the UNO service that implements the popup menu controller.
+ OUString aHashKey( getHashKeyFromStrings( aCommand, aModule ));
+ m_aMenuControllerMap.erase( aHashKey );
+ }
+}
+
+void SAL_CALL ConfigurationAccess_ControllerFactory::elementReplaced( const ContainerEvent& aEvent )
+{
+ elementInserted(aEvent);
+}
+
+// lang.XEventListener
+void SAL_CALL ConfigurationAccess_ControllerFactory::disposing( const EventObject& )
+{
+ // remove our reference to the config access
+ std::unique_lock g(m_mutex);
+ m_xConfigAccess.clear();
+}
+
+void ConfigurationAccess_ControllerFactory::readConfigurationData()
+{
+ // SAFE
+ std::unique_lock aLock( m_mutex );
+
+ if ( !m_bConfigAccessInitialized )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(m_sRoot)}
+ }));
+ try
+ {
+ m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments(SERVICENAME_CFGREADACCESS,aArgs ), UNO_QUERY );
+ }
+ catch ( const WrappedTargetException& )
+ {
+ }
+
+ m_bConfigAccessInitialized = true;
+ }
+
+ if ( !m_xConfigAccess.is() )
+ return;
+
+ // Read and update configuration data
+ updateConfigurationDataImpl();
+
+ uno::Reference< container::XContainer > xContainer( m_xConfigAccess, uno::UNO_QUERY );
+ // UNSAFE
+ aLock.unlock();
+
+ if ( xContainer.is() )
+ {
+ m_xConfigAccessListener = new WeakContainerListener(this);
+ xContainer->addContainerListener(m_xConfigAccessListener);
+ }
+}
+
+void ConfigurationAccess_ControllerFactory::updateConfigurationDataImpl()
+{
+ const Sequence< OUString > aPopupMenuControllers = m_xConfigAccess->getElementNames();
+
+ OUString aCommand;
+ OUString aModule;
+ OUString aService;
+ OUString aHashKey;
+ OUString aValue;
+
+ m_aMenuControllerMap.clear();
+ for ( OUString const & name : aPopupMenuControllers )
+ {
+ try
+ {
+ if ( impl_getElementProps( m_xConfigAccess->getByName( name ), aCommand, aModule, aService,aValue ))
+ {
+ // Create hash key from command and module as they are together a primary key to
+ // the UNO service that implements the popup menu controller.
+ aHashKey = getHashKeyFromStrings( aCommand, aModule );
+ m_aMenuControllerMap.emplace( aHashKey, ControllerInfo(aService,aValue) );
+ }
+ }
+ catch ( const NoSuchElementException& )
+ {
+ }
+ catch ( const WrappedTargetException& )
+ {
+ }
+ }
+}
+
+bool ConfigurationAccess_ControllerFactory::impl_getElementProps( const Any& aElement, OUString& aCommand, OUString& aModule, OUString& aServiceSpecifier,OUString& aValue ) const
+{
+ Reference< XPropertySet > xPropertySet;
+ aElement >>= xPropertySet;
+
+ if ( !xPropertySet.is() )
+ return true;
+
+ try
+ {
+ xPropertySet->getPropertyValue( m_aPropCommand ) >>= aCommand;
+ xPropertySet->getPropertyValue( m_aPropModule ) >>= aModule;
+ xPropertySet->getPropertyValue( m_aPropController ) >>= aServiceSpecifier;
+ xPropertySet->getPropertyValue( m_aPropValue ) >>= aValue;
+ }
+ catch ( const css::beans::UnknownPropertyException& )
+ {
+ return false;
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ return false;
+ }
+
+ return true;
+}
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uifactory/menubarfactory.cxx b/framework/source/uifactory/menubarfactory.cxx
new file mode 100644
index 000000000..febc33066
--- /dev/null
+++ b/framework/source/uifactory/menubarfactory.cxx
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uifactory/menubarfactory.hxx>
+
+#include <uielement/menubarwrapper.hxx>
+
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <utility>
+#include <vcl/svapp.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+using namespace ::com::sun::star::ui;
+
+namespace framework
+{
+
+MenuBarFactory::MenuBarFactory( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : m_xContext(std::move( xContext ))
+{
+}
+
+MenuBarFactory::~MenuBarFactory()
+{
+}
+
+// XUIElementFactory
+Reference< XUIElement > SAL_CALL MenuBarFactory::createUIElement(
+ const OUString& ResourceURL,
+ const Sequence< PropertyValue >& Args )
+{
+ Reference< css::ui::XUIElement > xMenuBar = new MenuBarWrapper(m_xContext);
+ CreateUIElement(ResourceURL, Args, u"private:resource/menubar/", xMenuBar, m_xContext);
+ return xMenuBar;
+}
+
+void MenuBarFactory::CreateUIElement(const OUString& ResourceURL
+ ,const Sequence< PropertyValue >& Args
+ ,std::u16string_view ResourceType
+ ,const Reference< css::ui::XUIElement >& _xMenuBar
+ ,const css::uno::Reference< css::uno::XComponentContext >& _rxContext)
+{
+ sal_Int32 nConfigPropertyIndex( Args.getLength() );
+ sal_Int32 nURLPropertyIndex( Args.getLength() );
+ Reference< XUIConfigurationManager > xCfgMgr;
+ Reference< XFrame > xFrame;
+ OUString aResourceURL( ResourceURL );
+
+ for ( sal_Int32 n = 0; n < Args.getLength(); n++ )
+ {
+ if ( Args[n].Name == "ConfigurationSource" )
+ {
+ nConfigPropertyIndex = n;
+ Args[n].Value >>= xCfgMgr;
+ }
+ else if ( Args[n].Name == "ResourceURL" )
+ {
+ nURLPropertyIndex = n;
+ Args[n].Value >>= aResourceURL;
+ }
+ else if ( Args[n].Name == "Frame" )
+ Args[n].Value >>= xFrame;
+ }
+ if (!aResourceURL.startsWith(ResourceType))
+ throw IllegalArgumentException();
+
+ // Identify frame and determine document based ui configuration manager/module ui configuration manager
+ if ( xFrame.is() && !xCfgMgr.is() )
+ {
+ bool bHasSettings( false );
+ Reference< XModel > xModel;
+
+ Reference< XController > xController = xFrame->getController();
+ if ( xController.is() )
+ xModel = xController->getModel();
+
+ if ( xModel.is() )
+ {
+ Reference< XUIConfigurationManagerSupplier > xUIConfigurationManagerSupplier( xModel, UNO_QUERY );
+ if ( xUIConfigurationManagerSupplier.is() )
+ {
+ xCfgMgr = xUIConfigurationManagerSupplier->getUIConfigurationManager();
+ bHasSettings = xCfgMgr->hasSettings( aResourceURL );
+ }
+ }
+
+ if ( !bHasSettings )
+ {
+ Reference< css::frame::XModuleManager2 > xModuleManager =
+ ModuleManager::create( _rxContext );
+ OUString aModuleIdentifier = xModuleManager->identify( Reference<XInterface>( xFrame, UNO_QUERY ) );
+ if ( !aModuleIdentifier.isEmpty() )
+ {
+ Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgSupplier =
+ theModuleUIConfigurationManagerSupplier::get( _rxContext );
+ xCfgMgr = xModuleCfgSupplier->getUIConfigurationManager( aModuleIdentifier );
+ }
+ }
+ }
+
+ sal_Int32 nSeqLength( Args.getLength() );
+ if ( Args.getLength() == nConfigPropertyIndex )
+ nSeqLength++;
+ if ( Args.getLength() == nURLPropertyIndex )
+ nSeqLength++;
+ if ( nConfigPropertyIndex == nURLPropertyIndex )
+ nURLPropertyIndex++;
+
+ Sequence< Any > aPropSeq( nSeqLength );
+ auto aPropSeqRange = asNonConstRange(aPropSeq);
+ for ( sal_Int32 n = 0; n < aPropSeq.getLength(); n++ )
+ {
+ PropertyValue aPropValue;
+ if ( n == nURLPropertyIndex )
+ {
+ aPropValue.Name = "ResourceURL";
+ aPropValue.Value <<= aResourceURL;
+ }
+ else if ( n == nConfigPropertyIndex )
+ {
+ aPropValue.Name = "ConfigurationSource";
+ aPropValue.Value <<= xCfgMgr;
+ }
+ else
+ aPropValue = Args[n];
+
+ aPropSeqRange[n] <<= aPropValue;
+ }
+
+ SolarMutexGuard aGuard;
+ Reference< XInitialization > xInit( _xMenuBar, UNO_QUERY );
+ xInit->initialize( aPropSeq );
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_MenuBarFactory_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new framework::MenuBarFactory(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uifactory/statusbarfactory.cxx b/framework/source/uifactory/statusbarfactory.cxx
new file mode 100644
index 000000000..2914dc4cc
--- /dev/null
+++ b/framework/source/uifactory/statusbarfactory.cxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <uifactory/menubarfactory.hxx>
+#include <uielement/statusbarwrapper.hxx>
+
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+using namespace ::com::sun::star::ui;
+
+using namespace framework;
+
+namespace {
+
+class StatusBarFactory : public MenuBarFactory
+{
+public:
+ explicit StatusBarFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.StatusBarFactory";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.ui.StatusBarFactory"};
+ }
+
+ // XUIElementFactory
+ virtual css::uno::Reference< css::ui::XUIElement > SAL_CALL createUIElement( const OUString& ResourceURL, const css::uno::Sequence< css::beans::PropertyValue >& Args ) override;
+};
+
+StatusBarFactory::StatusBarFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
+ MenuBarFactory( xContext )
+{
+}
+
+// XUIElementFactory
+Reference< XUIElement > SAL_CALL StatusBarFactory::createUIElement(
+ const OUString& ResourceURL,
+ const Sequence< PropertyValue >& Args )
+{
+ Reference< css::ui::XUIElement > xStatusBar = new StatusBarWrapper(m_xContext);
+ MenuBarFactory::CreateUIElement(ResourceURL, Args, u"private:resource/statusbar/", xStatusBar, m_xContext);
+ return xStatusBar;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_StatusBarFactory_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new StatusBarFactory(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uifactory/toolbarfactory.cxx b/framework/source/uifactory/toolbarfactory.cxx
new file mode 100644
index 000000000..1f9bb6114
--- /dev/null
+++ b/framework/source/uifactory/toolbarfactory.cxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/svapp.hxx>
+#include <uielement/toolbarwrapper.hxx>
+#include <uifactory/menubarfactory.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+using namespace ::com::sun::star::ui;
+using namespace framework;
+
+namespace {
+
+class ToolBarFactory : public MenuBarFactory
+{
+public:
+ explicit ToolBarFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.ToolBarFactory";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.ui.ToolBarFactory"};
+ }
+
+ // XUIElementFactory
+ virtual css::uno::Reference< css::ui::XUIElement > SAL_CALL createUIElement(
+ const OUString& ResourceURL, const css::uno::Sequence< css::beans::PropertyValue >& Args ) override;
+};
+
+ToolBarFactory::ToolBarFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
+ MenuBarFactory( xContext )
+{
+}
+
+// XUIElementFactory
+Reference< XUIElement > SAL_CALL ToolBarFactory::createUIElement(
+ const OUString& ResourceURL,
+ const Sequence< PropertyValue >& Args )
+{
+ Reference< css::ui::XUIElement > xToolBar = new ToolBarWrapper(m_xContext);
+ CreateUIElement(ResourceURL, Args, u"private:resource/toolbar/", xToolBar, m_xContext);
+ return xToolBar;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_ToolBarFactory_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ToolBarFactory(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uifactory/uicontrollerfactory.cxx b/framework/source/uifactory/uicontrollerfactory.cxx
new file mode 100644
index 000000000..5f2390076
--- /dev/null
+++ b/framework/source/uifactory/uicontrollerfactory.cxx
@@ -0,0 +1,341 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <uifactory/factoryconfiguration.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/frame/XUIControllerFactory.hpp>
+
+#include <rtl/ref.hxx>
+#include <comphelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace css::uno;
+using namespace css::lang;
+using namespace css::beans;
+using namespace css::container;
+using namespace css::frame;
+using namespace framework;
+
+namespace {
+
+typedef comphelper::WeakComponentImplHelper<
+ css::lang::XServiceInfo,
+ css::frame::XUIControllerFactory > UIControllerFactory_BASE;
+
+class UIControllerFactory : public UIControllerFactory_BASE
+{
+public:
+ virtual ~UIControllerFactory() override;
+
+ // XMultiComponentFactory
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithContext( const OUString& aServiceSpecifier, const css::uno::Reference< css::uno::XComponentContext >& Context ) override;
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArgumentsAndContext( const OUString& ServiceSpecifier, const css::uno::Sequence< css::uno::Any >& Arguments, const css::uno::Reference< css::uno::XComponentContext >& Context ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames() override;
+
+ // XUIControllerRegistration
+ virtual sal_Bool SAL_CALL hasController( const OUString& aCommandURL, const OUString& aModuleName ) override;
+ virtual void SAL_CALL registerController( const OUString& aCommandURL, const OUString& aModuleName, const OUString& aControllerImplementationName ) override;
+ virtual void SAL_CALL deregisterController( const OUString& aCommandURL, const OUString& aModuleName ) override;
+
+protected:
+ UIControllerFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext, std::u16string_view rUINode );
+ bool m_bConfigRead;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ rtl::Reference<ConfigurationAccess_ControllerFactory> m_pConfigAccess;
+
+private:
+ virtual void disposing(std::unique_lock<std::mutex>&) final override;
+};
+
+UIControllerFactory::UIControllerFactory(
+ const Reference< XComponentContext >& xContext,
+ std::u16string_view rConfigurationNode )
+ : m_bConfigRead( false )
+ , m_xContext( xContext )
+{
+ m_pConfigAccess = new ConfigurationAccess_ControllerFactory(m_xContext,
+ OUString::Concat("/org.openoffice.Office.UI.Controller/Registered/")
+ + rConfigurationNode);
+}
+
+UIControllerFactory::~UIControllerFactory()
+{
+ std::unique_lock g(m_aMutex);
+ disposing(g);
+}
+
+void UIControllerFactory::disposing(std::unique_lock<std::mutex>&)
+{
+ m_pConfigAccess.clear();
+}
+
+// XMultiComponentFactory
+Reference< XInterface > SAL_CALL UIControllerFactory::createInstanceWithContext(
+ const OUString& aServiceSpecifier,
+ const Reference< XComponentContext >& )
+{
+ // SAFE
+ std::unique_lock g(m_aMutex);
+
+ if ( !m_bConfigRead )
+ {
+ m_bConfigRead = true;
+ m_pConfigAccess->readConfigurationData();
+ }
+
+ OUString aServiceName = m_pConfigAccess->getServiceFromCommandModule( aServiceSpecifier, std::u16string_view() );
+ if ( !aServiceName.isEmpty() )
+ return m_xContext->getServiceManager()->createInstanceWithContext( aServiceName, m_xContext );
+ else
+ return Reference< XInterface >();
+ // SAFE
+}
+
+Reference< XInterface > SAL_CALL UIControllerFactory::createInstanceWithArgumentsAndContext(
+ const OUString& ServiceSpecifier,
+ const Sequence< Any >& Arguments,
+ const Reference< XComponentContext >& )
+{
+ static const OUStringLiteral aPropModuleName( u"ModuleIdentifier" );
+
+ OUString aPropName;
+ PropertyValue aPropValue;
+
+ // Retrieve the optional module name from the Arguments sequence. It is used as a part of
+ // the hash map key to support different controller implementation for the same URL but different
+ // module!!
+ for ( Any const & arg : Arguments )
+ {
+ if (( arg >>= aPropValue ) && ( aPropValue.Name == aPropModuleName ))
+ {
+ aPropValue.Value >>= aPropName;
+ break;
+ }
+ }
+
+ Sequence< Any > aNewArgs( Arguments );
+
+ sal_Int32 nAppendIndex = aNewArgs.getLength();
+ aNewArgs.realloc( aNewArgs.getLength() + 2 );
+ auto pNewArgs = aNewArgs.getArray();
+
+ // Append the command URL to the Arguments sequence so that one controller can be
+ // used for more than one command URL.
+ aPropValue.Name = "CommandURL";
+ aPropValue.Value <<= ServiceSpecifier;
+ pNewArgs[nAppendIndex] <<= aPropValue;
+
+ // Append the optional value argument. It's an empty string if no additional info
+ // is provided to the controller.
+ OUString aValue = m_pConfigAccess->getValueFromCommandModule( ServiceSpecifier, aPropName );
+ aPropValue.Name = "Value";
+ aPropValue.Value <<= aValue;
+ pNewArgs[nAppendIndex+1] <<= aPropValue;
+
+ {
+ OUString aServiceName;
+ { // SAFE
+ std::unique_lock g(m_aMutex);
+
+ if ( !m_bConfigRead )
+ {
+ m_bConfigRead = true;
+ m_pConfigAccess->readConfigurationData();
+ }
+
+ aServiceName = m_pConfigAccess->getServiceFromCommandModule( ServiceSpecifier, aPropName );
+ } // SAFE
+
+ if ( !aServiceName.isEmpty() )
+ return m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( aServiceName, aNewArgs, m_xContext );
+ else
+ return Reference< XInterface >();
+ }
+}
+
+Sequence< OUString > SAL_CALL UIControllerFactory::getAvailableServiceNames()
+{
+ return Sequence< OUString >();
+}
+
+// XUIControllerRegistration
+sal_Bool SAL_CALL UIControllerFactory::hasController(
+ const OUString& aCommandURL,
+ const OUString& aModuleName )
+{
+ std::unique_lock g(m_aMutex);
+
+ if ( !m_bConfigRead )
+ {
+ m_bConfigRead = true;
+ m_pConfigAccess->readConfigurationData();
+ }
+
+ return ( !m_pConfigAccess->getServiceFromCommandModule( aCommandURL, aModuleName ).isEmpty() );
+}
+
+void SAL_CALL UIControllerFactory::registerController(
+ const OUString& aCommandURL,
+ const OUString& aModuleName,
+ const OUString& aControllerImplementationName )
+{
+ // SAFE
+ std::unique_lock g(m_aMutex);
+
+ if ( !m_bConfigRead )
+ {
+ m_bConfigRead = true;
+ m_pConfigAccess->readConfigurationData();
+ }
+
+ m_pConfigAccess->addServiceToCommandModule( aCommandURL, aModuleName, aControllerImplementationName );
+ // SAFE
+}
+
+void SAL_CALL UIControllerFactory::deregisterController(
+ const OUString& aCommandURL,
+ const OUString& aModuleName )
+{
+ // SAFE
+ std::unique_lock g(m_aMutex);
+
+ if ( !m_bConfigRead )
+ {
+ m_bConfigRead = true;
+ m_pConfigAccess->readConfigurationData();
+ }
+
+ m_pConfigAccess->removeServiceFromCommandModule( aCommandURL, aModuleName );
+ // SAFE
+}
+
+class PopupMenuControllerFactory : public UIControllerFactory
+{
+public:
+ explicit PopupMenuControllerFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.PopupMenuControllerFactory";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.frame.PopupMenuControllerFactory"};
+ }
+
+};
+
+PopupMenuControllerFactory::PopupMenuControllerFactory( const Reference< XComponentContext >& xContext ) :
+ UIControllerFactory( xContext, u"PopupMenu" )
+{
+}
+
+class ToolbarControllerFactory : public UIControllerFactory
+{
+public:
+ explicit ToolbarControllerFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.ToolBarControllerFactory";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.frame.ToolbarControllerFactory"};
+ }
+
+};
+
+ToolbarControllerFactory::ToolbarControllerFactory( const Reference< XComponentContext >& xContext ) :
+ UIControllerFactory( xContext, u"ToolBar" )
+{
+}
+
+class StatusbarControllerFactory : public UIControllerFactory
+{
+public:
+ explicit StatusbarControllerFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.StatusBarControllerFactory";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.frame.StatusbarControllerFactory"};
+ }
+
+};
+
+StatusbarControllerFactory::StatusbarControllerFactory( const Reference< XComponentContext >& xContext ) :
+ UIControllerFactory( xContext, u"StatusBar" )
+{
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_PopupMenuControllerFactory_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new PopupMenuControllerFactory(context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_ToolBarControllerFactory_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ToolbarControllerFactory(context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_StatusBarControllerFactory_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new StatusbarControllerFactory(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uifactory/uielementfactorymanager.cxx b/framework/source/uifactory/uielementfactorymanager.cxx
new file mode 100644
index 000000000..b8a8ea46e
--- /dev/null
+++ b/framework/source/uifactory/uielementfactorymanager.cxx
@@ -0,0 +1,553 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <uifactory/configurationaccessfactorymanager.hxx>
+#include <helper/mischelper.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/loader/CannotActivateFactoryException.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/UnknownModuleException.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XModuleManager2.hpp>
+#include <com/sun/star/ui/XUIElementFactoryManager.hpp>
+
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <utility>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::configuration;
+using namespace com::sun::star::container;
+using namespace ::com::sun::star::ui;
+using namespace framework;
+
+namespace framework
+{
+
+// global function needed by both implementations
+static OUString getHashKeyFromStrings( std::u16string_view aType, std::u16string_view aName, std::u16string_view aModuleName )
+{
+ return OUString::Concat(aType) + "^" + aName + "^" + aModuleName;
+}
+
+ConfigurationAccess_FactoryManager::ConfigurationAccess_FactoryManager( const Reference< XComponentContext >& rxContext, OUString _sRoot ) :
+ m_aPropType( "Type" ),
+ m_aPropName( "Name" ),
+ m_aPropModule( "Module" ),
+ m_aPropFactory( "FactoryImplementation" ),
+ m_sRoot(std::move(_sRoot)),
+ m_bConfigAccessInitialized( false )
+{
+ m_xConfigProvider = theDefaultProvider::get( rxContext );
+}
+
+ConfigurationAccess_FactoryManager::~ConfigurationAccess_FactoryManager()
+{
+ // SAFE
+ std::unique_lock g(m_aMutex);
+
+ Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY );
+ if ( xContainer.is() )
+ xContainer->removeContainerListener(m_xConfigListener);
+}
+
+OUString ConfigurationAccess_FactoryManager::getFactorySpecifierFromTypeNameModule( std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule ) const
+{
+ // SAFE
+ std::unique_lock g(m_aMutex);
+
+ FactoryManagerMap::const_iterator pIter =
+ m_aFactoryManagerMap.find( getHashKeyFromStrings( rType, rName, rModule ));
+ if ( pIter != m_aFactoryManagerMap.end() )
+ return pIter->second;
+ else
+ {
+ pIter = m_aFactoryManagerMap.find(
+ getHashKeyFromStrings( rType, rName, std::u16string_view() ));
+ if ( pIter != m_aFactoryManagerMap.end() )
+ return pIter->second;
+ else
+ {
+ // Support factories which uses a defined prefix before the ui name.
+ size_t nIndex = rName.find( '_' );
+ if ( nIndex > 0 && nIndex != std::u16string_view::npos)
+ {
+ std::u16string_view aName = rName.substr( 0, nIndex+1 );
+ pIter = m_aFactoryManagerMap.find( getHashKeyFromStrings( rType, aName, std::u16string_view() ));
+ if ( pIter != m_aFactoryManagerMap.end() )
+ return pIter->second;
+ }
+
+ pIter = m_aFactoryManagerMap.find( getHashKeyFromStrings( rType, std::u16string_view(), std::u16string_view() ));
+ if ( pIter != m_aFactoryManagerMap.end() )
+ return pIter->second;
+ }
+ }
+
+ return OUString();
+}
+
+void ConfigurationAccess_FactoryManager::addFactorySpecifierToTypeNameModule( std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule, const OUString& rServiceSpecifier )
+{
+ // SAFE
+ std::unique_lock g(m_aMutex);
+
+ OUString aHashKey = getHashKeyFromStrings( rType, rName, rModule );
+
+ FactoryManagerMap::const_iterator pIter = m_aFactoryManagerMap.find( aHashKey );
+
+ if ( pIter != m_aFactoryManagerMap.end() )
+ throw ElementExistException();
+ m_aFactoryManagerMap.emplace( aHashKey, rServiceSpecifier );
+}
+
+void ConfigurationAccess_FactoryManager::removeFactorySpecifierFromTypeNameModule( std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule )
+{
+ // SAFE
+ std::unique_lock g(m_aMutex);
+
+ OUString aHashKey = getHashKeyFromStrings( rType, rName, rModule );
+
+ FactoryManagerMap::const_iterator pIter = m_aFactoryManagerMap.find( aHashKey );
+
+ if ( pIter == m_aFactoryManagerMap.end() )
+ throw NoSuchElementException();
+ m_aFactoryManagerMap.erase( aHashKey );
+}
+
+Sequence< Sequence< PropertyValue > > ConfigurationAccess_FactoryManager::getFactoriesDescription() const
+{
+ // SAFE
+ std::unique_lock g(m_aMutex);
+
+ Sequence< Sequence< PropertyValue > > aSeqSeq;
+
+ sal_Int32 nIndex( 0 );
+ for ( const auto& rEntry : m_aFactoryManagerMap )
+ {
+ OUString aFactory = rEntry.first;
+ if ( !aFactory.isEmpty() )
+ {
+ sal_Int32 nToken = 0;
+
+ aSeqSeq.realloc( aSeqSeq.getLength() + 1 );
+ Sequence< PropertyValue > aSeq{ comphelper::makePropertyValue(
+ m_aPropType, aFactory.getToken( 0, '^', nToken )) };
+ if ( nToken > 0 )
+ {
+ aSeq.realloc( 2 );
+ aSeq.getArray()[1]
+ = comphelper::makePropertyValue(m_aPropName,
+ aFactory.getToken( 0, '^', nToken ));
+ if ( nToken > 0 )
+ {
+ aSeq.realloc( 3 );
+ aSeq.getArray()[2]
+ = comphelper::makePropertyValue(m_aPropModule,
+ aFactory.getToken( 0, '^', nToken ));
+ }
+ }
+
+ aSeqSeq.getArray()[nIndex++] = aSeq;
+ }
+ }
+
+ return aSeqSeq;
+}
+
+// container.XContainerListener
+void SAL_CALL ConfigurationAccess_FactoryManager::elementInserted( const ContainerEvent& aEvent )
+{
+ OUString aType;
+ OUString aName;
+ OUString aModule;
+ OUString aService;
+
+ // SAFE
+ std::unique_lock g(m_aMutex);
+
+ if ( impl_getElementProps( aEvent.Element, aType, aName, aModule, aService ))
+ {
+ // Create hash key from type, name and module as they are together a primary key to
+ // the UNO service that implements a user interface factory.
+ OUString aHashKey( getHashKeyFromStrings( aType, aName, aModule ));
+ m_aFactoryManagerMap.emplace( aHashKey, aService );
+ }
+}
+
+void SAL_CALL ConfigurationAccess_FactoryManager::elementRemoved ( const ContainerEvent& aEvent )
+{
+ OUString aType;
+ OUString aName;
+ OUString aModule;
+ OUString aService;
+
+ // SAFE
+ std::unique_lock g(m_aMutex);
+
+ if ( impl_getElementProps( aEvent.Element, aType, aName, aModule, aService ))
+ {
+ // Create hash key from command and model as they are together a primary key to
+ // the UNO service that implements the popup menu controller.
+ OUString aHashKey( getHashKeyFromStrings( aType, aName, aModule ));
+ m_aFactoryManagerMap.erase( aHashKey );
+ }
+}
+
+void SAL_CALL ConfigurationAccess_FactoryManager::elementReplaced( const ContainerEvent& aEvent )
+{
+ OUString aType;
+ OUString aName;
+ OUString aModule;
+ OUString aService;
+
+ // SAFE
+ std::unique_lock g(m_aMutex);
+
+ if ( impl_getElementProps( aEvent.Element, aType, aName, aModule, aService ))
+ {
+ // Create hash key from command and model as they are together a primary key to
+ // the UNO service that implements the popup menu controller.
+ OUString aHashKey( getHashKeyFromStrings( aType, aName, aModule ));
+ m_aFactoryManagerMap.erase( aHashKey );
+ m_aFactoryManagerMap.emplace( aHashKey, aService );
+ }
+}
+
+// lang.XEventListener
+void SAL_CALL ConfigurationAccess_FactoryManager::disposing( const EventObject& )
+{
+ // SAFE
+ // remove our reference to the config access
+ std::unique_lock g(m_aMutex);
+ m_xConfigAccess.clear();
+}
+
+void ConfigurationAccess_FactoryManager::readConfigurationData()
+{
+ // SAFE
+ std::unique_lock g(m_aMutex);
+
+ if ( !m_bConfigAccessInitialized )
+ {
+ Sequence<Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", Any(m_sRoot)}
+ }));
+
+ try
+ {
+ m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess", aArgs ), UNO_QUERY );
+ }
+ catch ( const WrappedTargetException& )
+ {
+ }
+
+ m_bConfigAccessInitialized = true;
+ }
+
+ if ( !m_xConfigAccess.is() )
+ return;
+
+ const Sequence< OUString > aUIElementFactories = m_xConfigAccess->getElementNames();
+
+ OUString aType;
+ OUString aName;
+ OUString aModule;
+ OUString aService;
+ OUString aHashKey;
+ for ( OUString const & factoryName : aUIElementFactories )
+ {
+ if ( impl_getElementProps( m_xConfigAccess->getByName( factoryName ), aType, aName, aModule, aService ))
+ {
+ // Create hash key from type, name and module as they are together a primary key to
+ // the UNO service that implements the user interface element factory.
+ aHashKey = getHashKeyFromStrings( aType, aName, aModule );
+ m_aFactoryManagerMap.emplace( aHashKey, aService );
+ }
+ }
+
+ Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY );
+ if ( xContainer.is() )
+ {
+ m_xConfigListener = new WeakContainerListener(this);
+ xContainer->addContainerListener(m_xConfigListener);
+ }
+}
+
+bool ConfigurationAccess_FactoryManager::impl_getElementProps( const Any& aElement, OUString& rType, OUString& rName, OUString& rModule, OUString& rServiceSpecifier ) const
+{
+ Reference< XPropertySet > xPropertySet;
+ aElement >>= xPropertySet;
+
+ if ( !xPropertySet.is() )
+ return true;
+
+ try
+ {
+ xPropertySet->getPropertyValue( m_aPropType ) >>= rType;
+ xPropertySet->getPropertyValue( m_aPropName ) >>= rName;
+ xPropertySet->getPropertyValue( m_aPropModule ) >>= rModule;
+ xPropertySet->getPropertyValue( m_aPropFactory ) >>= rServiceSpecifier;
+ }
+ catch ( const css::beans::UnknownPropertyException& )
+ {
+ return false;
+ }
+ catch ( const css::lang::WrappedTargetException& )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+} // framework
+
+namespace {
+
+typedef comphelper::WeakComponentImplHelper<
+ css::lang::XServiceInfo,
+ css::ui::XUIElementFactoryManager> UIElementFactoryManager_BASE;
+
+class UIElementFactoryManager : public UIElementFactoryManager_BASE
+{
+ virtual void disposing(std::unique_lock<std::mutex>&) override;
+public:
+ explicit UIElementFactoryManager( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.UIElementFactoryManager";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.ui.UIElementFactoryManager"};
+ }
+
+ // XUIElementFactory
+ virtual css::uno::Reference< css::ui::XUIElement > SAL_CALL createUIElement( const OUString& ResourceURL, const css::uno::Sequence< css::beans::PropertyValue >& Args ) override;
+
+ // XUIElementFactoryRegistration
+ virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getRegisteredFactories( ) override;
+ virtual css::uno::Reference< css::ui::XUIElementFactory > SAL_CALL getFactory( const OUString& ResourceURL, const OUString& ModuleIdentifier ) override;
+ virtual void SAL_CALL registerFactory( const OUString& aType, const OUString& aName, const OUString& aModuleIdentifier, const OUString& aFactoryImplementationName ) override;
+ virtual void SAL_CALL deregisterFactory( const OUString& aType, const OUString& aName, const OUString& aModuleIdentifier ) override;
+
+private:
+ bool m_bConfigRead;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ rtl::Reference<ConfigurationAccess_FactoryManager> m_pConfigAccess;
+};
+
+UIElementFactoryManager::UIElementFactoryManager( const Reference< XComponentContext >& rxContext ) :
+ m_bConfigRead( false ),
+ m_xContext(rxContext),
+ m_pConfigAccess(
+ new ConfigurationAccess_FactoryManager(
+ rxContext,
+ "/org.openoffice.Office.UI.Factories/Registered/UIElementFactories"))
+{}
+
+void UIElementFactoryManager::disposing(std::unique_lock<std::mutex>&)
+{
+ m_pConfigAccess.clear();
+}
+
+// XUIElementFactory
+Reference< XUIElement > SAL_CALL UIElementFactoryManager::createUIElement(
+ const OUString& ResourceURL,
+ const Sequence< PropertyValue >& Args )
+{
+ Reference< XFrame > xFrame;
+ OUString aModuleId;
+ { // SAFE
+ std::unique_lock g(m_aMutex);
+ if (m_bDisposed) {
+ throw css::lang::DisposedException(
+ "disposed", static_cast<OWeakObject *>(this));
+ }
+
+ if ( !m_bConfigRead )
+ {
+ m_bConfigRead = true;
+ m_pConfigAccess->readConfigurationData();
+ }
+
+ // Retrieve the frame instance from the arguments to determine the module identifier. This must be provided
+ // to the search function. An empty module identifier is provided if the frame is missing or the module id cannot
+ // retrieve from it.
+ for ( auto const & arg : Args )
+ {
+ if ( arg.Name == "Frame")
+ arg.Value >>= xFrame;
+ if (arg.Name == "Module")
+ arg.Value >>= aModuleId;
+ }
+ } // SAFE
+
+ Reference< XModuleManager2 > xManager = ModuleManager::create( m_xContext );
+
+ // Determine the module identifier
+ try
+ {
+ if ( aModuleId.isEmpty() && xFrame.is() && xManager.is() )
+ aModuleId = xManager->identify( Reference<XInterface>( xFrame, UNO_QUERY ) );
+
+ Reference< XUIElementFactory > xUIElementFactory = getFactory( ResourceURL, aModuleId );
+ if ( xUIElementFactory.is() )
+ return xUIElementFactory->createUIElement( ResourceURL, Args );
+ }
+ catch ( const UnknownModuleException& )
+ {
+ }
+
+ throw NoSuchElementException();
+}
+
+// XUIElementFactoryRegistration
+Sequence< Sequence< PropertyValue > > SAL_CALL UIElementFactoryManager::getRegisteredFactories()
+{
+ // SAFE
+ std::unique_lock g(m_aMutex);
+ if (m_bDisposed) {
+ throw css::lang::DisposedException(
+ "disposed", static_cast<OWeakObject *>(this));
+ }
+
+ if ( !m_bConfigRead )
+ {
+ m_bConfigRead = true;
+ m_pConfigAccess->readConfigurationData();
+ }
+
+ return m_pConfigAccess->getFactoriesDescription();
+}
+
+Reference< XUIElementFactory > SAL_CALL UIElementFactoryManager::getFactory( const OUString& aResourceURL, const OUString& aModuleId )
+{
+ OUString aServiceSpecifier;
+ { // SAFE
+ std::unique_lock g(m_aMutex);
+ if (m_bDisposed) {
+ throw css::lang::DisposedException(
+ "disposed", static_cast<OWeakObject *>(this));
+ }
+
+ if ( !m_bConfigRead )
+ {
+ m_bConfigRead = true;
+ m_pConfigAccess->readConfigurationData();
+ }
+
+ OUString aType;
+ OUString aName;
+ RetrieveTypeNameFromResourceURL( aResourceURL, aType, aName );
+ aServiceSpecifier = m_pConfigAccess->getFactorySpecifierFromTypeNameModule( aType, aName, aModuleId );
+ } // SAFE
+
+ if ( !aServiceSpecifier.isEmpty() ) try
+ {
+ Reference< XUIElementFactory > xFactory(m_xContext->getServiceManager()->
+ createInstanceWithContext(aServiceSpecifier, m_xContext), UNO_QUERY);
+ SAL_WARN_IF(!xFactory.is(), "fwk.uielement", "could not create factory: " << aServiceSpecifier);
+ return xFactory;
+ }
+ catch ( const css::loader::CannotActivateFactoryException& )
+ {
+ SAL_WARN("fwk.uielement", aServiceSpecifier <<
+ " not available. This should happen only on mobile platforms.");
+ }
+ return Reference< XUIElementFactory >();
+}
+
+void SAL_CALL UIElementFactoryManager::registerFactory( const OUString& aType, const OUString& aName, const OUString& aModuleId, const OUString& aFactoryImplementationName )
+{
+ // SAFE
+ std::unique_lock g(m_aMutex);
+ if (m_bDisposed) {
+ throw css::lang::DisposedException(
+ "disposed", static_cast<OWeakObject *>(this));
+ }
+
+ if ( !m_bConfigRead )
+ {
+ m_bConfigRead = true;
+ m_pConfigAccess->readConfigurationData();
+ }
+
+ m_pConfigAccess->addFactorySpecifierToTypeNameModule( aType, aName, aModuleId, aFactoryImplementationName );
+ // SAFE
+}
+
+void SAL_CALL UIElementFactoryManager::deregisterFactory( const OUString& aType, const OUString& aName, const OUString& aModuleId )
+{
+ // SAFE
+ std::unique_lock g(m_aMutex);
+ if (m_bDisposed) {
+ throw css::lang::DisposedException(
+ "disposed", static_cast<OWeakObject *>(this));
+ }
+
+ if ( !m_bConfigRead )
+ {
+ m_bConfigRead = true;
+ m_pConfigAccess->readConfigurationData();
+ }
+
+ m_pConfigAccess->removeFactorySpecifierFromTypeNameModule( aType, aName, aModuleId );
+ // SAFE
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_UIElementFactoryManager_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new UIElementFactoryManager(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/uifactory/windowcontentfactorymanager.cxx b/framework/source/uifactory/windowcontentfactorymanager.cxx
new file mode 100644
index 000000000..da52775cc
--- /dev/null
+++ b/framework/source/uifactory/windowcontentfactorymanager.cxx
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <uifactory/configurationaccessfactorymanager.hxx>
+#include <helper/mischelper.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/UnknownModuleException.hpp>
+#include <com/sun/star/frame/XModuleManager2.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <comphelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/ref.hxx>
+#include <utility>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using namespace framework;
+
+namespace {
+
+typedef comphelper::WeakComponentImplHelper<
+ css::lang::XServiceInfo,
+ css::lang::XSingleComponentFactory > WindowContentFactoryManager_BASE;
+
+class WindowContentFactoryManager : public WindowContentFactoryManager_BASE
+{
+public:
+ explicit WindowContentFactoryManager( css::uno::Reference< css::uno::XComponentContext> xContext );
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.framework.WindowContentFactoryManager";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.ui.WindowContentFactoryManager"};
+ }
+
+ // XSingleComponentFactory
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithContext( const css::uno::Reference< css::uno::XComponentContext >& Context ) override;
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArgumentsAndContext( const css::uno::Sequence< css::uno::Any >& Arguments, const css::uno::Reference< css::uno::XComponentContext >& Context ) override;
+
+private:
+ virtual void disposing(std::unique_lock<std::mutex>&) override;
+
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ bool m_bConfigRead;
+ rtl::Reference<ConfigurationAccess_FactoryManager> m_pConfigAccess;
+};
+
+WindowContentFactoryManager::WindowContentFactoryManager( uno::Reference< uno::XComponentContext > xContext ) :
+ m_xContext(std::move( xContext )),
+ m_bConfigRead( false ),
+ m_pConfigAccess(
+ new ConfigurationAccess_FactoryManager(
+ m_xContext,
+ "/org.openoffice.Office.UI.WindowContentFactories/Registered/ContentFactories"))
+{}
+
+void WindowContentFactoryManager::disposing(std::unique_lock<std::mutex>&)
+{
+ m_pConfigAccess.clear();
+}
+
+// XSingleComponentFactory
+uno::Reference< uno::XInterface > SAL_CALL WindowContentFactoryManager::createInstanceWithContext(
+ const uno::Reference< uno::XComponentContext >& /*xContext*/ )
+{
+ uno::Reference< uno::XInterface > xWindow;
+ return xWindow;
+}
+
+uno::Reference< uno::XInterface > SAL_CALL WindowContentFactoryManager::createInstanceWithArgumentsAndContext(
+ const uno::Sequence< uno::Any >& Arguments, const uno::Reference< uno::XComponentContext >& Context )
+{
+ uno::Reference< uno::XInterface > xWindow;
+ uno::Reference< frame::XFrame > xFrame;
+ OUString aResourceURL;
+
+ for (auto const & arg : Arguments )
+ {
+ beans::PropertyValue aPropValue;
+ if ( arg >>= aPropValue )
+ {
+ if ( aPropValue.Name == "Frame" )
+ aPropValue.Value >>= xFrame;
+ else if ( aPropValue.Name == "ResourceURL" )
+ aPropValue.Value >>= aResourceURL;
+ }
+ }
+
+ // Determine the module identifier
+ OUString aType;
+ OUString aName;
+ OUString aModuleId;
+ uno::Reference< frame::XModuleManager2 > xModuleManager =
+ frame::ModuleManager::create( m_xContext );
+ try
+ {
+ if ( xFrame.is() && xModuleManager.is() )
+ aModuleId = xModuleManager->identify( uno::Reference< uno::XInterface >( xFrame, uno::UNO_QUERY ) );
+ }
+ catch ( const frame::UnknownModuleException& )
+ {
+ }
+
+ RetrieveTypeNameFromResourceURL( aResourceURL, aType, aName );
+ if ( !aType.isEmpty() &&
+ !aName.isEmpty() &&
+ !aModuleId.isEmpty() )
+ {
+ OUString aImplementationName;
+ uno::Reference< uno::XInterface > xHolder( static_cast<cppu::OWeakObject*>(this), uno::UNO_QUERY );
+
+ // Determine the implementation name of the window content factory dependent on the
+ // module identifier, user interface element type and name
+ { // SAFE
+ std::unique_lock g(m_aMutex);
+ if (m_bDisposed) {
+ throw css::lang::DisposedException(
+ "disposed", static_cast<OWeakObject *>(this));
+ }
+ if ( !m_bConfigRead )
+ {
+ m_bConfigRead = true;
+ m_pConfigAccess->readConfigurationData();
+ }
+ aImplementationName = m_pConfigAccess->getFactorySpecifierFromTypeNameModule( aType, aName, aModuleId );
+ } // SAFE
+
+ if ( !aImplementationName.isEmpty() )
+ {
+ uno::Reference< lang::XMultiServiceFactory > xServiceManager( Context->getServiceManager(), uno::UNO_QUERY );
+ if ( xServiceManager.is() )
+ {
+ uno::Reference< lang::XSingleComponentFactory > xFactory(
+ xServiceManager->createInstance( aImplementationName ), uno::UNO_QUERY );
+ if ( xFactory.is() )
+ {
+ // Be careful: We call external code. Therefore here we have to catch all exceptions
+ try
+ {
+ xWindow = xFactory->createInstanceWithArgumentsAndContext( Arguments, Context );
+ }
+ catch ( uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ }
+ }
+ }
+ }
+
+ // UNSAFE
+ if ( !xWindow.is())
+ {
+ // Fallback: Use internal factory code to create a toolkit dialog as a content window
+ xWindow = createInstanceWithContext(Context);
+ }
+
+ return xWindow;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_framework_WindowContentFactoryManager_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new WindowContentFactoryManager(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/xml/acceleratorconfigurationreader.cxx b/framework/source/xml/acceleratorconfigurationreader.cxx
new file mode 100644
index 000000000..ad6bc7259
--- /dev/null
+++ b/framework/source/xml/acceleratorconfigurationreader.cxx
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <accelerators/keymapping.hxx>
+#include <xml/acceleratorconfigurationreader.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <com/sun/star/awt/KeyEvent.hpp>
+
+namespace framework{
+
+/* Throws a SaxException in case a wrong formatted XML
+ structure was detected.
+
+ This macro combined the given comment with a generic
+ way to find out the XML line (where the error occurred)
+ to format a suitable message.
+
+ @param COMMENT
+ an ascii string, which describe the problem.
+ */
+#define THROW_PARSEEXCEPTION(COMMENT) \
+ { \
+ throw css::xml::sax::SAXException( \
+ implts_getErrorLineString() + COMMENT, \
+ static_cast< css::xml::sax::XDocumentHandler* >(this), \
+ css::uno::Any()); \
+ }
+
+AcceleratorConfigurationReader::AcceleratorConfigurationReader(AcceleratorCache& rContainer)
+ : m_rContainer (rContainer )
+ , m_bInsideAcceleratorList(false )
+ , m_bInsideAcceleratorItem(false )
+{
+}
+
+AcceleratorConfigurationReader::~AcceleratorConfigurationReader()
+{
+}
+
+void SAL_CALL AcceleratorConfigurationReader::startDocument()
+{
+}
+
+void SAL_CALL AcceleratorConfigurationReader::endDocument()
+{
+ // The xml file seems to be corrupted.
+ // Because we found no end-tags ... at least for
+ // one list or item.
+ if (m_bInsideAcceleratorList || m_bInsideAcceleratorItem)
+ {
+ THROW_PARSEEXCEPTION("No matching start or end element 'acceleratorlist' found!")
+ }
+}
+
+void SAL_CALL AcceleratorConfigurationReader::startElement(const OUString& sElement ,
+ const css::uno::Reference< css::xml::sax::XAttributeList >& xAttributeList)
+{
+ EXMLElement eElement = AcceleratorConfigurationReader::implst_classifyElement(sElement);
+
+ // Note: We handle "accel:item" before "accel:acceleratorlist" to perform this operation.
+ // Because an item occurs very often ... a list should occur one times only!
+ if (eElement == E_ELEMENT_ITEM)
+ {
+ if (!m_bInsideAcceleratorList)
+ THROW_PARSEEXCEPTION("An element \"accel:item\" must be embedded into 'accel:acceleratorlist'.")
+ if (m_bInsideAcceleratorItem)
+ THROW_PARSEEXCEPTION("An element \"accel:item\" is not a container.")
+ m_bInsideAcceleratorItem = true;
+
+ OUString sCommand;
+ css::awt::KeyEvent aEvent;
+
+ sal_Int16 c = xAttributeList->getLength();
+ sal_Int16 i = 0;
+ for (i=0; i<c; ++i)
+ {
+ OUString sAttribute = xAttributeList->getNameByIndex(i);
+ OUString sValue = xAttributeList->getValueByIndex(i);
+ EXMLAttribute eAttribute = AcceleratorConfigurationReader::implst_classifyAttribute(sAttribute);
+ switch(eAttribute)
+ {
+ case E_ATTRIBUTE_URL :
+ sCommand = sValue.intern();
+ break;
+
+ case E_ATTRIBUTE_KEYCODE :
+ aEvent.KeyCode = KeyMapping::get().mapIdentifierToCode(sValue);
+ break;
+
+ case E_ATTRIBUTE_MOD_SHIFT :
+ aEvent.Modifiers |= css::awt::KeyModifier::SHIFT;
+ break;
+
+ case E_ATTRIBUTE_MOD_MOD1 :
+ aEvent.Modifiers |= css::awt::KeyModifier::MOD1;
+ break;
+
+ case E_ATTRIBUTE_MOD_MOD2 :
+ aEvent.Modifiers |= css::awt::KeyModifier::MOD2;
+ break;
+
+ case E_ATTRIBUTE_MOD_MOD3 :
+ aEvent.Modifiers |= css::awt::KeyModifier::MOD3;
+ }
+ }
+
+ // validate command and key event.
+ if (
+ sCommand.isEmpty() ||
+ (aEvent.KeyCode == 0 )
+ )
+ {
+ THROW_PARSEEXCEPTION("XML element does not describe a valid accelerator nor a valid command.")
+ }
+
+ // register key event + command inside cache ...
+ // Check for already existing items there.
+ if (!m_rContainer.hasKey(aEvent))
+ m_rContainer.setKeyCommandPair(aEvent, sCommand);
+ else
+ {
+ // Attention: It's not really a reason to throw an exception and kill the office, if the configuration contains
+ // multiple registrations for the same key :-) Show a warning ... and ignore the second item.
+ // THROW_PARSEEXCEPTION("Command is registered for the same key more than once.")
+ SAL_INFO("fwk",
+ "AcceleratorConfigurationReader::startElement(): Double registration detected. Command=\"" <<
+ sCommand <<
+ "\" KeyCode=" <<
+ aEvent.KeyCode <<
+ "Modifiers=" <<
+ aEvent.Modifiers);
+ }
+ }
+
+ if (eElement == E_ELEMENT_ACCELERATORLIST)
+ {
+ if (m_bInsideAcceleratorList)
+ THROW_PARSEEXCEPTION("An element \"accel:acceleratorlist\" cannot be used recursive.")
+ m_bInsideAcceleratorList = true;
+ return;
+ }
+}
+
+void SAL_CALL AcceleratorConfigurationReader::endElement(const OUString& sElement)
+{
+ EXMLElement eElement = AcceleratorConfigurationReader::implst_classifyElement(sElement);
+
+ // Note: We handle "accel:item" before "accel:acceleratorlist" to perform this operation.
+ // Because an item occurs very often ... a list should occur one times only!
+ if (eElement == E_ELEMENT_ITEM)
+ {
+ if (!m_bInsideAcceleratorItem)
+ THROW_PARSEEXCEPTION("Found end element 'accel:item', but no start element.")
+ m_bInsideAcceleratorItem = false;
+ }
+
+ if (eElement == E_ELEMENT_ACCELERATORLIST)
+ {
+ if (!m_bInsideAcceleratorList)
+ THROW_PARSEEXCEPTION("Found end element 'accel:acceleratorlist', but no start element.")
+ m_bInsideAcceleratorList = false;
+ }
+}
+
+void SAL_CALL AcceleratorConfigurationReader::characters(const OUString&)
+{
+}
+
+void SAL_CALL AcceleratorConfigurationReader::ignorableWhitespace(const OUString&)
+{
+}
+
+void SAL_CALL AcceleratorConfigurationReader::processingInstruction(const OUString& /*sTarget*/,
+ const OUString& /*sData*/ )
+{
+}
+
+void SAL_CALL AcceleratorConfigurationReader::setDocumentLocator(const css::uno::Reference< css::xml::sax::XLocator >& xLocator)
+{
+ m_xLocator = xLocator;
+}
+
+AcceleratorConfigurationReader::EXMLElement AcceleratorConfigurationReader::implst_classifyElement(std::u16string_view sElement)
+{
+ AcceleratorConfigurationReader::EXMLElement eElement;
+
+ if (sElement == u"http://openoffice.org/2001/accel^acceleratorlist")
+ eElement = E_ELEMENT_ACCELERATORLIST;
+ else if (sElement == u"http://openoffice.org/2001/accel^item")
+ eElement = E_ELEMENT_ITEM;
+ else
+ throw css::uno::RuntimeException(
+ "Unknown XML element detected!",
+ css::uno::Reference< css::xml::sax::XDocumentHandler >());
+
+ return eElement;
+}
+
+AcceleratorConfigurationReader::EXMLAttribute AcceleratorConfigurationReader::implst_classifyAttribute(std::u16string_view sAttribute)
+{
+ AcceleratorConfigurationReader::EXMLAttribute eAttribute;
+
+ if (sAttribute == u"http://openoffice.org/2001/accel^code")
+ eAttribute = E_ATTRIBUTE_KEYCODE;
+ else if (sAttribute == u"http://openoffice.org/2001/accel^shift")
+ eAttribute = E_ATTRIBUTE_MOD_SHIFT;
+ else if (sAttribute == u"http://openoffice.org/2001/accel^mod1")
+ eAttribute = E_ATTRIBUTE_MOD_MOD1;
+ else if (sAttribute == u"http://openoffice.org/2001/accel^mod2")
+ eAttribute = E_ATTRIBUTE_MOD_MOD2;
+ else if (sAttribute == u"http://openoffice.org/2001/accel^mod3")
+ eAttribute = E_ATTRIBUTE_MOD_MOD3;
+ else if (sAttribute == u"http://www.w3.org/1999/xlink^href")
+ eAttribute = E_ATTRIBUTE_URL;
+ else
+ throw css::uno::RuntimeException(
+ "Unknown XML attribute detected!",
+ css::uno::Reference< css::xml::sax::XDocumentHandler >());
+
+ return eAttribute;
+}
+
+OUString AcceleratorConfigurationReader::implts_getErrorLineString()
+{
+ if (!m_xLocator.is())
+ return "Error during parsing XML. (No further info available ...)";
+
+ return "Error during parsing XML in\nline = " +
+ OUString::number(m_xLocator->getLineNumber()) +
+ "\ncolumn = " +
+ OUString::number(m_xLocator->getColumnNumber()) +
+ ".";
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/xml/acceleratorconfigurationwriter.cxx b/framework/source/xml/acceleratorconfigurationwriter.cxx
new file mode 100644
index 000000000..04e50f5ae
--- /dev/null
+++ b/framework/source/xml/acceleratorconfigurationwriter.cxx
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <accelerators/keymapping.hxx>
+#include <utility>
+#include <xml/acceleratorconfigurationwriter.hxx>
+
+#include <acceleratorconst.h>
+
+#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+
+#include <comphelper/attributelist.hxx>
+#include <rtl/ref.hxx>
+
+namespace framework{
+
+AcceleratorConfigurationWriter::AcceleratorConfigurationWriter(const AcceleratorCache& rContainer,
+ css::uno::Reference< css::xml::sax::XDocumentHandler > xConfig )
+ : m_xConfig (std::move(xConfig ))
+ , m_rContainer (rContainer )
+{
+}
+
+AcceleratorConfigurationWriter::~AcceleratorConfigurationWriter()
+{
+}
+
+void AcceleratorConfigurationWriter::flush()
+{
+ css::uno::Reference< css::xml::sax::XExtendedDocumentHandler > xExtendedCFG(m_xConfig, css::uno::UNO_QUERY_THROW);
+
+ // prepare attribute list
+ rtl::Reference<::comphelper::AttributeList> pAttribs = new ::comphelper::AttributeList;
+
+ pAttribs->AddAttribute(
+ "xmlns:accel", ATTRIBUTE_TYPE_CDATA,
+ "http://openoffice.org/2001/accel");
+ pAttribs->AddAttribute(
+ "xmlns:xlink", ATTRIBUTE_TYPE_CDATA, "http://www.w3.org/1999/xlink");
+
+ // generate xml
+ xExtendedCFG->startDocument();
+
+ xExtendedCFG->unknown(
+ "<!DOCTYPE accel:acceleratorlist PUBLIC \"-//OpenOffice.org//DTD"
+ " OfficeDocument 1.0//EN\" \"accelerator.dtd\">");
+ xExtendedCFG->ignorableWhitespace(OUString());
+
+ xExtendedCFG->startElement(AL_ELEMENT_ACCELERATORLIST, pAttribs);
+ xExtendedCFG->ignorableWhitespace(OUString());
+
+ // TODO think about threadsafe using of cache
+ AcceleratorCache::TKeyList lKeys = m_rContainer.getAllKeys();
+ for (auto const& lKey : lKeys)
+ {
+ const OUString& rCommand = m_rContainer.getCommandByKey(lKey);
+ impl_ts_writeKeyCommandPair(lKey, rCommand, xExtendedCFG);
+ }
+
+ /* TODO write key-command list
+ for (auto const& writeAccelerator : m_aWriteAcceleratorList)
+ WriteAcceleratorItem(writeAccelerator);
+ */
+
+ xExtendedCFG->ignorableWhitespace(OUString());
+ xExtendedCFG->endElement(AL_ELEMENT_ACCELERATORLIST);
+ xExtendedCFG->ignorableWhitespace(OUString());
+ xExtendedCFG->endDocument();
+}
+
+void AcceleratorConfigurationWriter::impl_ts_writeKeyCommandPair(const css::awt::KeyEvent& aKey ,
+ const OUString& sCommand,
+ const css::uno::Reference< css::xml::sax::XDocumentHandler >& xConfig )
+{
+ rtl::Reference<::comphelper::AttributeList> pAttribs = new ::comphelper::AttributeList;
+
+ OUString sKey = KeyMapping::get().mapCodeToIdentifier(aKey.KeyCode);
+ // TODO check if key is empty!
+
+ pAttribs->AddAttribute("accel:code", ATTRIBUTE_TYPE_CDATA, sKey );
+ pAttribs->AddAttribute("xlink:href", ATTRIBUTE_TYPE_CDATA, sCommand);
+
+ if ((aKey.Modifiers & css::awt::KeyModifier::SHIFT) == css::awt::KeyModifier::SHIFT)
+ pAttribs->AddAttribute("accel:shift", ATTRIBUTE_TYPE_CDATA, "true");
+
+ if ((aKey.Modifiers & css::awt::KeyModifier::MOD1) == css::awt::KeyModifier::MOD1)
+ pAttribs->AddAttribute("accel:mod1", ATTRIBUTE_TYPE_CDATA, "true");
+
+ if ((aKey.Modifiers & css::awt::KeyModifier::MOD2) == css::awt::KeyModifier::MOD2)
+ pAttribs->AddAttribute("accel:mod2", ATTRIBUTE_TYPE_CDATA, "true");
+
+ if ((aKey.Modifiers & css::awt::KeyModifier::MOD3) == css::awt::KeyModifier::MOD3)
+ pAttribs->AddAttribute("accel:mod3", ATTRIBUTE_TYPE_CDATA, "true");
+
+ xConfig->ignorableWhitespace(OUString());
+ xConfig->startElement(AL_ELEMENT_ITEM, pAttribs);
+ xConfig->ignorableWhitespace(OUString());
+ xConfig->endElement(AL_ELEMENT_ITEM);
+ xConfig->ignorableWhitespace(OUString());
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/xml/imagesconfiguration.cxx b/framework/source/xml/imagesconfiguration.cxx
new file mode 100644
index 000000000..6b4773c21
--- /dev/null
+++ b/framework/source/xml/imagesconfiguration.cxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xml/imagesconfiguration.hxx>
+
+#include <xml/imagesdocumenthandler.hxx>
+#include <xml/saxnamespacefilter.hxx>
+
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::io;
+
+namespace framework
+{
+bool ImagesConfiguration::LoadImages(
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::io::XInputStream>& rInputStream, ImageItemDescriptorList& rItems)
+{
+ Reference<XParser> xParser = Parser::create(rxContext);
+
+ // connect stream to input stream to the parser
+ InputSource aInputSource;
+
+ aInputSource.aInputStream = rInputStream;
+
+ // create namespace filter and set document handler inside to support xml namespaces
+ Reference<XDocumentHandler> xDocHandler(new OReadImagesDocumentHandler(rItems));
+ Reference<XDocumentHandler> xFilter(new SaxNamespaceFilter(xDocHandler));
+
+ // connect parser and filter
+ xParser->setDocumentHandler(xFilter);
+
+ try
+ {
+ xParser->parseStream(aInputSource);
+ return true;
+ }
+ catch (const RuntimeException&)
+ {
+ return false;
+ }
+ catch (const SAXException&)
+ {
+ return false;
+ }
+ catch (const css::io::IOException&)
+ {
+ return false;
+ }
+}
+
+bool ImagesConfiguration::StoreImages(
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::io::XOutputStream>& rOutputStream,
+ const ImageItemDescriptorList& rItems)
+{
+ Reference<XWriter> xWriter = Writer::create(rxContext);
+ xWriter->setOutputStream(rOutputStream);
+
+ try
+ {
+ OWriteImagesDocumentHandler aWriteImagesDocumentHandler(rItems, xWriter);
+ aWriteImagesDocumentHandler.WriteImagesDocument();
+ return true;
+ }
+ catch (const RuntimeException&)
+ {
+ return false;
+ }
+ catch (const SAXException&)
+ {
+ return false;
+ }
+ catch (const css::io::IOException&)
+ {
+ return false;
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/xml/imagesdocumenthandler.cxx b/framework/source/xml/imagesdocumenthandler.cxx
new file mode 100644
index 000000000..ebdd5366f
--- /dev/null
+++ b/framework/source/xml/imagesdocumenthandler.cxx
@@ -0,0 +1,372 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xml/imagesdocumenthandler.hxx>
+
+#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <comphelper/attributelist.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+
+#define ELEMENT_IMAGECONTAINER "imagescontainer"
+#define ELEMENT_IMAGES "images"
+#define ELEMENT_ENTRY "entry"
+#define ELEMENT_EXTERNALIMAGES "externalimages"
+#define ELEMENT_EXTERNALENTRY "externalentry"
+
+constexpr OUStringLiteral ELEMENT_NS_IMAGESCONTAINER = u"image:imagescontainer";
+constexpr OUStringLiteral ELEMENT_NS_IMAGES = u"image:images";
+constexpr OUStringLiteral ELEMENT_NS_ENTRY = u"image:entry";
+
+#define ATTRIBUTE_HREF "href"
+#define ATTRIBUTE_MASKCOLOR "maskcolor"
+#define ATTRIBUTE_COMMAND "command"
+#define ATTRIBUTE_BITMAPINDEX "bitmap-index"
+#define ATTRIBUTE_MASKURL "maskurl"
+#define ATTRIBUTE_MASKMODE "maskmode"
+#define ATTRIBUTE_HIGHCONTRASTURL "highcontrasturl"
+#define ATTRIBUTE_HIGHCONTRASTMASKURL "highcontrastmaskurl"
+constexpr OUStringLiteral ATTRIBUTE_TYPE_CDATA = u"CDATA";
+
+constexpr OUStringLiteral ATTRIBUTE_XMLNS_IMAGE = u"xmlns:image";
+constexpr OUStringLiteral ATTRIBUTE_XMLNS_XLINK = u"xmlns:xlink";
+
+constexpr OUStringLiteral ATTRIBUTE_XLINK_TYPE = u"xlink:type";
+constexpr OUStringLiteral ATTRIBUTE_XLINK_TYPE_VALUE = u"simple";
+
+constexpr OUStringLiteral XMLNS_IMAGE = u"http://openoffice.org/2001/image";
+constexpr OUStringLiteral XMLNS_XLINK = u"http://www.w3.org/1999/xlink";
+constexpr OUStringLiteral XMLNS_IMAGE_PREFIX = u"image:";
+
+constexpr OUStringLiteral XMLNS_FILTER_SEPARATOR = u"^";
+
+constexpr OUStringLiteral IMAGES_DOCTYPE = u"<!DOCTYPE image:imagecontainer PUBLIC \"-//OpenOffice.org//DTD OfficeDocument 1.0//EN\" \"image.dtd\">";
+
+namespace framework
+{
+
+namespace {
+
+struct ImageXMLEntryProperty
+{
+ OReadImagesDocumentHandler::Image_XML_Namespace nNamespace;
+ char aEntryName[20];
+};
+
+}
+
+ImageXMLEntryProperty const ImagesEntries[OReadImagesDocumentHandler::IMG_XML_ENTRY_COUNT] =
+{
+ { OReadImagesDocumentHandler::IMG_NS_IMAGE, ELEMENT_IMAGECONTAINER },
+ { OReadImagesDocumentHandler::IMG_NS_IMAGE, ELEMENT_IMAGES },
+ { OReadImagesDocumentHandler::IMG_NS_IMAGE, ELEMENT_ENTRY },
+ { OReadImagesDocumentHandler::IMG_NS_IMAGE, ELEMENT_EXTERNALIMAGES },
+ { OReadImagesDocumentHandler::IMG_NS_IMAGE, ELEMENT_EXTERNALENTRY },
+ { OReadImagesDocumentHandler::IMG_NS_XLINK, ATTRIBUTE_HREF },
+ { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_MASKCOLOR },
+ { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_COMMAND },
+ { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_BITMAPINDEX },
+ { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_MASKURL },
+ { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_MASKMODE },
+ { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_HIGHCONTRASTURL },
+ { OReadImagesDocumentHandler::IMG_NS_IMAGE, ATTRIBUTE_HIGHCONTRASTMASKURL }
+};
+
+OReadImagesDocumentHandler::OReadImagesDocumentHandler( ImageItemDescriptorList& rItems ) :
+ m_rImageList( rItems )
+{
+ // create hash map to speed up lookup
+ for ( int i = 0; i < IMG_XML_ENTRY_COUNT; i++ )
+ {
+ OUStringBuffer temp( 20 );
+
+ if ( ImagesEntries[i].nNamespace == IMG_NS_IMAGE )
+ temp.append( XMLNS_IMAGE );
+ else
+ temp.append( XMLNS_XLINK );
+
+ temp.append( XMLNS_FILTER_SEPARATOR );
+ temp.appendAscii( ImagesEntries[i].aEntryName );
+ m_aImageMap.emplace( temp.makeStringAndClear(), static_cast<Image_XML_Entry>(i) );
+ }
+
+ // reset states
+ m_bImageContainerStartFound = false;
+ m_bImageContainerEndFound = false;
+ m_bImagesStartFound = false;
+}
+
+OReadImagesDocumentHandler::~OReadImagesDocumentHandler()
+{
+}
+
+// XDocumentHandler
+void SAL_CALL OReadImagesDocumentHandler::startDocument()
+{
+}
+
+void SAL_CALL OReadImagesDocumentHandler::endDocument()
+{
+ if (m_bImageContainerStartFound != m_bImageContainerEndFound)
+ {
+ OUString aErrorMessage = getErrorLineString() + "No matching start or end element 'image:imagecontainer' found!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+}
+
+void SAL_CALL OReadImagesDocumentHandler::startElement(
+ const OUString& aName, const Reference< XAttributeList > &xAttribs )
+{
+ ImageHashMap::const_iterator pImageEntry = m_aImageMap.find( aName );
+ if ( pImageEntry == m_aImageMap.end() )
+ return;
+
+ switch ( pImageEntry->second )
+ {
+ case IMG_ELEMENT_IMAGECONTAINER:
+ {
+ // image:imagecontainer element (container element for all further image elements)
+ if ( m_bImageContainerStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element 'image:imagecontainer' cannot be embedded into 'image:imagecontainer'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bImageContainerStartFound = true;
+ }
+ break;
+
+ case IMG_ELEMENT_IMAGES:
+ {
+ if ( !m_bImageContainerStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element 'image:images' must be embedded into element 'image:imagecontainer'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ if ( m_bImagesStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element 'image:images' cannot be embedded into 'image:images'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bImagesStartFound = true;
+ }
+ break;
+
+ case IMG_ELEMENT_ENTRY:
+ {
+ // Check that image:entry is embedded into image:images!
+ if ( !m_bImagesStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element 'image:entry' must be embedded into element 'image:images'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ // Create new image item descriptor
+ ImageItemDescriptor aItem;
+
+ // Read attributes for this image definition
+ for ( sal_Int16 n = 0; n < xAttribs->getLength(); n++ )
+ {
+ pImageEntry = m_aImageMap.find( xAttribs->getNameByIndex( n ) );
+ if ( pImageEntry != m_aImageMap.end() )
+ {
+ switch ( pImageEntry->second )
+ {
+ case IMG_ATTRIBUTE_COMMAND:
+ {
+ aItem.aCommandURL = xAttribs->getValueByIndex( n );
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ // Check required attribute "command"
+ if ( aItem.aCommandURL.isEmpty() )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Required attribute 'image:command' must have a value!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_rImageList.push_back( aItem );
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void SAL_CALL OReadImagesDocumentHandler::endElement(const OUString& aName)
+{
+ ImageHashMap::const_iterator pImageEntry = m_aImageMap.find( aName );
+ if ( pImageEntry == m_aImageMap.end() )
+ return;
+
+ switch ( pImageEntry->second )
+ {
+ case IMG_ELEMENT_IMAGECONTAINER:
+ {
+ m_bImageContainerEndFound = true;
+ }
+ break;
+
+ case IMG_ELEMENT_IMAGES:
+ {
+ m_bImagesStartFound = false;
+ }
+ break;
+
+ default: break;
+ }
+}
+
+void SAL_CALL OReadImagesDocumentHandler::characters(const OUString&)
+{
+}
+
+void SAL_CALL OReadImagesDocumentHandler::ignorableWhitespace(const OUString&)
+{
+}
+
+void SAL_CALL OReadImagesDocumentHandler::processingInstruction(
+ const OUString& /*aTarget*/, const OUString& /*aData*/ )
+{
+}
+
+void SAL_CALL OReadImagesDocumentHandler::setDocumentLocator(
+ const Reference< XLocator > &xLocator)
+{
+ m_xLocator = xLocator;
+}
+
+OUString OReadImagesDocumentHandler::getErrorLineString()
+{
+ if ( m_xLocator.is() )
+ {
+ return "Line: " +
+ OUString::number(m_xLocator->getLineNumber()) +
+ " - ";
+ }
+ else
+ return OUString();
+}
+
+// OWriteImagesDocumentHandler
+
+OWriteImagesDocumentHandler::OWriteImagesDocumentHandler(
+ const ImageItemDescriptorList& rItems,
+ Reference< XDocumentHandler > const & rWriteDocumentHandler ) :
+ m_rImageItemList( rItems ),
+ m_xWriteDocumentHandler( rWriteDocumentHandler )
+{
+ m_xEmptyList = new ::comphelper::AttributeList;
+ m_aAttributeType = ATTRIBUTE_TYPE_CDATA;
+ m_aXMLImageNS = XMLNS_IMAGE_PREFIX;
+ m_aAttributeXlinkType = ATTRIBUTE_XLINK_TYPE;
+ m_aAttributeValueSimple = ATTRIBUTE_XLINK_TYPE_VALUE;
+}
+
+OWriteImagesDocumentHandler::~OWriteImagesDocumentHandler()
+{
+}
+
+void OWriteImagesDocumentHandler::WriteImagesDocument()
+{
+ m_xWriteDocumentHandler->startDocument();
+
+ // write DOCTYPE line!
+ Reference< XExtendedDocumentHandler > xExtendedDocHandler( m_xWriteDocumentHandler, UNO_QUERY );
+ if ( xExtendedDocHandler.is() )
+ {
+ xExtendedDocHandler->unknown( IMAGES_DOCTYPE );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ }
+
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ pList->AddAttribute( ATTRIBUTE_XMLNS_IMAGE,
+ m_aAttributeType,
+ XMLNS_IMAGE );
+
+ pList->AddAttribute( ATTRIBUTE_XMLNS_XLINK,
+ m_aAttributeType,
+ XMLNS_XLINK );
+
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_IMAGESCONTAINER, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+
+ WriteImageList( &m_rImageItemList );
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_IMAGESCONTAINER );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endDocument();
+}
+
+// protected member functions
+
+void OWriteImagesDocumentHandler::WriteImageList( const ImageItemDescriptorList* pImageList )
+{
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ // save required attributes
+ pList->AddAttribute( m_aAttributeXlinkType,
+ m_aAttributeType,
+ m_aAttributeValueSimple );
+
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_IMAGES, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+
+ for (const ImageItemDescriptor & i : *pImageList)
+ WriteImage( &i );
+
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_IMAGES );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+}
+
+void OWriteImagesDocumentHandler::WriteImage( const ImageItemDescriptor* pImage )
+{
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ pList->AddAttribute( m_aXMLImageNS + ATTRIBUTE_COMMAND,
+ m_aAttributeType,
+ pImage->aCommandURL );
+
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_ENTRY, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_ENTRY );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */