From 940b4d1848e8c70ab7642901a68594e8016caffc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 18:51:28 +0200 Subject: Adding upstream version 1:7.0.4. Signed-off-by: Daniel Baumann --- .../accelerators/acceleratorconfiguration.cxx | 1330 ++++++++++++++++++++ 1 file changed, 1330 insertions(+) create mode 100644 framework/source/accelerators/acceleratorconfiguration.cxx (limited to 'framework/source/accelerators/acceleratorconfiguration.cxx') diff --git a/framework/source/accelerators/acceleratorconfiguration.cxx b/framework/source/accelerators/acceleratorconfiguration.cxx new file mode 100644 index 000000000..6daa18b33 --- /dev/null +++ b/framework/source/accelerators/acceleratorconfiguration.cxx @@ -0,0 +1,1330 @@ +/* -*- 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 + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRESET_DEFAULT "default" +#define TARGET_CURRENT "current" + +namespace framework +{ + const char CFG_ENTRY_SECONDARY[] = "SecondaryKeys"; + const char CFG_PROP_COMMAND[] = "Command"; + + static OUString lcl_getKeyString(const css::awt::KeyEvent& aKeyEvent) + { + const sal_Int32 nBeginIndex = 4; // "KEY_" is the prefix of an identifier... + OUStringBuffer sKeyBuffer((KeyMapping::get().mapCodeToIdentifier(aKeyEvent.KeyCode)).copy(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(); + + for (i=0; i(this), + static_cast(i)); + + if (!rCache.hasCommand(rCommand)) + continue; + + AcceleratorCache::TKeyList lKeys = rCache.getKeysByCommand(rCommand); + if ( lKeys.empty() ) + continue; + + css::uno::Any& rAny = lPreferredOnes[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! + AcceleratorConfigurationReader* pReader = new AcceleratorConfigurationReader(m_aReadCache); + css::uno::Reference< css::xml::sax::XDocumentHandler > xReader (static_cast< ::cppu::OWeakObject* >(pReader), css::uno::UNO_QUERY_THROW); + SaxNamespaceFilter* pFilter = new SaxNamespaceFilter(xReader); + css::uno::Reference< css::xml::sax::XDocumentHandler > xFilter (static_cast< ::cppu::OWeakObject* >(pFilter), css::uno::UNO_QUERY_THROW); + + // connect parser, filter and stream + css::uno::Reference< css::xml::sax::XParser > xParser = css::xml::sax::Parser::create(xContext); + xParser->setDocumentHandler(xFilter); + + 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(const css::uno::Reference< css::uno::XComponentContext >& xContext) + : m_xContext (xContext ) +{ + const OUString CFG_ENTRY_ACCELERATORS("org.openoffice.Office.Accelerators"); + m_xCfg.set( + ::comphelper::ConfigurationHelper::openConfig( m_xContext, CFG_ENTRY_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); + + for (i=0; i(this), + static_cast(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 = lPreferredOnes[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; + + 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']/Key['']/Command[]" 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(); + const OUString sDefaultLocale("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; igetByName(sKey) >>= xKey; + xKey->getByName(CFG_PROP_COMMAND) >>= xCommand; + + css::uno::Sequence< OUString > lLocales = xCommand->getElementNames(); + sal_Int32 nLocales = lLocales.getLength(); + ::std::vector< OUString > aLocales; + aLocales.reserve(nLocales); + for (sal_Int32 j = 0; j < nLocales; ++j) + aLocales.push_back(lLocales[j]); + + 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; + OUString sKeyCommand = sKey.getToken(0, '_', nIndex); + aKeyEvent.KeyCode = KeyMapping::get().mapIdentifierToCode("KEY_" + sKeyCommand); + + css::uno::Sequence< OUString > sToken(4); + const sal_Int32 nToken = 4; + bool bValid = true; + sal_Int32 k; + for (k=0; kgetAllKeys(); + + 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::makeAny(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::makeAny(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::makeAny(sCommand)); + else + xCommand->replaceByName(sLocale, css::uno::makeAny(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, const OUString& 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; + OUString sKeyIdentifier; + + sal_Int32 nIndex = 0; + sKeyIdentifier = sKey.getToken(0, '_', nIndex); + aKeyEvent.KeyCode = KeyMapping::get().mapIdentifierToCode("KEY_"+sKeyIdentifier); + + css::uno::Sequence< OUString > sToken(3); + const sal_Int32 nToken = 3; + for (sal_Int32 i=0; i 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: */ -- cgit v1.2.3