summaryrefslogtreecommitdiffstats
path: root/framework/source/accelerators
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/accelerators
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
8 files changed, 3305 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: */