summaryrefslogtreecommitdiffstats
path: root/cui/source/customize
diff options
context:
space:
mode:
Diffstat (limited to 'cui/source/customize')
-rw-r--r--cui/source/customize/CommandCategoryListBox.cxx607
-rw-r--r--cui/source/customize/CustomNotebookbarGenerator.cxx278
-rw-r--r--cui/source/customize/SvxConfigPageHelper.cxx424
-rw-r--r--cui/source/customize/SvxMenuConfigPage.cxx595
-rw-r--r--cui/source/customize/SvxNotebookbarConfigPage.cxx543
-rw-r--r--cui/source/customize/SvxToolbarConfigPage.cxx929
-rw-r--r--cui/source/customize/acccfg.cxx1600
-rw-r--r--cui/source/customize/cfg.cxx3221
-rw-r--r--cui/source/customize/cfgutil.cxx1270
-rw-r--r--cui/source/customize/eventdlg.cxx159
-rw-r--r--cui/source/customize/eventdlg.hxx52
-rw-r--r--cui/source/customize/macropg.cxx663
-rw-r--r--cui/source/customize/macropg_impl.hxx55
13 files changed, 10396 insertions, 0 deletions
diff --git a/cui/source/customize/CommandCategoryListBox.cxx b/cui/source/customize/CommandCategoryListBox.cxx
new file mode 100644
index 000000000..f6b34076f
--- /dev/null
+++ b/cui/source/customize/CommandCategoryListBox.cxx
@@ -0,0 +1,607 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * 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 <CommandCategoryListBox.hxx>
+
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XDispatchInformationProvider.hpp>
+#include <com/sun/star/frame/theUICommandDescription.hpp>
+#include <com/sun/star/ui/theUICategoryDescription.hpp>
+#include <com/sun/star/script/browse/XBrowseNode.hpp>
+#include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
+#include <com/sun/star/script/browse/theBrowseNodeFactory.hpp>
+#include <com/sun/star/script/browse/BrowseNodeFactoryViewTypes.hpp>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+// include search util
+#include <com/sun/star/util/SearchFlags.hpp>
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+#include <tools/diagnose_ex.h>
+#include <unotools/textsearch.hxx>
+
+#include <dialmgr.hxx>
+#include <strings.hrc>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/SetFlagContextHelper.hxx>
+#include <comphelper/string.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nutil/searchopt.hxx>
+#include <sal/log.hxx>
+
+#include <cfg.hxx> //for SaveInData
+
+CommandCategoryListBox::CommandCategoryListBox(std::unique_ptr<weld::ComboBox> xControl)
+ : pStylesInfo(nullptr)
+ , m_xControl(std::move(xControl))
+{
+ //Initialize search util
+ m_searchOptions.AlgorithmType2 = css::util::SearchAlgorithms2::ABSOLUTE;
+ m_searchOptions.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
+ m_searchOptions.searchFlag |= (css::util::SearchFlags::REG_NOT_BEGINOFLINE
+ | css::util::SearchFlags::REG_NOT_ENDOFLINE);
+}
+
+CommandCategoryListBox::~CommandCategoryListBox() { ClearAll(); }
+
+void CommandCategoryListBox::ClearAll()
+{
+ // Clear objects from m_aGroupInfo vector to avoid memory leak
+ for (const auto& It : m_aGroupInfo)
+ {
+ if (It->nKind == SfxCfgKind::GROUP_STYLES && It->pObject)
+ {
+ SfxStyleInfo_Impl* pStyle = static_cast<SfxStyleInfo_Impl*>(It->pObject);
+ delete pStyle;
+ }
+ else if (It->nKind == SfxCfgKind::FUNCTION_SCRIPT && It->pObject)
+ {
+ OUString* pScriptURI = static_cast<OUString*>(It->pObject);
+ delete pScriptURI;
+ }
+ else if (It->nKind == SfxCfgKind::GROUP_SCRIPTCONTAINER && It->pObject)
+ {
+ css::uno::XInterface* xi = static_cast<css::uno::XInterface*>(It->pObject);
+ if (xi != nullptr)
+ {
+ xi->release();
+ }
+ }
+ }
+
+ m_aGroupInfo.clear();
+ m_xControl->clear();
+}
+
+void CommandCategoryListBox::Init(const css::uno::Reference<css::uno::XComponentContext>& xContext,
+ const css::uno::Reference<css::frame::XFrame>& xFrame,
+ const OUString& sModuleLongName)
+{
+ // User will not see incomplete UI
+ m_xControl->freeze();
+ ClearAll();
+
+ m_xContext = xContext;
+ m_xFrame = xFrame;
+
+ m_sModuleLongName = sModuleLongName;
+ m_xGlobalCategoryInfo = css::ui::theUICategoryDescription::get(m_xContext);
+ m_xModuleCategoryInfo.set(m_xGlobalCategoryInfo->getByName(m_sModuleLongName),
+ css::uno::UNO_QUERY_THROW);
+ m_xUICmdDescription = css::frame::theUICommandDescription::get(m_xContext);
+
+ // Support style commands
+ css::uno::Reference<css::frame::XController> xController;
+ css::uno::Reference<css::frame::XModel> xModel;
+ if (xFrame.is())
+ xController = xFrame->getController();
+ if (xController.is())
+ xModel = xController->getModel();
+
+ m_aStylesInfo.init(sModuleLongName, xModel);
+ SetStylesInfo(&m_aStylesInfo);
+
+ try
+ {
+ css::uno::Reference<css::frame::XDispatchInformationProvider> xProvider(
+ m_xFrame, css::uno::UNO_QUERY_THROW);
+ css::uno::Sequence<sal_Int16> lGroups = xProvider->getSupportedCommandGroups();
+
+ sal_Int32 nGroupsLength = lGroups.getLength();
+
+ if (nGroupsLength > 0)
+ {
+ // Add the category of "All commands"
+ m_aGroupInfo.push_back(
+ std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_ALLFUNCTIONS, 0));
+ m_xControl->append(weld::toId(m_aGroupInfo.back().get()),
+ CuiResId(RID_CUISTR_ALLFUNCTIONS));
+ }
+
+ // Separate the "All commands"category from the actual categories
+ m_xControl->append_separator("");
+
+ typedef std::pair<OUString, sal_Int16> str_id;
+ std::vector<str_id> aCategories;
+
+ // Add the actual categories
+ for (sal_Int32 i = 0; i < nGroupsLength; ++i)
+ {
+ sal_Int16 nGroupID = lGroups[i];
+ OUString sGroupID = OUString::number(nGroupID);
+ OUString sGroupName;
+
+ try
+ {
+ m_xModuleCategoryInfo->getByName(sGroupID) >>= sGroupName;
+ if (sGroupName.isEmpty())
+ continue;
+ }
+ catch (const css::container::NoSuchElementException&)
+ {
+ continue;
+ }
+ aCategories.emplace_back(std::make_pair(sGroupName, nGroupID));
+ }
+
+ auto const sort = comphelper::string::NaturalStringSorter(
+ comphelper::getProcessComponentContext(),
+ Application::GetSettings().GetUILanguageTag().getLocale());
+
+ std::sort(aCategories.begin(), aCategories.end(),
+ [&sort](const str_id& a, const str_id& b) {
+ return sort.compare(a.first, b.first) < 0;
+ });
+
+ // Add the actual categories
+ for (const auto& a : aCategories)
+ {
+ const OUString& rGroupName = a.first;
+ sal_Int16 nGroupID = a.second;
+ m_aGroupInfo.push_back(
+ std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_FUNCTION, nGroupID));
+ m_xControl->append(weld::toId(m_aGroupInfo.back().get()), rGroupName);
+ }
+
+ // Separate regular commands from styles and macros
+ m_xControl->append_separator("");
+
+ // Add macros category
+ m_aGroupInfo.push_back(
+ std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_SCRIPTCONTAINER, 0, nullptr));
+ m_xControl->append(weld::toId(m_aGroupInfo.back().get()), CuiResId(RID_CUISTR_MACROS));
+
+ // Add styles category
+ //TODO: last param should contain user data?
+ m_aGroupInfo.push_back(
+ std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_STYLES, 0, nullptr));
+ m_xControl->append(weld::toId(m_aGroupInfo.back().get()),
+ CuiResId(RID_CUISTR_GROUP_STYLES));
+ }
+ catch (const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ // Reveal the updated UI to user
+ m_xControl->thaw();
+ m_xControl->set_active(0);
+}
+
+void CommandCategoryListBox::FillFunctionsList(
+ const css::uno::Sequence<css::frame::DispatchInformation>& xCommands,
+ CuiConfigFunctionListBox* pFunctionListBox, const OUString& filterTerm,
+ SaveInData* pCurrentSaveInData)
+{
+ // Setup search filter parameters
+ m_searchOptions.searchString = filterTerm;
+ utl::TextSearch textSearch(m_searchOptions);
+ const bool bInExperimentalMode = officecfg::Office::Common::Misc::ExperimentalMode::get();
+
+ for (const auto& rInfo : xCommands)
+ {
+ auto aProperties
+ = vcl::CommandInfoProvider::GetCommandProperties(rInfo.Command, m_sModuleLongName);
+
+ OUString sUIName = getCommandName(rInfo.Command);
+ OUString sLabel = vcl::CommandInfoProvider::GetLabelForCommand(aProperties);
+ OUString sTooltipLabel
+ = vcl::CommandInfoProvider::GetTooltipForCommand(rInfo.Command, aProperties, m_xFrame);
+ OUString sPopupLabel = (vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties))
+ .replaceFirst("~", "");
+ bool bIsExperimental
+ = vcl::CommandInfoProvider::IsExperimental(rInfo.Command, m_sModuleLongName);
+
+ // Hide experimental commands when not in experimental mode
+ bool bHideExperimental = bIsExperimental && !bInExperimentalMode;
+
+ // Apply the search filter
+ if (bHideExperimental
+ || (!filterTerm.isEmpty() && !textSearch.searchForward(sUIName)
+ && !textSearch.searchForward(sLabel) && !textSearch.searchForward(sTooltipLabel)
+ && !textSearch.searchForward(sPopupLabel)))
+ {
+ continue;
+ }
+
+ css::uno::Reference<css::graphic::XGraphic> xImage;
+ if (pCurrentSaveInData)
+ xImage = pCurrentSaveInData->GetImage(rInfo.Command);
+
+ m_aGroupInfo.push_back(std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::FUNCTION_SLOT, 0));
+ SfxGroupInfo_Impl* pGrpInfo = m_aGroupInfo.back().get();
+ pGrpInfo->sCommand = rInfo.Command;
+ pGrpInfo->sLabel = sUIName;
+ pGrpInfo->sTooltip = sTooltipLabel;
+ pFunctionListBox->append(weld::toId(m_aGroupInfo.back().get()), sUIName, xImage);
+ }
+}
+
+OUString CommandCategoryListBox::getCommandName(const OUString& sCommand)
+{
+ OUString sUIName;
+ try
+ {
+ css::uno::Reference<css::container::XNameAccess> xModuleConf;
+ m_xUICmdDescription->getByName(m_sModuleLongName) >>= xModuleConf;
+ if (xModuleConf.is())
+ {
+ ::comphelper::SequenceAsHashMap lProps(xModuleConf->getByName(sCommand));
+ sUIName = lProps.getUnpackedValueOrDefault("Name", OUString());
+ }
+ }
+ catch (const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (css::uno::Exception&)
+ {
+ sUIName.clear();
+ }
+
+ // fallback for missing UINames !?
+ if (sUIName.isEmpty())
+ {
+ sUIName = sCommand;
+ }
+
+ return sUIName;
+}
+
+void CommandCategoryListBox::categorySelected(CuiConfigFunctionListBox* pFunctionListBox,
+ const OUString& filterTerm,
+ SaveInData* pCurrentSaveInData)
+{
+ SfxGroupInfo_Impl* pInfo = weld::fromId<SfxGroupInfo_Impl*>(m_xControl->get_active_id());
+ std::vector<std::unique_ptr<weld::TreeIter>> aNodesToExpand;
+ pFunctionListBox->freeze();
+ pFunctionListBox->ClearAll();
+
+ switch (pInfo->nKind)
+ {
+ case SfxCfgKind::GROUP_ALLFUNCTIONS:
+ {
+ css::uno::Reference<css::frame::XDispatchInformationProvider> xProvider(
+ m_xFrame, css::uno::UNO_QUERY);
+ sal_Int32 nEntryCount = m_xControl->get_count();
+
+ for (sal_Int32 nCurPos = 0; nCurPos < nEntryCount; ++nCurPos)
+ {
+ SfxGroupInfo_Impl* pCurrentInfo
+ = weld::fromId<SfxGroupInfo_Impl*>(m_xControl->get_id(nCurPos));
+
+ if (!pCurrentInfo) //separator
+ continue;
+
+ if (pCurrentInfo->nKind == SfxCfgKind::GROUP_FUNCTION)
+ {
+ css::uno::Sequence<css::frame::DispatchInformation> lCommands;
+ try
+ {
+ lCommands = xProvider->getConfigurableDispatchInformation(
+ pCurrentInfo->nUniqueID);
+ FillFunctionsList(lCommands, pFunctionListBox, filterTerm,
+ pCurrentSaveInData);
+ }
+ catch (css::container::NoSuchElementException&)
+ {
+ }
+ }
+ }
+
+ break;
+ }
+ case SfxCfgKind::GROUP_FUNCTION:
+ {
+ sal_uInt16 nGroup = pInfo->nUniqueID;
+ css::uno::Reference<css::frame::XDispatchInformationProvider> xProvider(
+ m_xFrame, css::uno::UNO_QUERY_THROW);
+ css::uno::Sequence<css::frame::DispatchInformation> lCommands
+ = xProvider->getConfigurableDispatchInformation(nGroup);
+ FillFunctionsList(lCommands, pFunctionListBox, filterTerm, pCurrentSaveInData);
+ break;
+ }
+ case SfxCfgKind::GROUP_SCRIPTCONTAINER: //Macros
+ {
+ SAL_INFO("cui.customize", "** ** About to initialise SF Scripts");
+ // Add Scripting Framework entries
+ css::uno::Reference<css::script::browse::XBrowseNode> rootNode;
+ try
+ {
+ css::uno::Reference<css::script::browse::XBrowseNodeFactory> xFac
+ = css::script::browse::theBrowseNodeFactory::get(m_xContext);
+ rootNode.set(xFac->createView(
+ css::script::browse::BrowseNodeFactoryViewTypes::MACROSELECTOR));
+ }
+ catch (css::uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION(
+ "cui.customize",
+ "Caught some exception whilst retrieving browse nodes from factory");
+ // TODO exception handling
+ }
+
+ if (rootNode.is() && rootNode->hasChildNodes())
+ {
+ //We call acquire on the XBrowseNode so that it does not
+ //get autodestructed and become invalid when accessed later.
+ rootNode->acquire();
+
+ m_aGroupInfo.push_back(std::make_unique<SfxGroupInfo_Impl>(
+ SfxCfgKind::GROUP_SCRIPTCONTAINER, 0, static_cast<void*>(rootNode.get())));
+
+ // Add main macro groups
+ const css::uno::Sequence<css::uno::Reference<css::script::browse::XBrowseNode>>
+ aChildNodes = rootNode->getChildNodes();
+ for (auto const& childGroup : aChildNodes)
+ {
+ childGroup->acquire();
+
+ if (childGroup->hasChildNodes())
+ {
+ OUString sUIName;
+ if (childGroup->getName() == "user")
+ {
+ sUIName = CuiResId(RID_CUISTR_MYMACROS);
+ }
+ else if (childGroup->getName() == "share")
+ {
+ sUIName = CuiResId(RID_CUISTR_PRODMACROS);
+ }
+ else
+ {
+ sUIName = childGroup->getName();
+ }
+
+ if (sUIName.isEmpty())
+ {
+ continue;
+ }
+
+ m_aGroupInfo.push_back(std::make_unique<SfxGroupInfo_Impl>(
+ SfxCfgKind::GROUP_SCRIPTCONTAINER, 0));
+ std::unique_ptr<weld::TreeIter> xMacroGroup(pFunctionListBox->tree_append(
+ weld::toId(m_aGroupInfo.back().get()), sUIName));
+
+ {
+ // tdf#128010: Do not nag user asking to enable JRE: if it's disabled,
+ // simply don't show relevant entries (user chose to not use JRE)
+ css::uno::ContextLayer layer(
+ comphelper::NoEnableJavaInteractionContext());
+ //Add the children and the grand children
+ addChildren(xMacroGroup.get(), childGroup, pFunctionListBox, filterTerm,
+ pCurrentSaveInData, aNodesToExpand);
+ }
+
+ // Remove the main group if empty
+ if (!pFunctionListBox->iter_has_child(*xMacroGroup))
+ {
+ pFunctionListBox->remove(*xMacroGroup);
+ }
+ else if (!filterTerm.isEmpty())
+ {
+ aNodesToExpand.emplace_back(std::move(xMacroGroup));
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ case SfxCfgKind::GROUP_STYLES:
+ {
+ const std::vector<SfxStyleInfo_Impl> lStyleFamilies = pStylesInfo->getStyleFamilies();
+
+ for (const auto& pIt : lStyleFamilies)
+ {
+ if (pIt.sLabel.isEmpty())
+ {
+ continue;
+ }
+
+ m_aGroupInfo.push_back(
+ std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_STYLES, 0));
+ // pIt.sLabel is Name of the style family
+ std::unique_ptr<weld::TreeIter> xFuncEntry(pFunctionListBox->tree_append(
+ weld::toId(m_aGroupInfo.back().get()), pIt.sLabel));
+
+ const std::vector<SfxStyleInfo_Impl> lStyles = pStylesInfo->getStyles(pIt.sFamily);
+
+ // Setup search filter parameters
+ m_searchOptions.searchString = filterTerm;
+ utl::TextSearch textSearch(m_searchOptions);
+
+ // Insert children (styles)
+ for (const auto& pStyleIt : lStyles)
+ {
+ OUString sUIName = pStyleIt.sLabel;
+ sal_Int32 aStartPos = 0;
+ sal_Int32 aEndPos = sUIName.getLength();
+
+ // Apply the search filter
+ if (!filterTerm.isEmpty()
+ && !textSearch.SearchForward(sUIName, &aStartPos, &aEndPos))
+ {
+ continue;
+ }
+
+ SfxStyleInfo_Impl* pStyle = new SfxStyleInfo_Impl(pStyleIt);
+
+ m_aGroupInfo.push_back(
+ std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_STYLES, 0, pStyle));
+
+ m_aGroupInfo.back()->sCommand = pStyle->sCommand;
+ m_aGroupInfo.back()->sLabel = pStyle->sLabel;
+
+ pFunctionListBox->append(weld::toId(m_aGroupInfo.back().get()), sUIName,
+ xFuncEntry.get());
+ }
+
+ // Remove the style group from the list if no children
+ if (!pFunctionListBox->iter_has_child(*xFuncEntry))
+ {
+ pFunctionListBox->remove(*xFuncEntry);
+ }
+ else if (!filterTerm.isEmpty())
+ {
+ aNodesToExpand.emplace_back(std::move(xFuncEntry));
+ }
+ }
+
+ break;
+ }
+ default:
+ // Do nothing, the list box will stay empty
+ SAL_INFO("cui.customize",
+ "Ignoring unexpected SfxCfgKind: " << static_cast<int>(pInfo->nKind));
+ break;
+ }
+
+ pFunctionListBox->thaw();
+
+ if (pFunctionListBox->n_children())
+ pFunctionListBox->select(0);
+
+ //post freeze
+ for (const auto& it : aNodesToExpand)
+ pFunctionListBox->expand_row(*it);
+}
+
+void CommandCategoryListBox::SetStylesInfo(SfxStylesInfo_Impl* pStyles) { pStylesInfo = pStyles; }
+
+void CommandCategoryListBox::addChildren(
+ const weld::TreeIter* parentEntry,
+ const css::uno::Reference<css::script::browse::XBrowseNode>& parentNode,
+ CuiConfigFunctionListBox* pFunctionListBox, const OUString& filterTerm,
+ SaveInData* pCurrentSaveInData, std::vector<std::unique_ptr<weld::TreeIter>>& rNodesToExpand)
+{
+ // Setup search filter parameters
+ m_searchOptions.searchString = filterTerm;
+ utl::TextSearch textSearch(m_searchOptions);
+
+ const css::uno::Sequence<css::uno::Reference<css::script::browse::XBrowseNode>> aChildNodes
+ = parentNode->getChildNodes();
+ for (auto const& child : aChildNodes)
+ {
+ // Acquire to prevent auto-destruction
+ child->acquire();
+
+ if (child->hasChildNodes())
+ {
+ OUString sUIName = child->getName();
+
+ m_aGroupInfo.push_back(std::make_unique<SfxGroupInfo_Impl>(
+ SfxCfgKind::GROUP_SCRIPTCONTAINER, 0, static_cast<void*>(child.get())));
+ std::unique_ptr<weld::TreeIter> xNewEntry(pFunctionListBox->tree_append(
+ weld::toId(m_aGroupInfo.back().get()), sUIName, parentEntry));
+
+ addChildren(xNewEntry.get(), child, pFunctionListBox, filterTerm, pCurrentSaveInData,
+ rNodesToExpand);
+
+ // Remove the group if empty
+ if (!pFunctionListBox->iter_has_child(*xNewEntry))
+ pFunctionListBox->remove(*xNewEntry);
+ else
+ rNodesToExpand.emplace_back(std::move(xNewEntry));
+ }
+ else if (child->getType() == css::script::browse::BrowseNodeTypes::SCRIPT)
+ {
+ // Prepare for filtering
+ OUString sUIName = child->getName();
+ sal_Int32 aStartPos = 0;
+ sal_Int32 aEndPos = sUIName.getLength();
+
+ // Apply the search filter
+ if (!filterTerm.isEmpty() && !textSearch.SearchForward(sUIName, &aStartPos, &aEndPos))
+ {
+ continue;
+ }
+
+ OUString uri, description;
+
+ css::uno::Reference<css::beans::XPropertySet> xPropSet(child, css::uno::UNO_QUERY);
+
+ if (!xPropSet.is())
+ {
+ continue;
+ }
+
+ css::uno::Any value = xPropSet->getPropertyValue("URI");
+ value >>= uri;
+
+ try
+ {
+ value = xPropSet->getPropertyValue("Description");
+ value >>= description;
+ }
+ catch (css::uno::Exception&)
+ {
+ // do nothing, the description will be empty
+ }
+
+ if (description.isEmpty())
+ {
+ description = CuiResId(RID_CUISTR_NOMACRODESC);
+ }
+
+ OUString* pScriptURI = new OUString(uri);
+
+ css::uno::Reference<css::graphic::XGraphic> xImage;
+ if (pCurrentSaveInData)
+ xImage = pCurrentSaveInData->GetImage(uri);
+
+ m_aGroupInfo.push_back(
+ std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::FUNCTION_SCRIPT, 0, pScriptURI));
+ m_aGroupInfo.back()->sCommand = uri;
+ m_aGroupInfo.back()->sLabel = sUIName;
+ m_aGroupInfo.back()->sHelpText = description;
+ pFunctionListBox->append(weld::toId(m_aGroupInfo.back().get()), sUIName, xImage,
+ parentEntry);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/cui/source/customize/CustomNotebookbarGenerator.cxx b/cui/source/customize/CustomNotebookbarGenerator.cxx
new file mode 100644
index 000000000..bae85525e
--- /dev/null
+++ b/cui/source/customize/CustomNotebookbarGenerator.cxx
@@ -0,0 +1,278 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/processfactory.hxx>
+#include <rtl/bootstrap.hxx>
+#include <config_folders.h>
+#include <CustomNotebookbarGenerator.hxx>
+#include <osl/file.hxx>
+#include <osl/thread.h>
+#include <vcl/EnumContext.hxx>
+#include <vcl/settings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <unotools/confignode.hxx>
+#include <libxml/parser.h>
+#include <o3tl/string_view.hxx>
+
+#define aUIPropertiesCount 3
+
+using namespace css;
+
+CustomNotebookbarGenerator::CustomNotebookbarGenerator() {}
+
+static OUString lcl_activeAppName(vcl::EnumContext::Application eApp)
+{
+ switch (eApp)
+ {
+ case vcl::EnumContext::Application::Writer:
+ return "ActiveWriter";
+ case vcl::EnumContext::Application::Calc:
+ return "ActiveCalc";
+ case vcl::EnumContext::Application::Impress:
+ return "ActiveImpress";
+ case vcl::EnumContext::Application::Draw:
+ return "ActiveDraw";
+ default:
+ return OUString();
+ }
+}
+
+static OUString lcl_getAppName(vcl::EnumContext::Application eApp)
+{
+ switch (eApp)
+ {
+ case vcl::EnumContext::Application::Writer:
+ return "Writer";
+ case vcl::EnumContext::Application::Calc:
+ return "Calc";
+ case vcl::EnumContext::Application::Impress:
+ return "Impress";
+ case vcl::EnumContext::Application::Draw:
+ return "Draw";
+ default:
+ return OUString();
+ }
+}
+
+static OUString getAppNameRegistryPath()
+{
+ vcl::EnumContext::Application eApp = vcl::EnumContext::Application::Any;
+
+ if (SfxViewFrame* pViewFrame = SfxViewFrame::Current())
+ {
+ const Reference<frame::XFrame>& xFrame = pViewFrame->GetFrame().GetFrameInterface();
+ const Reference<frame::XModuleManager> xModuleManager
+ = frame::ModuleManager::create(::comphelper::getProcessComponentContext());
+ eApp = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(xFrame));
+ }
+
+ OUString sAppName(lcl_getAppName(eApp));
+ return "org.openoffice.Office.UI.ToolbarMode/Applications/" + sAppName;
+}
+
+static OUString customizedUIPathBuffer()
+{
+ OUString sDirPath("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE(
+ "bootstrap") ":UserInstallation}/user/config/soffice.cfg/");
+ rtl::Bootstrap::expandMacros(sDirPath);
+ return sDirPath;
+}
+
+OUString CustomNotebookbarGenerator::getCustomizedUIPath()
+{
+ OUString sAppName, sNotebookbarUIFileName;
+ CustomNotebookbarGenerator::getFileNameAndAppName(sAppName, sNotebookbarUIFileName);
+ return customizedUIPathBuffer() + "modules/s" + sAppName.toAsciiLowerCase() + "/ui/"
+ + sNotebookbarUIFileName;
+}
+
+OUString CustomNotebookbarGenerator::getOriginalUIPath()
+{
+ OUString sAppName, sNotebookbarUIFileName;
+ CustomNotebookbarGenerator::getFileNameAndAppName(sAppName, sNotebookbarUIFileName);
+ return AllSettings::GetUIRootDir() + "modules/s" + sAppName.toAsciiLowerCase() + "/ui/"
+ + sNotebookbarUIFileName;
+}
+
+static OUString getUIDirPath()
+{
+ OUString sAppName, sNotebookbarUIFileName;
+ CustomNotebookbarGenerator::getFileNameAndAppName(sAppName, sNotebookbarUIFileName);
+ OUString sUIDirPath
+ = customizedUIPathBuffer() + "modules/s" + sAppName.toAsciiLowerCase() + "/ui/";
+ return sUIDirPath;
+}
+
+OString CustomNotebookbarGenerator::getSystemPath(OUString const& sURL)
+{
+ if (sURL.isEmpty())
+ return OString();
+ OUString sSystemPathSettings;
+ if (osl_getSystemPathFromFileURL(sURL.pData, &sSystemPathSettings.pData) != osl_File_E_None)
+ {
+ SAL_WARN("cui.customnotebookbar", "Cannot get system path for :" << sURL);
+ return OString();
+ }
+ OString osSystemPathSettings
+ = OUStringToOString(sSystemPathSettings, osl_getThreadTextEncoding());
+ return osSystemPathSettings;
+}
+
+static void changeNodeValue(xmlNode* pNodePtr, const char* pProperty, const char* pValue)
+{
+ pNodePtr = pNodePtr->xmlChildrenNode;
+ while (pNodePtr)
+ {
+ if (!(xmlStrcmp(pNodePtr->name, reinterpret_cast<const xmlChar*>("property"))))
+ {
+ xmlChar* UriValue = xmlGetProp(pNodePtr, reinterpret_cast<const xmlChar*>("name"));
+ if (!(xmlStrcmp(UriValue, reinterpret_cast<const xmlChar*>(pProperty))))
+ xmlNodeSetContent(pNodePtr, reinterpret_cast<const xmlChar*>(pValue));
+ xmlFree(UriValue);
+ break;
+ }
+ pNodePtr = pNodePtr->next;
+ }
+}
+
+static void searchNodeAndAttribute(xmlNode* pNodePtr, const char* pUIItemID, const char* pProperty,
+ const char* pValue)
+{
+ pNodePtr = pNodePtr->xmlChildrenNode;
+ while (pNodePtr)
+ {
+ if (pNodePtr->type == XML_ELEMENT_NODE)
+ {
+ if (!(xmlStrcmp(pNodePtr->name, reinterpret_cast<const xmlChar*>("object"))))
+ {
+ xmlChar* UriValue = xmlGetProp(pNodePtr, reinterpret_cast<const xmlChar*>("id"));
+ if (!(xmlStrcmp(UriValue, reinterpret_cast<const xmlChar*>(pUIItemID))))
+ changeNodeValue(pNodePtr, pProperty, pValue);
+ xmlFree(UriValue);
+ }
+ searchNodeAndAttribute(pNodePtr, pUIItemID, pProperty, pValue);
+ }
+ pNodePtr = pNodePtr->next;
+ }
+}
+
+static xmlDocPtr notebookbarXMLParser(const OString& rDocName, const OString& rUIItemID,
+ const OString& rProperty, const OString& rValue)
+{
+ xmlDocPtr pDocPtr = xmlParseFile(rDocName.getStr());
+ xmlNodePtr pNodePtr = xmlDocGetRootElement(pDocPtr);
+ searchNodeAndAttribute(pNodePtr, rUIItemID.getStr(), rProperty.getStr(), rValue.getStr());
+ return pDocPtr;
+}
+
+void CustomNotebookbarGenerator::modifyCustomizedUIFile(const Sequence<OUString>& sUIItemProperties)
+{
+ OString sCustomizedUIPath = getSystemPath(getCustomizedUIPath());
+ for (auto const& aValue : sUIItemProperties)
+ {
+ std::vector<OString> aProperties(aUIPropertiesCount);
+ for (sal_Int32 aIndex = 0; aIndex < aUIPropertiesCount; aIndex++)
+ {
+ sal_Int32 nPos = aIndex;
+ std::u16string_view sToken = o3tl::getToken(aValue, nPos, ',', nPos);
+ aProperties[aIndex] = OUStringToOString(sToken, RTL_TEXTENCODING_UTF8);
+ }
+ xmlDocPtr doc = notebookbarXMLParser(sCustomizedUIPath, aProperties[0], aProperties[1],
+ aProperties[2]);
+
+ if (doc != nullptr)
+ {
+ xmlSaveFormatFile(sCustomizedUIPath.getStr(), doc, 1);
+ xmlFreeDoc(doc);
+ }
+ }
+}
+
+void CustomNotebookbarGenerator::getFileNameAndAppName(OUString& sAppName,
+ OUString& sNotebookbarUIFileName)
+{
+ SfxViewFrame* pFrame = SfxViewFrame::Current();
+ if (!pFrame)
+ return;
+
+ const auto xContext = comphelper::getProcessComponentContext();
+ utl::OConfigurationTreeRoot aRoot(xContext, "org.openoffice.Office.UI.ToolbarMode/", false);
+ const Reference<frame::XFrame>& xFrame = pFrame->GetFrame().GetFrameInterface();
+ const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create(xContext);
+
+ vcl::EnumContext::Application eApp
+ = vcl::EnumContext::GetApplicationEnum(xModuleManager->identify(xFrame));
+ OUString sActiveAppName(lcl_activeAppName(eApp));
+ sAppName = lcl_getAppName(eApp);
+ const Any aValue = aRoot.getNodeValue(sActiveAppName);
+ aValue >>= sNotebookbarUIFileName;
+}
+
+void CustomNotebookbarGenerator::createCustomizedUIFile()
+{
+ OUString sUserUIDir = getUIDirPath();
+ OUString sOriginalUIPath = getOriginalUIPath();
+ OUString sCustomizedUIPath = getCustomizedUIPath();
+
+ sal_uInt32 nflag = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
+ osl::Directory aDirectory(sUserUIDir);
+ if (aDirectory.open() != osl::FileBase::E_None)
+ osl::Directory::create(sUserUIDir, nflag);
+ else
+ SAL_WARN("cui.customnotebookbar",
+ "Cannot create the directory or directory was present :" << sUserUIDir);
+
+ osl::File aFile(sCustomizedUIPath);
+ if (aFile.open(nflag) != osl::FileBase::E_None)
+ osl::File::copy(sOriginalUIPath, sCustomizedUIPath);
+ else
+ SAL_WARN("cui.customnotebookbar",
+ "Cannot copy the file or file was present :" << sCustomizedUIPath);
+}
+
+Sequence<OUString> CustomNotebookbarGenerator::getCustomizedUIItem(OUString sNotebookbarConfigType)
+{
+ OUString aPath = getAppNameRegistryPath();
+ const utl::OConfigurationTreeRoot aAppNode(::comphelper::getProcessComponentContext(), aPath,
+ false);
+
+ const utl::OConfigurationNode aModesNode = aAppNode.openNode("Modes");
+ const utl::OConfigurationNode aModeNode(aModesNode.openNode(sNotebookbarConfigType));
+ const Any aValue = aModeNode.getNodeValue("UIItemProperties");
+ Sequence<OUString> aValues;
+ aValue >>= aValues;
+ return aValues;
+}
+
+void CustomNotebookbarGenerator::setCustomizedUIItem(Sequence<OUString> sUIItemProperties,
+ OUString sNotebookbarConfigType)
+{
+ OUString aPath = getAppNameRegistryPath();
+ const utl::OConfigurationTreeRoot aAppNode(::comphelper::getProcessComponentContext(), aPath,
+ true);
+ const utl::OConfigurationNode aModesNode = aAppNode.openNode("Modes");
+ const utl::OConfigurationNode aModeNode(aModesNode.openNode(sNotebookbarConfigType));
+
+ css::uno::Any aUIItemProperties(sUIItemProperties);
+ aModeNode.setNodeValue("UIItemProperties", aUIItemProperties);
+ aAppNode.commit();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/customize/SvxConfigPageHelper.cxx b/cui/source/customize/SvxConfigPageHelper.cxx
new file mode 100644
index 000000000..52265ef09
--- /dev/null
+++ b/cui/source/customize/SvxConfigPageHelper.cxx
@@ -0,0 +1,424 @@
+/* -*- 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 <SvxConfigPageHelper.hxx>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/ui/ImageType.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/random.hxx>
+#include <svtools/imgdef.hxx>
+#include <svtools/miscopt.hxx>
+
+static sal_Int16 theImageType = css::ui::ImageType::COLOR_NORMAL | css::ui::ImageType::SIZE_DEFAULT;
+
+void SvxConfigPageHelper::RemoveEntry(SvxEntries* pEntries, SvxConfigEntry const* pChildEntry)
+{
+ SvxEntries::iterator iter = pEntries->begin();
+
+ while (iter != pEntries->end())
+ {
+ if (pChildEntry == *iter)
+ {
+ pEntries->erase(iter);
+ break;
+ }
+ ++iter;
+ }
+}
+
+OUString SvxConfigPageHelper::replaceSaveInName(const OUString& rMessage,
+ std::u16string_view rSaveInName)
+{
+ OUString name = rMessage.replaceFirst("%SAVE IN SELECTION%", rSaveInName);
+
+ return name;
+}
+
+OUString SvxConfigPageHelper::stripHotKey(const OUString& str) { return str.replaceFirst("~", ""); }
+
+OUString SvxConfigPageHelper::replaceSixteen(const OUString& str, sal_Int32 nReplacement)
+{
+ return str.replaceAll(OUString::number(16), OUString::number(nReplacement));
+}
+
+sal_Int16 SvxConfigPageHelper::GetImageType() { return theImageType; }
+
+void SvxConfigPageHelper::InitImageType()
+{
+ theImageType = css::ui::ImageType::COLOR_NORMAL | css::ui::ImageType::SIZE_DEFAULT;
+
+ if (SvtMiscOptions().GetCurrentSymbolsSize() == SFX_SYMBOLS_SIZE_LARGE)
+ {
+ theImageType |= css::ui::ImageType::SIZE_LARGE;
+ }
+ else if (SvtMiscOptions().GetCurrentSymbolsSize() == SFX_SYMBOLS_SIZE_32)
+ {
+ theImageType |= css::ui::ImageType::SIZE_32;
+ }
+}
+
+css::uno::Reference<css::graphic::XGraphic>
+SvxConfigPageHelper::GetGraphic(const css::uno::Reference<css::ui::XImageManager>& xImageManager,
+ const OUString& rCommandURL)
+{
+ css::uno::Reference<css::graphic::XGraphic> result;
+
+ if (xImageManager.is())
+ {
+ // TODO handle large graphics
+ css::uno::Sequence<css::uno::Reference<css::graphic::XGraphic>> aGraphicSeq;
+
+ css::uno::Sequence<OUString> aImageCmdSeq{ rCommandURL };
+
+ try
+ {
+ aGraphicSeq = xImageManager->getImages(GetImageType(), aImageCmdSeq);
+
+ if (aGraphicSeq.hasElements())
+ {
+ result = aGraphicSeq[0];
+ }
+ }
+ catch (css::uno::Exception&)
+ {
+ // will return empty XGraphic
+ }
+ }
+
+ return result;
+}
+
+OUString SvxConfigPageHelper::generateCustomName(const OUString& prefix, SvxEntries* entries,
+ sal_Int32 suffix /*= 1*/)
+{
+ OUString name;
+ sal_Int32 pos = 0;
+
+ // find and replace the %n placeholder in the prefix string
+ name = prefix.replaceFirst("%n", OUString::number(suffix), &pos);
+
+ if (pos == -1)
+ {
+ // no placeholder found so just append the suffix
+ name += OUString::number(suffix);
+ }
+
+ if (!entries)
+ return name;
+
+ // now check if there is an already existing entry with this name
+ bool bFoundEntry = false;
+ for (auto const& entry : *entries)
+ {
+ if (name.equals(entry->GetName()))
+ {
+ bFoundEntry = true;
+ break;
+ }
+ }
+
+ if (bFoundEntry)
+ {
+ // name already exists so try the next number up
+ return generateCustomName(prefix, entries, ++suffix);
+ }
+
+ return name;
+}
+
+OUString SvxConfigPageHelper::generateCustomMenuURL(SvxEntries* entries, sal_Int32 suffix /*= 1*/)
+{
+ OUString url = "vnd.openoffice.org:CustomMenu" + OUString::number(suffix);
+ if (!entries)
+ return url;
+
+ // now check is there is an already existing entry with this url
+ bool bFoundEntry = false;
+ for (auto const& entry : *entries)
+ {
+ if (url.equals(entry->GetCommand()))
+ {
+ bFoundEntry = true;
+ break;
+ }
+ }
+
+ if (bFoundEntry)
+ {
+ // url already exists so try the next number up
+ return generateCustomMenuURL(entries, ++suffix);
+ }
+
+ return url;
+}
+
+sal_uInt32 SvxConfigPageHelper::generateRandomValue()
+{
+ return comphelper::rng::uniform_uint_distribution(0, std::numeric_limits<unsigned int>::max());
+}
+
+OUString SvxConfigPageHelper::generateCustomURL(SvxEntries* entries)
+{
+ OUString url = OUString::Concat(ITEM_TOOLBAR_URL) + CUSTOM_TOOLBAR_STR +
+ // use a random number to minimize possible clash with existing custom toolbars
+ OUString::number(generateRandomValue(), 16);
+
+ // now check is there is an already existing entry with this url
+ bool bFoundEntry = false;
+ for (auto const& entry : *entries)
+ {
+ if (url.equals(entry->GetCommand()))
+ {
+ bFoundEntry = true;
+ break;
+ }
+ }
+
+ if (bFoundEntry)
+ {
+ // url already exists so try the next number up
+ return generateCustomURL(entries);
+ }
+
+ return url;
+}
+
+OUString SvxConfigPageHelper::GetModuleName(std::u16string_view aModuleId)
+{
+ if (aModuleId == u"com.sun.star.text.TextDocument"
+ || aModuleId == u"com.sun.star.text.GlobalDocument")
+ return "Writer";
+ else if (aModuleId == u"com.sun.star.text.WebDocument")
+ return "Writer/Web";
+ else if (aModuleId == u"com.sun.star.drawing.DrawingDocument")
+ return "Draw";
+ else if (aModuleId == u"com.sun.star.presentation.PresentationDocument")
+ return "Impress";
+ else if (aModuleId == u"com.sun.star.sheet.SpreadsheetDocument")
+ return "Calc";
+ else if (aModuleId == u"com.sun.star.script.BasicIDE")
+ return "Basic";
+ else if (aModuleId == u"com.sun.star.formula.FormulaProperties")
+ return "Math";
+ else if (aModuleId == u"com.sun.star.sdb.RelationDesign")
+ return "Relation Design";
+ else if (aModuleId == u"com.sun.star.sdb.QueryDesign")
+ return "Query Design";
+ else if (aModuleId == u"com.sun.star.sdb.TableDesign")
+ return "Table Design";
+ else if (aModuleId == u"com.sun.star.sdb.DataSourceBrowser")
+ return "Data Source Browser";
+ else if (aModuleId == u"com.sun.star.sdb.DatabaseDocument")
+ return "Database";
+
+ return OUString();
+}
+
+OUString SvxConfigPageHelper::GetUIModuleName(
+ const OUString& aModuleId,
+ const css::uno::Reference<css::frame::XModuleManager2>& rModuleManager)
+{
+ assert(rModuleManager.is());
+
+ OUString aModuleUIName;
+
+ try
+ {
+ css::uno::Any a = rModuleManager->getByName(aModuleId);
+ css::uno::Sequence<css::beans::PropertyValue> aSeq;
+
+ if (a >>= aSeq)
+ {
+ for (css::beans::PropertyValue const& rProp : std::as_const(aSeq))
+ {
+ if (rProp.Name == "ooSetupFactoryUIName")
+ {
+ rProp.Value >>= aModuleUIName;
+ break;
+ }
+ }
+ }
+ }
+ catch (css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (css::uno::Exception&)
+ {
+ }
+
+ if (aModuleUIName.isEmpty())
+ aModuleUIName = GetModuleName(aModuleId);
+
+ return aModuleUIName;
+}
+
+bool SvxConfigPageHelper::GetMenuItemData(
+ const css::uno::Reference<css::container::XIndexAccess>& rItemContainer, sal_Int32 nIndex,
+ OUString& rCommandURL, OUString& rLabel, sal_uInt16& rType, sal_Int32& rStyle,
+ css::uno::Reference<css::container::XIndexAccess>& rSubMenu)
+{
+ try
+ {
+ css::uno::Sequence<css::beans::PropertyValue> aProps;
+ if (rItemContainer->getByIndex(nIndex) >>= aProps)
+ {
+ for (css::beans::PropertyValue const& rProp : std::as_const(aProps))
+ {
+ if (rProp.Name == ITEM_DESCRIPTOR_COMMANDURL)
+ {
+ rProp.Value >>= rCommandURL;
+ }
+ else if (rProp.Name == ITEM_DESCRIPTOR_CONTAINER)
+ {
+ rProp.Value >>= rSubMenu;
+ }
+ else if (rProp.Name == ITEM_DESCRIPTOR_STYLE)
+ {
+ rProp.Value >>= rStyle;
+ }
+ else if (rProp.Name == ITEM_DESCRIPTOR_LABEL)
+ {
+ rProp.Value >>= rLabel;
+ }
+ else if (rProp.Name == ITEM_DESCRIPTOR_TYPE)
+ {
+ rProp.Value >>= rType;
+ }
+ }
+
+ return true;
+ }
+ }
+ catch (css::lang::IndexOutOfBoundsException&)
+ {
+ }
+
+ return false;
+}
+
+bool SvxConfigPageHelper::GetToolbarItemData(
+ const css::uno::Reference<css::container::XIndexAccess>& rItemContainer, sal_Int32 nIndex,
+ OUString& rCommandURL, OUString& rLabel, sal_uInt16& rType, bool& rIsVisible, sal_Int32& rStyle)
+{
+ try
+ {
+ css::uno::Sequence<css::beans::PropertyValue> aProps;
+ if (rItemContainer->getByIndex(nIndex) >>= aProps)
+ {
+ for (css::beans::PropertyValue const& rProp : std::as_const(aProps))
+ {
+ if (rProp.Name == ITEM_DESCRIPTOR_COMMANDURL)
+ {
+ rProp.Value >>= rCommandURL;
+ }
+ else if (rProp.Name == ITEM_DESCRIPTOR_STYLE)
+ {
+ rProp.Value >>= rStyle;
+ }
+ else if (rProp.Name == ITEM_DESCRIPTOR_LABEL)
+ {
+ rProp.Value >>= rLabel;
+ }
+ else if (rProp.Name == ITEM_DESCRIPTOR_TYPE)
+ {
+ rProp.Value >>= rType;
+ }
+ else if (rProp.Name == ITEM_DESCRIPTOR_ISVISIBLE)
+ {
+ rProp.Value >>= rIsVisible;
+ }
+ }
+
+ return true;
+ }
+ }
+ catch (css::lang::IndexOutOfBoundsException&)
+ {
+ }
+
+ return false;
+}
+
+css::uno::Sequence<css::beans::PropertyValue>
+SvxConfigPageHelper::ConvertSvxConfigEntry(const SvxConfigEntry* pEntry)
+{
+ // If the name has not been changed, then the label can be stored
+ // as an empty string.
+ // It will be initialised again later using the command to label map.
+ OUString sLabel;
+ if (pEntry->HasChangedName() || pEntry->GetCommand().isEmpty())
+ sLabel = pEntry->GetName();
+
+ css::uno::Sequence<css::beans::PropertyValue> aPropSeq{
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_COMMANDURL, pEntry->GetCommand()),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_TYPE, css::ui::ItemType::DEFAULT),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_LABEL, sLabel),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_STYLE,
+ static_cast<sal_Int16>(pEntry->GetStyle()))
+ };
+
+ return aPropSeq;
+}
+
+css::uno::Sequence<css::beans::PropertyValue>
+SvxConfigPageHelper::ConvertToolbarEntry(const SvxConfigEntry* pEntry)
+{
+ // If the name has not been changed, then the label can be stored
+ // as an empty string.
+ // It will be initialised again later using the command to label map.
+ OUString sLabel;
+ if (pEntry->HasChangedName() || pEntry->GetCommand().isEmpty())
+ sLabel = pEntry->GetName();
+
+ css::uno::Sequence<css::beans::PropertyValue> aPropSeq{
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_COMMANDURL, pEntry->GetCommand()),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_TYPE, css::ui::ItemType::DEFAULT),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_LABEL, sLabel),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_ISVISIBLE, pEntry->IsVisible()),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_STYLE,
+ static_cast<sal_Int16>(pEntry->GetStyle()))
+ };
+
+ return aPropSeq;
+}
+
+bool SvxConfigPageHelper::EntrySort(SvxConfigEntry const* a, SvxConfigEntry const* b)
+{
+ return a->GetName().compareTo(b->GetName()) < 0;
+}
+
+bool SvxConfigPageHelper::SvxConfigEntryModified(SvxConfigEntry const* pEntry)
+{
+ SvxEntries* pEntries = pEntry->GetEntries();
+ if (!pEntries)
+ return false;
+
+ for (const auto& entry : *pEntries)
+ {
+ if (entry->IsModified() || SvxConfigEntryModified(entry))
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/customize/SvxMenuConfigPage.cxx b/cui/source/customize/SvxMenuConfigPage.cxx
new file mode 100644
index 000000000..5793825b5
--- /dev/null
+++ b/cui/source/customize/SvxMenuConfigPage.cxx
@@ -0,0 +1,595 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <vcl/weld.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/commandevent.hxx>
+
+#include <strings.hrc>
+#include <helpids.h>
+
+#include <cfg.hxx>
+#include <SvxMenuConfigPage.hxx>
+#include <SvxConfigPageHelper.hxx>
+#include <dialmgr.hxx>
+
+#include <comphelper/processfactory.hxx>
+
+#include <dlgname.hxx>
+
+SvxMenuConfigPage::SvxMenuConfigPage(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet& rSet, bool bIsMenuBar)
+ : SvxConfigPage(pPage, pController, rSet)
+ , m_bIsMenuBar(bIsMenuBar)
+{
+ m_xGearBtn = m_xBuilder->weld_menu_button("menugearbtn");
+ m_xGearBtn->show();
+ m_xContentsListBox.reset(
+ new SvxMenuEntriesListBox(m_xBuilder->weld_tree_view("menucontents"), this));
+ weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
+ m_xDropTargetHelper.reset(new SvxConfigPageFunctionDropTarget(*this, rTreeView));
+ rTreeView.connect_size_allocate(LINK(this, SvxMenuConfigPage, MenuEntriesSizeAllocHdl));
+ Size aSize(m_xFunctions->get_size_request());
+ rTreeView.set_size_request(aSize.Width(), aSize.Height());
+ MenuEntriesSizeAllocHdl(aSize);
+ rTreeView.set_hexpand(true);
+ rTreeView.set_vexpand(true);
+ rTreeView.show();
+
+ rTreeView.connect_changed(LINK(this, SvxMenuConfigPage, SelectMenuEntry));
+ rTreeView.connect_popup_menu(LINK(this, SvxMenuConfigPage, ContentContextMenuHdl));
+
+ m_xFunctions->get_widget().connect_popup_menu(
+ LINK(this, SvxMenuConfigPage, FunctionContextMenuHdl));
+
+ m_xGearBtn->connect_selected(LINK(this, SvxMenuConfigPage, GearHdl));
+
+ m_xCommandCategoryListBox->connect_changed(LINK(this, SvxMenuConfigPage, SelectCategory));
+
+ m_xMoveUpButton->connect_clicked(LINK(this, SvxConfigPage, MoveHdl));
+ m_xMoveDownButton->connect_clicked(LINK(this, SvxConfigPage, MoveHdl));
+
+ m_xAddCommandButton->connect_clicked(LINK(this, SvxMenuConfigPage, AddCommandHdl));
+ m_xRemoveCommandButton->connect_clicked(LINK(this, SvxMenuConfigPage, RemoveCommandHdl));
+
+ m_xInsertBtn->connect_selected(LINK(this, SvxMenuConfigPage, InsertHdl));
+ m_xModifyBtn->connect_selected(LINK(this, SvxMenuConfigPage, ModifyItemHdl));
+ m_xResetBtn->connect_clicked(LINK(this, SvxMenuConfigPage, ResetMenuHdl));
+
+ // These operations are not possible on menus/context menus yet
+ m_xModifyBtn->remove_item("changeIcon");
+ m_xModifyBtn->remove_item("resetIcon");
+ m_xModifyBtn->remove_item("restoreItem");
+
+ if (!bIsMenuBar)
+ {
+ //TODO: Remove this when the gear button is implemented for context menus
+ m_xGearBtn->set_sensitive(false);
+ m_xGearBtn->hide();
+ }
+ else
+ {
+ // TODO: Remove this when it is possible to reset menubar menus individually
+ m_xResetBtn->set_sensitive(false);
+ }
+}
+
+void SvxMenuConfigPage::ListModified()
+{
+ // regenerate with the current ordering within the list
+ SvxEntries* pEntries = GetTopLevelSelection()->GetEntries();
+ pEntries->clear();
+
+ for (int i = 0; i < m_xContentsListBox->n_children(); ++i)
+ pEntries->push_back(weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(i)));
+
+ GetSaveInData()->SetModified();
+ GetTopLevelSelection()->SetModified();
+ UpdateButtonStates();
+}
+
+IMPL_LINK(SvxMenuConfigPage, MenuEntriesSizeAllocHdl, const Size&, rSize, void)
+{
+ weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
+ std::vector<int> aWidths;
+
+ int nStandardImageColWidth = rTreeView.get_checkbox_column_width();
+ int nMargin = 16;
+
+ aWidths.push_back(rSize.Width() - (nMargin + nStandardImageColWidth));
+ rTreeView.set_column_fixed_widths(aWidths);
+}
+
+SvxMenuConfigPage::~SvxMenuConfigPage()
+{
+ for (int i = 0, nCount = m_xSaveInListBox->get_count(); i < nCount; ++i)
+ delete weld::fromId<SaveInData*>(m_xSaveInListBox->get_id(i));
+ m_xSaveInListBox->clear();
+}
+
+// Populates the Menu combo box
+void SvxMenuConfigPage::Init()
+{
+ // ensure that the UI is cleared before populating it
+ m_xTopLevelListBox->clear();
+ m_xContentsListBox->clear();
+
+ ReloadTopLevelListBox();
+
+ m_xTopLevelListBox->set_active(m_xTopLevelListBox->get_count() ? 0 : -1);
+ SelectElement();
+
+ m_xCommandCategoryListBox->Init(comphelper::getProcessComponentContext(), m_xFrame,
+ m_aModuleId);
+ m_xCommandCategoryListBox->categorySelected(m_xFunctions.get(), OUString(), GetSaveInData());
+ SelectFunctionHdl(m_xFunctions->get_widget());
+}
+
+IMPL_LINK_NOARG(SvxMenuConfigPage, SelectMenuEntry, weld::TreeView&, void) { UpdateButtonStates(); }
+
+void SvxMenuConfigPage::UpdateButtonStates()
+{
+ // Disable Up and Down buttons depending on current selection
+ int selection = m_xContentsListBox->get_selected_index();
+
+ bool bIsSeparator
+ = selection != -1
+ && weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(selection))->IsSeparator();
+ bool bIsValidSelection = (m_xContentsListBox->n_children() != 0 && selection != -1);
+
+ m_xMoveUpButton->set_sensitive(bIsValidSelection && selection != 0);
+ m_xMoveDownButton->set_sensitive(bIsValidSelection
+ && selection != m_xContentsListBox->n_children() - 1);
+
+ m_xRemoveCommandButton->set_sensitive(bIsValidSelection);
+
+ m_xModifyBtn->set_sensitive(bIsValidSelection && !bIsSeparator);
+
+ // If there is no top level selection (menu), then everything working on the right box
+ // which contains the functions of the selected menu/toolbar needs to be disabled
+ SvxConfigEntry* pMenuData = GetTopLevelSelection();
+
+ m_xInsertBtn->set_sensitive(pMenuData != nullptr);
+
+ SvxConfigEntry* selectedCmd = CreateCommandFromSelection(GetScriptURL());
+
+ m_xAddCommandButton->set_sensitive(
+ pMenuData != nullptr && !IsCommandInMenuList(selectedCmd, pMenuData->GetEntries()));
+
+ delete selectedCmd;
+
+ if (bIsValidSelection)
+ {
+ m_xRemoveCommandButton->set_sensitive(pMenuData != nullptr);
+ }
+
+ //Handle the gear button
+ if (pMenuData && m_bIsMenuBar)
+ {
+ // Add option (gear_add) will always be enabled
+ m_xGearBtn->set_item_sensitive("menu_gear_delete", pMenuData->IsDeletable());
+ m_xGearBtn->set_item_sensitive("menu_gear_rename", pMenuData->IsRenamable());
+ m_xGearBtn->set_item_sensitive("menu_gear_move", pMenuData->IsMovable());
+ }
+}
+
+void SvxMenuConfigPage::DeleteSelectedTopLevel()
+{
+ SvxConfigEntry* pMenuData = GetTopLevelSelection();
+
+ SvxEntries* pParentEntries = FindParentForChild(GetSaveInData()->GetEntries(), pMenuData);
+
+ SvxConfigPageHelper::RemoveEntry(pParentEntries, pMenuData);
+ delete pMenuData;
+
+ ReloadTopLevelListBox();
+
+ GetSaveInData()->SetModified();
+}
+
+void SvxMenuConfigPage::DeleteSelectedContent()
+{
+ int nActEntry = m_xContentsListBox->get_selected_index();
+
+ if (nActEntry == -1)
+ return;
+
+ // get currently selected menu entry
+ SvxConfigEntry* pMenuEntry
+ = weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nActEntry));
+
+ // get currently selected menu
+ SvxConfigEntry* pMenu = GetTopLevelSelection();
+
+ // remove menu entry from the list for this menu
+ SvxConfigPageHelper::RemoveEntry(pMenu->GetEntries(), pMenuEntry);
+
+ // remove menu entry from UI
+ m_xContentsListBox->remove(nActEntry);
+
+ // if this is a submenu entry, redraw the menus list box
+ if (pMenuEntry->IsPopup())
+ {
+ ReloadTopLevelListBox();
+ }
+
+ // delete data for menu entry
+ delete pMenuEntry;
+
+ GetSaveInData()->SetModified();
+ pMenu->SetModified();
+}
+
+short SvxMenuConfigPage::QueryReset()
+{
+ OUString msg = CuiResId(RID_CUISTR_CONFIRM_MENU_RESET);
+
+ OUString saveInName = m_xSaveInListBox->get_active_text();
+
+ OUString label = SvxConfigPageHelper::replaceSaveInName(msg, saveInName);
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(
+ GetFrameWeld(), VclMessageType::Question, VclButtonsType::YesNo, label));
+ return xQueryBox->run();
+}
+
+void SvxMenuConfigPage::SelectElement()
+{
+ weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
+
+ SvxConfigEntry* pMenuData = GetTopLevelSelection();
+ if (!pMenuData)
+ rTreeView.clear();
+ else
+ {
+ SvxEntries* pEntries = pMenuData->GetEntries();
+
+ rTreeView.bulk_insert_for_each(
+ pEntries->size(), [this, &rTreeView, pEntries](weld::TreeIter& rIter, int nIdx) {
+ auto const& entry = (*pEntries)[nIdx];
+ OUString sId(weld::toId(entry));
+ rTreeView.set_id(rIter, sId);
+ InsertEntryIntoUI(entry, rTreeView, rIter, true);
+ });
+ }
+
+ UpdateButtonStates();
+}
+
+IMPL_LINK(SvxMenuConfigPage, GearHdl, const OString&, rIdent, void)
+{
+ if (rIdent == "menu_gear_add")
+ {
+ SvxMainMenuOrganizerDialog aDialog(GetFrameWeld(), GetSaveInData()->GetEntries(), nullptr,
+ true);
+
+ if (aDialog.run() == RET_OK)
+ {
+ GetSaveInData()->SetEntries(aDialog.ReleaseEntries());
+ ReloadTopLevelListBox(aDialog.GetSelectedEntry());
+ GetSaveInData()->SetModified();
+ }
+ }
+ else if (rIdent == "menu_gear_delete")
+ {
+ DeleteSelectedTopLevel();
+ }
+ else if (rIdent == "menu_gear_rename")
+ {
+ SvxConfigEntry* pMenuData = GetTopLevelSelection();
+
+ OUString sCurrentName(SvxConfigPageHelper::stripHotKey(pMenuData->GetName()));
+ OUString sDesc = CuiResId(RID_CUISTR_LABEL_NEW_NAME);
+
+ SvxNameDialog aNameDialog(GetFrameWeld(), sCurrentName, sDesc);
+ aNameDialog.set_help_id(HID_SVX_CONFIG_RENAME_MENU);
+ aNameDialog.set_title(CuiResId(RID_CUISTR_RENAME_MENU));
+
+ if (aNameDialog.run() == RET_OK)
+ {
+ OUString sNewName = aNameDialog.GetName();
+
+ if (sCurrentName == sNewName)
+ return;
+
+ pMenuData->SetName(sNewName);
+
+ ReloadTopLevelListBox();
+
+ GetSaveInData()->SetModified();
+ }
+ }
+ else if (rIdent == "menu_gear_move")
+ {
+ SvxConfigEntry* pMenuData = GetTopLevelSelection();
+
+ SvxMainMenuOrganizerDialog aDialog(GetFrameWeld(), GetSaveInData()->GetEntries(), pMenuData,
+ false);
+ if (aDialog.run() == RET_OK)
+ {
+ GetSaveInData()->SetEntries(aDialog.ReleaseEntries());
+
+ ReloadTopLevelListBox();
+
+ GetSaveInData()->SetModified();
+ }
+ }
+ else
+ {
+ //This block should never be reached
+ SAL_WARN("cui.customize", "Unknown gear menu option: " << rIdent);
+ return;
+ }
+
+ UpdateButtonStates();
+}
+
+IMPL_LINK_NOARG(SvxMenuConfigPage, SelectCategory, weld::ComboBox&, void)
+{
+ OUString aSearchTerm(m_xSearchEdit->get_text());
+
+ m_xCommandCategoryListBox->categorySelected(m_xFunctions.get(), aSearchTerm, GetSaveInData());
+
+ SelectFunctionHdl(m_xFunctions->get_widget());
+}
+
+IMPL_LINK_NOARG(SvxMenuConfigPage, AddCommandHdl, weld::Button&, void)
+{
+ int nPos = AddFunction(-1, /*bAllowDuplicates*/ false);
+ if (nPos == -1)
+ return;
+ weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
+ SvxConfigEntry* pEntry = weld::fromId<SvxConfigEntry*>(rTreeView.get_id(nPos));
+ InsertEntryIntoUI(pEntry, rTreeView, nPos, true);
+}
+
+IMPL_LINK_NOARG(SvxMenuConfigPage, RemoveCommandHdl, weld::Button&, void)
+{
+ DeleteSelectedContent();
+ if (GetSaveInData()->IsModified())
+ {
+ UpdateButtonStates();
+ }
+}
+
+IMPL_LINK(SvxMenuConfigPage, InsertHdl, const OString&, rIdent, void)
+{
+ weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
+ if (rIdent == "insertseparator")
+ {
+ SvxConfigEntry* pNewEntryData = new SvxConfigEntry;
+ pNewEntryData->SetUserDefined();
+ int nPos = AppendEntry(pNewEntryData, -1);
+ InsertEntryIntoUI(pNewEntryData, rTreeView, nPos, true);
+ }
+ else if (rIdent == "insertsubmenu")
+ {
+ OUString aNewName;
+ OUString aDesc = CuiResId(RID_CUISTR_SUBMENU_NAME);
+
+ SvxNameDialog aNameDialog(GetFrameWeld(), aNewName, aDesc);
+ aNameDialog.set_help_id(HID_SVX_CONFIG_NAME_SUBMENU);
+ aNameDialog.set_title(CuiResId(RID_CUISTR_ADD_SUBMENU));
+
+ if (aNameDialog.run() == RET_OK)
+ {
+ aNewName = aNameDialog.GetName();
+
+ SvxConfigEntry* pNewEntryData
+ = new SvxConfigEntry(aNewName, aNewName, true, /*bParentData*/ false);
+ pNewEntryData->SetName(aNewName);
+ pNewEntryData->SetUserDefined();
+
+ int nPos = AppendEntry(pNewEntryData, -1);
+ InsertEntryIntoUI(pNewEntryData, rTreeView, nPos, true);
+
+ ReloadTopLevelListBox();
+
+ m_xContentsListBox->scroll_to_row(nPos);
+ m_xContentsListBox->select(nPos);
+
+ GetSaveInData()->SetModified();
+ }
+ }
+ else
+ {
+ //This block should never be reached
+ SAL_WARN("cui.customize", "Unknown insert option: " << rIdent);
+ return;
+ }
+
+ if (GetSaveInData()->IsModified())
+ {
+ UpdateButtonStates();
+ }
+}
+
+IMPL_LINK(SvxMenuConfigPage, ModifyItemHdl, const OString&, rIdent, void)
+{
+ if (rIdent == "renameItem")
+ {
+ int nActEntry = m_xContentsListBox->get_selected_index();
+ SvxConfigEntry* pEntry
+ = weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nActEntry));
+
+ OUString aNewName(SvxConfigPageHelper::stripHotKey(pEntry->GetName()));
+ OUString aDesc = CuiResId(RID_CUISTR_LABEL_NEW_NAME);
+
+ SvxNameDialog aNameDialog(GetFrameWeld(), aNewName, aDesc);
+ aNameDialog.set_help_id(HID_SVX_CONFIG_RENAME_MENU_ITEM);
+ aNameDialog.set_title(CuiResId(RID_CUISTR_RENAME_MENU));
+
+ if (aNameDialog.run() == RET_OK)
+ {
+ aNewName = aNameDialog.GetName();
+
+ pEntry->SetName(aNewName);
+ m_xContentsListBox->set_text(nActEntry, aNewName, 0);
+
+ GetSaveInData()->SetModified();
+ GetTopLevelSelection()->SetModified();
+ }
+ }
+ else
+ {
+ //This block should never be reached
+ SAL_WARN("cui.customize", "Unknown insert option: " << rIdent);
+ return;
+ }
+
+ if (GetSaveInData()->IsModified())
+ {
+ UpdateButtonStates();
+ }
+}
+
+IMPL_LINK_NOARG(SvxMenuConfigPage, ResetMenuHdl, weld::Button&, void)
+{
+ SvxConfigEntry* pMenuData = GetTopLevelSelection();
+
+ if (pMenuData == nullptr)
+ {
+ SAL_WARN("cui.customize",
+ "RHB top level selection is null. A menu must be selected to reset!");
+ return;
+ }
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(
+ GetFrameWeld(), VclMessageType::Question, VclButtonsType::YesNo,
+ CuiResId(RID_CUISTR_CONFIRM_RESTORE_DEFAULT_MENU)));
+
+ // Resetting individual top-level menus is not possible at the moment.
+ // So we are resetting only if it is a context menu
+ if (m_bIsMenuBar || xQueryBox->run() != RET_YES)
+ return;
+
+ sal_Int32 nPos = m_xTopLevelListBox->get_active();
+ ContextMenuSaveInData* pSaveInData = static_cast<ContextMenuSaveInData*>(GetSaveInData());
+
+ pSaveInData->ResetContextMenu(pMenuData);
+
+ // ensure that the UI is cleared before populating it
+ m_xTopLevelListBox->clear();
+ m_xContentsListBox->clear();
+
+ ReloadTopLevelListBox();
+
+ // Reselect the reset menu
+ m_xTopLevelListBox->set_active(nPos);
+ SelectElement();
+}
+
+SaveInData* SvxMenuConfigPage::CreateSaveInData(
+ const css::uno::Reference<css::ui::XUIConfigurationManager>& xCfgMgr,
+ const css::uno::Reference<css::ui::XUIConfigurationManager>& xParentCfgMgr,
+ const OUString& aModuleId, bool bDocConfig)
+{
+ if (!m_bIsMenuBar)
+ return static_cast<SaveInData*>(
+ new ContextMenuSaveInData(xCfgMgr, xParentCfgMgr, aModuleId, bDocConfig));
+
+ return static_cast<SaveInData*>(
+ new MenuSaveInData(xCfgMgr, xParentCfgMgr, aModuleId, bDocConfig));
+}
+
+IMPL_LINK(SvxMenuConfigPage, ContentContextMenuHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
+
+ // Select clicked entry
+ std::unique_ptr<weld::TreeIter> xIter(rTreeView.make_iterator());
+ if (!rTreeView.get_dest_row_at_pos(rCEvt.GetMousePosPixel(), xIter.get(), false))
+ return false;
+ rTreeView.select(*xIter);
+ SelectMenuEntry(rTreeView);
+
+ int nSelectIndex = m_xContentsListBox->get_selected_index();
+
+ bool bIsSeparator
+ = nSelectIndex != -1
+ && weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nSelectIndex))->IsSeparator();
+ bool bIsValidSelection = (m_xContentsListBox->n_children() != 0 && nSelectIndex != -1);
+
+ std::unique_ptr<weld::Builder> xBuilder(
+ Application::CreateBuilder(&rTreeView, "cui/ui/entrycontextmenu.ui"));
+ auto xContextMenu = xBuilder->weld_menu("menu");
+ xContextMenu->set_visible("add", false);
+ xContextMenu->set_visible("remove", bIsValidSelection);
+ xContextMenu->set_visible("rename", bIsValidSelection && !bIsSeparator);
+ xContextMenu->set_visible("changeIcon", false);
+ xContextMenu->set_visible("resetIcon", false);
+ xContextMenu->set_visible("restoreDefault", false);
+ OString sCommand(xContextMenu->popup_at_rect(
+ &rTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1))));
+
+ if (sCommand == "remove")
+ {
+ RemoveCommandHdl(*m_xRemoveCommandButton);
+ }
+ else if (sCommand == "rename")
+ {
+ ModifyItemHdl("renameItem");
+ }
+ else if (!sCommand.isEmpty())
+ SAL_WARN("cui.customize", "Unknown context menu action: " << sCommand);
+ return true;
+}
+
+IMPL_LINK(SvxMenuConfigPage, FunctionContextMenuHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ weld::TreeView& rTreeView = m_xFunctions->get_widget();
+
+ // Select clicked entry
+ std::unique_ptr<weld::TreeIter> xIter(rTreeView.make_iterator());
+ if (!rTreeView.get_dest_row_at_pos(rCEvt.GetMousePosPixel(), xIter.get(), false))
+ return false;
+ rTreeView.select(*xIter);
+ SelectFunctionHdl(rTreeView);
+
+ std::unique_ptr<weld::Builder> xBuilder(
+ Application::CreateBuilder(&rTreeView, "cui/ui/entrycontextmenu.ui"));
+ auto xContextMenu = xBuilder->weld_menu("menu");
+ xContextMenu->set_visible("add", true);
+ xContextMenu->set_visible("remove", false);
+ xContextMenu->set_visible("rename", false);
+ xContextMenu->set_visible("changeIcon", false);
+ xContextMenu->set_visible("resetIcon", false);
+ xContextMenu->set_visible("restoreDefault", false);
+ OString sCommand(xContextMenu->popup_at_rect(
+ &rTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1))));
+
+ if (sCommand == "add")
+ {
+ AddCommandHdl(*m_xAddCommandButton);
+ }
+ else if (!sCommand.isEmpty())
+ SAL_WARN("cui.customize", "Unknown context menu action: " << sCommand);
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/customize/SvxNotebookbarConfigPage.cxx b/cui/source/customize/SvxNotebookbarConfigPage.cxx
new file mode 100644
index 000000000..99c15b8f3
--- /dev/null
+++ b/cui/source/customize/SvxNotebookbarConfigPage.cxx
@@ -0,0 +1,543 @@
+/* -*- 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 <vcl/commandinfoprovider.hxx>
+#include <vcl/event.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/svapp.hxx>
+
+#include <algorithm>
+#include <cstddef>
+
+#include <helpids.h>
+#include <strings.hrc>
+
+#include <cfg.hxx>
+#include <SvxNotebookbarConfigPage.hxx>
+#include <SvxConfigPageHelper.hxx>
+#include <dialmgr.hxx>
+#include <libxml/parser.h>
+#include <osl/file.hxx>
+#include <CustomNotebookbarGenerator.hxx>
+#include <sfx2/notebookbar/SfxNotebookBar.hxx>
+#include <unotools/configmgr.hxx>
+#include <comphelper/processfactory.hxx>
+#include <o3tl/string_view.hxx>
+#include <com/sun/star/frame/theUICommandDescription.hpp>
+
+namespace uno = com::sun::star::uno;
+namespace frame = com::sun::star::frame;
+namespace lang = com::sun::star::lang;
+namespace container = com::sun::star::container;
+namespace beans = com::sun::star::beans;
+namespace graphic = com::sun::star::graphic;
+
+static bool isCategoryAvailable(std::u16string_view sClassId, std::u16string_view sUIItemId,
+ std::u16string_view sActiveCategory, bool& isCategory)
+{
+ if (sUIItemId == sActiveCategory)
+ return true;
+ else if ((sClassId == u"GtkMenu" || sClassId == u"GtkGrid") && sUIItemId != sActiveCategory)
+ {
+ isCategory = false;
+ return false;
+ }
+ return false;
+}
+
+static OUString charToString(const char* cString)
+{
+ return OUString(cString, strlen(cString), RTL_TEXTENCODING_UTF8);
+}
+
+static OUString getFileName(std::u16string_view aFileName)
+{
+ if (aFileName == u"notebookbar.ui")
+ return CuiResId(RID_CUISTR_TABBED);
+ else if (aFileName == u"notebookbar_compact.ui")
+ return CuiResId(RID_CUISTR_TABBED_COMPACT);
+ else if (aFileName == u"notebookbar_groupedbar_full.ui")
+ return CuiResId(RID_CUISTR_GROUPEDBAR);
+ else if (aFileName == u"notebookbar_groupedbar_compact.ui")
+ return CuiResId(RID_CUISTR_GROUPEDBAR_COMPACT);
+ else
+ return "None";
+}
+
+static OUString getModuleId(std::u16string_view sModuleName)
+{
+ if (sModuleName == u"Writer")
+ return "com.sun.star.text.TextDocument";
+ else if (sModuleName == u"Draw")
+ return "com.sun.star.drawing.DrawingDocument";
+ else if (sModuleName == u"Impress")
+ return "com.sun.star.presentation.PresentationDocument";
+ else if (sModuleName == u"Calc")
+ return "com.sun.star.sheet.SpreadsheetDocument";
+ else
+ return "None";
+}
+
+SvxNotebookbarConfigPage::SvxNotebookbarConfigPage(weld::Container* pPage,
+ weld::DialogController* pController,
+ const SfxItemSet& rSet)
+ : SvxConfigPage(pPage, pController, rSet)
+{
+ m_xCommandCategoryListBox->set_visible(false);
+ m_xDescriptionFieldLb->set_visible(false);
+ m_xSearchEdit->set_visible(false);
+ m_xDescriptionField->set_visible(false);
+ m_xMoveUpButton->set_visible(false);
+ m_xMoveDownButton->set_visible(false);
+ m_xAddCommandButton->set_visible(false);
+ m_xRemoveCommandButton->set_visible(false);
+ m_xLeftFunctionLabel->set_visible(false);
+ m_xSearchLabel->set_visible(false);
+ m_xCategoryLabel->set_visible(false);
+ m_xInsertBtn->set_visible(false);
+ m_xModifyBtn->set_visible(false);
+ m_xResetBtn->set_visible(false);
+ m_xCustomizeLabel->set_visible(false);
+
+ weld::TreeView& rCommandCategoryBox = m_xFunctions->get_widget();
+ rCommandCategoryBox.hide();
+
+ m_xContentsListBox.reset(
+ new SvxNotebookbarEntriesListBox(m_xBuilder->weld_tree_view("toolcontents"), this));
+ m_xDropTargetHelper.reset(
+ new SvxConfigPageFunctionDropTarget(*this, m_xContentsListBox->get_widget()));
+ weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
+ Size aSize(m_xFunctions->get_size_request());
+ rTreeView.set_size_request(aSize.Width(), aSize.Height());
+
+ rTreeView.set_hexpand(true);
+ rTreeView.set_vexpand(true);
+ rTreeView.set_help_id(HID_SVX_CONFIG_NOTEBOOKBAR_CONTENTS);
+ rTreeView.show();
+}
+
+SvxNotebookbarConfigPage::~SvxNotebookbarConfigPage() {}
+
+void SvxNotebookbarConfigPage::DeleteSelectedTopLevel() {}
+
+void SvxNotebookbarConfigPage::DeleteSelectedContent() {}
+
+void SvxNotebookbarConfigPage::Init()
+{
+ m_xTopLevelListBox->clear();
+ m_xContentsListBox->clear();
+ m_xSaveInListBox->clear();
+ CustomNotebookbarGenerator::createCustomizedUIFile();
+ OUString sNotebookbarInterface = getFileName(m_sFileName);
+
+ OUString sScopeName
+ = utl::ConfigManager::getProductName() + " " + m_sAppName + " - " + sNotebookbarInterface;
+ OUString sSaveInListBoxID = notebookbarTabScope;
+
+ m_xSaveInListBox->append(sSaveInListBoxID, sScopeName);
+ m_xSaveInListBox->set_active_id(sSaveInListBoxID);
+
+ m_xTopLevelListBox->append("NotebookBar", CuiResId(RID_CUISTR_ALL_COMMANDS));
+ m_xTopLevelListBox->set_active_id("NotebookBar");
+ SelectElement();
+}
+
+SaveInData* SvxNotebookbarConfigPage::CreateSaveInData(
+ const css::uno::Reference<css::ui::XUIConfigurationManager>& xCfgMgr,
+ const css::uno::Reference<css::ui::XUIConfigurationManager>& xParentCfgMgr,
+ const OUString& aModuleId, bool bDocConfig)
+{
+ return static_cast<SaveInData*>(
+ new ToolbarSaveInData(xCfgMgr, xParentCfgMgr, aModuleId, bDocConfig));
+}
+
+void SvxNotebookbarConfigPage::UpdateButtonStates() {}
+
+short SvxNotebookbarConfigPage::QueryReset()
+{
+ OUString msg = CuiResId(RID_CUISTR_CONFIRM_TOOLBAR_RESET);
+
+ OUString saveInName = m_xSaveInListBox->get_active_text();
+
+ OUString label = SvxConfigPageHelper::replaceSaveInName(msg, saveInName);
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(
+ GetFrameWeld(), VclMessageType::Question, VclButtonsType::YesNo, label));
+ int nValue = xQueryBox->run();
+ if (nValue == RET_YES)
+ {
+ OUString sOriginalUIPath = CustomNotebookbarGenerator::getOriginalUIPath();
+ OUString sCustomizedUIPath = CustomNotebookbarGenerator::getCustomizedUIPath();
+ osl::File::copy(sOriginalUIPath, sCustomizedUIPath);
+ OUString sNotebookbarInterface = getFileName(m_sFileName);
+ Sequence<OUString> sSequenceEntries;
+ CustomNotebookbarGenerator::setCustomizedUIItem(sSequenceEntries, sNotebookbarInterface);
+ OUString sUIPath = "modules/s" + m_sAppName.toAsciiLowerCase() + "/ui/";
+ sfx2::SfxNotebookBar::ReloadNotebookBar(sUIPath);
+ }
+ return nValue;
+}
+
+void SvxConfigPage::InsertEntryIntoNotebookbarTabUI(std::u16string_view sClassId,
+ const OUString& sUIItemId,
+ const OUString& sUIItemCommand,
+ weld::TreeView& rTreeView,
+ const weld::TreeIter& rIter)
+{
+ css::uno::Reference<css::container::XNameAccess> m_xCommandToLabelMap;
+ uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference<container::XNameAccess> xNameAccess(
+ css::frame::theUICommandDescription::get(xContext));
+
+ uno::Sequence<beans::PropertyValue> aPropSeq;
+
+ xNameAccess->getByName(getModuleId(m_sAppName)) >>= m_xCommandToLabelMap;
+
+ try
+ {
+ uno::Any aModuleVal = m_xCommandToLabelMap->getByName(sUIItemCommand);
+
+ aModuleVal >>= aPropSeq;
+ }
+ catch (container::NoSuchElementException&)
+ {
+ }
+
+ OUString aLabel;
+ for (auto const& prop : std::as_const(aPropSeq))
+ if (prop.Name == "Name")
+ prop.Value >>= aLabel;
+
+ OUString aName = SvxConfigPageHelper::stripHotKey(aLabel);
+
+ if (sClassId == u"GtkSeparatorMenuItem" || sClassId == u"GtkSeparator")
+ {
+ rTreeView.set_text(rIter, "--------------------------------------------", 0);
+ }
+ else
+ {
+ if (aName.isEmpty())
+ aName = sUIItemId;
+ auto xImage = GetSaveInData()->GetImage(sUIItemCommand);
+ if (xImage.is())
+ rTreeView.set_image(rIter, xImage, -1);
+ rTreeView.set_text(rIter, aName, 0);
+ rTreeView.set_id(rIter, sUIItemId);
+ }
+}
+
+void SvxNotebookbarConfigPage::getNodeValue(xmlNode* pNodePtr, NotebookbarEntries& aNodeEntries)
+{
+ pNodePtr = pNodePtr->xmlChildrenNode;
+ while (pNodePtr)
+ {
+ if (!(xmlStrcmp(pNodePtr->name, reinterpret_cast<const xmlChar*>("property"))))
+ {
+ xmlChar* UriValue = xmlGetProp(pNodePtr, reinterpret_cast<const xmlChar*>("name"));
+ if (!(xmlStrcmp(UriValue, reinterpret_cast<const xmlChar*>("visible"))))
+ {
+ xmlChar* aValue = xmlNodeGetContent(pNodePtr);
+ const char* cVisibleValue = reinterpret_cast<const char*>(aValue);
+ aNodeEntries.sVisibleValue = charToString(cVisibleValue);
+ xmlFree(aValue);
+ }
+ if (!(xmlStrcmp(UriValue, reinterpret_cast<const xmlChar*>("action_name"))))
+ {
+ xmlChar* aValue = xmlNodeGetContent(pNodePtr);
+ const char* cActionName = reinterpret_cast<const char*>(aValue);
+ aNodeEntries.sActionName = charToString(cActionName);
+ xmlFree(aValue);
+ }
+ xmlFree(UriValue);
+ }
+ pNodePtr = pNodePtr->next;
+ }
+}
+
+void SvxNotebookbarConfigPage::searchNodeandAttribute(std::vector<NotebookbarEntries>& aEntries,
+ std::vector<CategoriesEntries>& aCategoryList,
+ OUString& sActiveCategory,
+ CategoriesEntries& aCurItemEntry,
+ xmlNode* pNodePtr, bool isCategory)
+{
+ pNodePtr = pNodePtr->xmlChildrenNode;
+ while (pNodePtr)
+ {
+ if (pNodePtr->type == XML_ELEMENT_NODE)
+ {
+ const char* cNodeName = reinterpret_cast<const char*>(pNodePtr->name);
+ if (strcmp(cNodeName, "object") == 0)
+ {
+ OUString sSecondVal;
+
+ xmlChar* UriValue = xmlGetProp(pNodePtr, reinterpret_cast<const xmlChar*>("id"));
+ const char* cUIItemID = reinterpret_cast<const char*>(UriValue);
+ OUString sUIItemId = charToString(cUIItemID);
+ xmlFree(UriValue);
+
+ UriValue = xmlGetProp(pNodePtr, reinterpret_cast<const xmlChar*>("class"));
+ const char* cClassId = reinterpret_cast<const char*>(UriValue);
+ OUString sClassId = charToString(cClassId);
+ xmlFree(UriValue);
+
+ CategoriesEntries aCategoryEntry;
+ if (sClassId == "sfxlo-PriorityHBox")
+ {
+ aCategoryEntry.sDisplayName = sUIItemId;
+ aCategoryEntry.sUIItemId = sUIItemId;
+ aCategoryEntry.sClassType = sClassId;
+ aCategoryList.push_back(aCategoryEntry);
+
+ aCurItemEntry = aCategoryEntry;
+ }
+ else if (sClassId == "sfxlo-PriorityMergedHBox")
+ {
+ aCategoryEntry.sDisplayName = aCurItemEntry.sDisplayName + " | " + sUIItemId;
+ aCategoryEntry.sUIItemId = sUIItemId;
+ aCategoryEntry.sClassType = sClassId;
+
+ if (aCurItemEntry.sClassType == sClassId)
+ {
+ sal_Int32 rPos = 0;
+ aCategoryEntry.sDisplayName
+ = OUString::Concat(
+ o3tl::getToken(aCurItemEntry.sDisplayName, rPos, ' ', rPos))
+ + " | " + sUIItemId;
+ }
+ aCategoryList.push_back(aCategoryEntry);
+ aCurItemEntry = aCategoryEntry;
+ }
+ else if (sClassId == "svtlo-ManagedMenuButton")
+ {
+ sal_Int32 rPos = 1;
+ sSecondVal = sUIItemId.getToken(rPos, ':', rPos);
+ if (!sSecondVal.isEmpty())
+ {
+ aCategoryEntry.sDisplayName
+ = aCurItemEntry.sDisplayName + " | " + sSecondVal;
+ aCategoryEntry.sUIItemId = sSecondVal;
+ aCategoryList.push_back(aCategoryEntry);
+ }
+ }
+
+ NotebookbarEntries nodeEntries;
+ if (isCategoryAvailable(sClassId, sUIItemId, sActiveCategory, isCategory)
+ || isCategory)
+ {
+ isCategory = true;
+ if (sClassId == "GtkMenuItem" || sClassId == "GtkToolButton"
+ || sClassId == "GtkMenuToolButton"
+ || (sClassId == "svtlo-ManagedMenuButton" && sSecondVal.isEmpty()))
+ {
+ nodeEntries.sClassId = sClassId;
+ nodeEntries.sUIItemId = sUIItemId;
+ nodeEntries.sDisplayName = sUIItemId;
+
+ getNodeValue(pNodePtr, nodeEntries);
+ aEntries.push_back(nodeEntries);
+ }
+ else if (sClassId == "GtkSeparatorMenuItem" || sClassId == "GtkSeparator")
+ {
+ nodeEntries.sClassId = sClassId;
+ nodeEntries.sUIItemId = sUIItemId;
+ nodeEntries.sDisplayName = "Null";
+ nodeEntries.sVisibleValue = "Null";
+ nodeEntries.sActionName = "Null";
+ aEntries.push_back(nodeEntries);
+ }
+ else if (sClassId == "sfxlo-PriorityHBox"
+ || sClassId == "sfxlo-PriorityMergedHBox"
+ || sClassId == "svtlo-ManagedMenuButton")
+ {
+ nodeEntries.sClassId = sClassId;
+ nodeEntries.sUIItemId = sUIItemId;
+ nodeEntries.sDisplayName
+ = aCategoryList[aCategoryList.size() - 1].sDisplayName;
+ nodeEntries.sVisibleValue = "Null";
+ nodeEntries.sActionName = "Null";
+ aEntries.push_back(nodeEntries);
+ }
+ }
+ }
+ searchNodeandAttribute(aEntries, aCategoryList, sActiveCategory, aCurItemEntry,
+ pNodePtr, isCategory);
+ }
+ pNodePtr = pNodePtr->next;
+ }
+}
+
+void SvxNotebookbarConfigPage::FillFunctionsList(xmlNodePtr pRootNodePtr,
+ std::vector<NotebookbarEntries>& aEntries,
+ std::vector<CategoriesEntries>& aCategoryList,
+ OUString& sActiveCategory)
+{
+ CategoriesEntries aCurItemEntry;
+ searchNodeandAttribute(aEntries, aCategoryList, sActiveCategory, aCurItemEntry, pRootNodePtr,
+ false);
+}
+
+void SvxNotebookbarConfigPage::SelectElement()
+{
+ OString sUIFileUIPath = CustomNotebookbarGenerator::getSystemPath(
+ CustomNotebookbarGenerator::getCustomizedUIPath());
+ xmlDocPtr pDoc = xmlParseFile(sUIFileUIPath.getStr());
+ if (!pDoc)
+ return;
+ xmlNodePtr pNodePtr = xmlDocGetRootElement(pDoc);
+
+ std::vector<NotebookbarEntries> aEntries;
+ std::vector<CategoriesEntries> aCategoryList;
+ OUString sActiveCategory = m_xTopLevelListBox->get_active_id();
+ FillFunctionsList(pNodePtr, aEntries, aCategoryList, sActiveCategory);
+
+ if (m_xTopLevelListBox->get_count() == 1)
+ {
+ for (std::size_t nIdx = 0; nIdx < aCategoryList.size(); nIdx++)
+ m_xTopLevelListBox->append(aCategoryList[nIdx].sUIItemId,
+ aCategoryList[nIdx].sDisplayName);
+ }
+ tools::ULong nStart = 0;
+ if (aEntries[nStart].sClassId == "sfxlo-PriorityHBox"
+ || aEntries[nStart].sClassId == "sfxlo-PriorityMergedHBox")
+ nStart = 1;
+
+ std::vector<NotebookbarEntries> aTempEntries;
+ for (std::size_t nIdx = nStart; nIdx < aEntries.size(); nIdx++)
+ {
+ if (aEntries[nIdx].sClassId == "svtlo-ManagedMenuButton")
+ {
+ aTempEntries.push_back(aEntries[nIdx]);
+ sal_Int32 rPos = 1;
+ sActiveCategory = aEntries[nIdx].sUIItemId.getToken(rPos, ':', rPos);
+ FillFunctionsList(pNodePtr, aTempEntries, aCategoryList, sActiveCategory);
+ }
+ else
+ aTempEntries.push_back(aEntries[nIdx]);
+ }
+
+ aEntries = std::move(aTempEntries);
+
+ weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
+ rTreeView.bulk_insert_for_each(
+ aEntries.size(), [this, &rTreeView, &aEntries](weld::TreeIter& rIter, int nIdx) {
+ OUString sId(OUString::number(nIdx));
+ rTreeView.set_id(rIter, sId);
+ if (aEntries[nIdx].sActionName != "Null")
+ {
+ if (aEntries[nIdx].sVisibleValue == "True")
+ {
+ rTreeView.set_toggle(rIter, TRISTATE_TRUE);
+ }
+ else
+ {
+ rTreeView.set_toggle(rIter, TRISTATE_FALSE);
+ }
+ }
+ InsertEntryIntoNotebookbarTabUI(aEntries[nIdx].sClassId, aEntries[nIdx].sDisplayName,
+ aEntries[nIdx].sActionName, rTreeView, rIter);
+ });
+
+ aEntries.clear();
+
+ xmlFreeDoc(pDoc);
+}
+
+SvxNotebookbarEntriesListBox::SvxNotebookbarEntriesListBox(std::unique_ptr<weld::TreeView> xParent,
+ SvxConfigPage* pPg)
+ : SvxMenuEntriesListBox(std::move(xParent), pPg)
+{
+ m_xControl->connect_toggled(LINK(this, SvxNotebookbarEntriesListBox, CheckButtonHdl));
+ m_xControl->connect_key_press(Link<const KeyEvent&, bool>());
+ m_xControl->connect_key_press(LINK(this, SvxNotebookbarEntriesListBox, KeyInputHdl));
+}
+
+SvxNotebookbarEntriesListBox::~SvxNotebookbarEntriesListBox() {}
+
+static void EditRegistryFile(std::u16string_view sUIItemId, const OUString& sSetEntry,
+ const OUString& sNotebookbarInterface)
+{
+ int nFlag = 0;
+ Sequence<OUString> aOldEntries
+ = CustomNotebookbarGenerator::getCustomizedUIItem(sNotebookbarInterface);
+ Sequence<OUString> aNewEntries(aOldEntries.getLength() + 1);
+ auto pNewEntries = aNewEntries.getArray();
+ for (int nIdx = 0; nIdx < aOldEntries.getLength(); nIdx++)
+ {
+ sal_Int32 rPos = 0;
+ std::u16string_view sFirstValue = o3tl::getToken(aOldEntries[nIdx], rPos, ',', rPos);
+ if (sFirstValue == sUIItemId)
+ {
+ aOldEntries.getArray()[nIdx] = sSetEntry;
+ nFlag = 1;
+ break;
+ }
+ pNewEntries[nIdx] = aOldEntries[nIdx];
+ }
+
+ if (nFlag == 0)
+ {
+ pNewEntries[aOldEntries.getLength()] = sSetEntry;
+ CustomNotebookbarGenerator::setCustomizedUIItem(aNewEntries, sNotebookbarInterface);
+ }
+ else
+ {
+ CustomNotebookbarGenerator::setCustomizedUIItem(aOldEntries, sNotebookbarInterface);
+ }
+}
+
+void SvxNotebookbarEntriesListBox::ChangedVisibility(int nRow)
+{
+ OUString sUIItemId = m_xControl->get_selected_id();
+ OUString sNotebookbarInterface = getFileName(m_pPage->GetFileName());
+
+ OUString sVisible;
+ if (m_xControl->get_toggle(nRow) == TRISTATE_TRUE)
+ sVisible = "True";
+ else
+ sVisible = "False";
+ OUString sSetEntries = sUIItemId + ",visible," + sVisible;
+ Sequence<OUString> sSeqOfEntries{ sSetEntries };
+ EditRegistryFile(sUIItemId, sSetEntries, sNotebookbarInterface);
+ CustomNotebookbarGenerator::modifyCustomizedUIFile(sSeqOfEntries);
+ OUString sUIPath = "modules/s" + m_pPage->GetAppName().toAsciiLowerCase() + "/ui/";
+ sfx2::SfxNotebookBar::ReloadNotebookBar(sUIPath);
+}
+
+IMPL_LINK(SvxNotebookbarEntriesListBox, CheckButtonHdl, const weld::TreeView::iter_col&, rRowCol,
+ void)
+{
+ ChangedVisibility(m_xControl->get_iter_index_in_parent(rRowCol.first));
+}
+
+IMPL_LINK(SvxNotebookbarEntriesListBox, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ if (rKeyEvent.GetKeyCode() == KEY_SPACE)
+ {
+ int nRow = m_xControl->get_selected_index();
+ m_xControl->set_toggle(nRow, m_xControl->get_toggle(nRow) == TRISTATE_TRUE ? TRISTATE_FALSE
+ : TRISTATE_TRUE);
+ ChangedVisibility(nRow);
+ return true;
+ }
+ return SvxMenuEntriesListBox::KeyInputHdl(rKeyEvent);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/customize/SvxToolbarConfigPage.cxx b/cui/source/customize/SvxToolbarConfigPage.cxx
new file mode 100644
index 000000000..bc8346922
--- /dev/null
+++ b/cui/source/customize/SvxToolbarConfigPage.cxx
@@ -0,0 +1,929 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <vcl/event.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/commandevent.hxx>
+
+#include <sfx2/sfxsids.hrc>
+#include <svl/stritem.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <algorithm>
+#include <helpids.h>
+#include <strings.hrc>
+
+#include <cfg.hxx>
+#include <SvxToolbarConfigPage.hxx>
+#include <SvxConfigPageHelper.hxx>
+#include <dialmgr.hxx>
+
+#include <comphelper/processfactory.hxx>
+
+#include <dlgname.hxx>
+
+SvxToolbarConfigPage::SvxToolbarConfigPage(weld::Container* pPage,
+ weld::DialogController* pController,
+ const SfxItemSet& rSet)
+ : SvxConfigPage(pPage, pController, rSet)
+{
+ m_xGearBtn = m_xBuilder->weld_menu_button("toolbargearbtn");
+ m_xGearBtn->show();
+ m_xContainer->set_help_id(HID_SVX_CONFIG_TOOLBAR);
+
+ m_xContentsListBox.reset(
+ new SvxToolbarEntriesListBox(m_xBuilder->weld_tree_view("toolcontents"), this));
+ m_xDropTargetHelper.reset(
+ new SvxConfigPageFunctionDropTarget(*this, m_xContentsListBox->get_widget()));
+
+ weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
+ Size aSize(m_xFunctions->get_size_request());
+ rTreeView.set_size_request(aSize.Width(), aSize.Height());
+
+ rTreeView.set_hexpand(true);
+ rTreeView.set_vexpand(true);
+ rTreeView.set_help_id(HID_SVX_CONFIG_TOOLBAR_CONTENTS);
+ rTreeView.show();
+
+ rTreeView.connect_changed(LINK(this, SvxToolbarConfigPage, SelectToolbarEntry));
+ rTreeView.connect_popup_menu(LINK(this, SvxToolbarConfigPage, ContentContextMenuHdl));
+
+ m_xFunctions->get_widget().connect_popup_menu(
+ LINK(this, SvxToolbarConfigPage, FunctionContextMenuHdl));
+
+ m_xTopLevelListBox->set_help_id(HID_SVX_TOPLEVELLISTBOX);
+ m_xSaveInListBox->set_help_id(HID_SVX_SAVE_IN);
+ m_xMoveUpButton->set_help_id(HID_SVX_UP_TOOLBAR_ITEM);
+ m_xMoveDownButton->set_help_id(HID_SVX_DOWN_TOOLBAR_ITEM);
+ m_xDescriptionField->set_help_id(HID_SVX_DESCFIELD);
+
+ m_xCommandCategoryListBox->connect_changed(LINK(this, SvxToolbarConfigPage, SelectCategory));
+
+ m_xGearBtn->connect_selected(LINK(this, SvxToolbarConfigPage, GearHdl));
+
+ m_xMoveUpButton->connect_clicked(LINK(this, SvxToolbarConfigPage, MoveHdl));
+ m_xMoveDownButton->connect_clicked(LINK(this, SvxToolbarConfigPage, MoveHdl));
+ // Always enable Up and Down buttons
+ // added for issue i53677 by shizhoubo
+ m_xMoveDownButton->set_sensitive(true);
+ m_xMoveUpButton->set_sensitive(true);
+
+ m_xAddCommandButton->connect_clicked(LINK(this, SvxToolbarConfigPage, AddCommandHdl));
+ m_xRemoveCommandButton->connect_clicked(LINK(this, SvxToolbarConfigPage, RemoveCommandHdl));
+
+ m_xInsertBtn->connect_selected(LINK(this, SvxToolbarConfigPage, InsertHdl));
+ m_xModifyBtn->connect_selected(LINK(this, SvxToolbarConfigPage, ModifyItemHdl));
+ m_xResetBtn->connect_clicked(LINK(this, SvxToolbarConfigPage, ResetToolbarHdl));
+
+ // "Insert Submenu" is irrelevant to the toolbars
+ m_xInsertBtn->remove_item("insertsubmenu");
+
+ // Gear menu's "Move" action is irrelevant to the toolbars
+ m_xGearBtn->set_item_sensitive("toolbar_gear_move", false);
+
+ // default toolbar to select is standardbar unless a different one
+ // has been passed in
+ m_aURLToSelect = ITEM_TOOLBAR_URL;
+ m_aURLToSelect += "standardbar";
+
+ const SfxPoolItem* pItem = rSet.GetItem(SID_CONFIG);
+
+ if (pItem)
+ {
+ OUString text = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if (text.startsWith(ITEM_TOOLBAR_URL))
+ {
+ m_aURLToSelect = text.copy(0);
+ }
+ }
+}
+
+void SvxToolbarConfigPage::ListModified()
+{
+ // regenerate with the current ordering within the list
+ SvxEntries* pEntries = GetTopLevelSelection()->GetEntries();
+ pEntries->clear();
+
+ for (int i = 0; i < m_xContentsListBox->n_children(); ++i)
+ pEntries->push_back(weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(i)));
+
+ GetSaveInData()->SetModified();
+ GetTopLevelSelection()->SetModified();
+
+ SvxConfigEntry* pToolbar = GetTopLevelSelection();
+ if (pToolbar)
+ static_cast<ToolbarSaveInData*>(GetSaveInData())->ApplyToolbar(pToolbar);
+}
+
+SvxToolbarConfigPage::~SvxToolbarConfigPage()
+{
+ for (int i = 0, nCount = m_xSaveInListBox->get_count(); i < nCount; ++i)
+ {
+ ToolbarSaveInData* pData = weld::fromId<ToolbarSaveInData*>(m_xSaveInListBox->get_id(i));
+ delete pData;
+ }
+ m_xSaveInListBox->clear();
+}
+
+void SvxToolbarConfigPage::DeleteSelectedTopLevel()
+{
+ const sal_Int32 nSelectionPos = m_xTopLevelListBox->get_active();
+ ToolbarSaveInData* pSaveInData = static_cast<ToolbarSaveInData*>(GetSaveInData());
+ pSaveInData->RemoveToolbar(GetTopLevelSelection());
+
+ int nCount = m_xTopLevelListBox->get_count();
+ if (nCount > 1)
+ {
+ // select next entry after the one being deleted
+ // selection position is indexed from 0 so need to
+ // subtract one from the entry count
+ if (nSelectionPos != nCount - 1)
+ {
+ m_xTopLevelListBox->set_active(nSelectionPos + 1);
+ }
+ else
+ {
+ m_xTopLevelListBox->set_active(nSelectionPos - 1);
+ }
+ SelectElement();
+
+ // and now remove the entry
+ m_xTopLevelListBox->remove(nSelectionPos);
+ }
+ else
+ {
+ ReloadTopLevelListBox();
+ }
+}
+
+void SvxToolbarConfigPage::DeleteSelectedContent()
+{
+ int nActEntry = m_xContentsListBox->get_selected_index();
+
+ if (nActEntry == -1)
+ return;
+
+ // get currently selected entry
+ SvxConfigEntry* pEntry = weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nActEntry));
+
+ SvxConfigEntry* pToolbar = GetTopLevelSelection();
+
+ // remove entry from the list for this toolbar
+ SvxConfigPageHelper::RemoveEntry(pToolbar->GetEntries(), pEntry);
+
+ // remove toolbar entry from UI
+ m_xContentsListBox->remove(nActEntry);
+
+ // delete data for toolbar entry
+ delete pEntry;
+
+ static_cast<ToolbarSaveInData*>(GetSaveInData())->ApplyToolbar(pToolbar);
+ UpdateButtonStates();
+
+ // if this is the last entry in the toolbar and it is a user
+ // defined toolbar pop up a dialog asking the user if they
+ // want to delete the toolbar
+ if (m_xContentsListBox->n_children() == 0 && GetTopLevelSelection()->IsDeletable())
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(
+ GetFrameWeld(), VclMessageType::Question, VclButtonsType::YesNo,
+ CuiResId(RID_SXVSTR_CONFIRM_DELETE_TOOLBAR)));
+ if (xQueryBox->run() == RET_YES)
+ {
+ DeleteSelectedTopLevel();
+ }
+ }
+}
+
+IMPL_LINK(SvxToolbarConfigPage, MoveHdl, weld::Button&, rButton, void)
+{
+ MoveEntry(&rButton == m_xMoveUpButton.get());
+}
+
+void SvxToolbarConfigPage::MoveEntry(bool bMoveUp)
+{
+ SvxConfigPage::MoveEntry(bMoveUp);
+
+ // Apply change to currently selected toolbar
+ SvxConfigEntry* pToolbar = GetTopLevelSelection();
+ if (pToolbar)
+ static_cast<ToolbarSaveInData*>(GetSaveInData())->ApplyToolbar(pToolbar);
+ else
+ {
+ SAL_WARN("cui.customize", "SvxToolbarConfigPage::MoveEntry(): no entry");
+ UpdateButtonStates();
+ }
+}
+
+void SvxToolbarConfigPage::Init()
+{
+ // ensure that the UI is cleared before populating it
+ m_xTopLevelListBox->clear();
+ m_xContentsListBox->clear();
+
+ ReloadTopLevelListBox();
+
+ sal_Int32 nCount = m_xTopLevelListBox->get_count();
+ sal_Int32 nPos = nCount > 0 ? 0 : -1;
+
+ if (!m_aURLToSelect.isEmpty())
+ {
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ SvxConfigEntry* pData = weld::fromId<SvxConfigEntry*>(m_xTopLevelListBox->get_id(i));
+
+ if (pData->GetCommand().equals(m_aURLToSelect))
+ {
+ nPos = i;
+ break;
+ }
+ }
+
+ // in future select the default toolbar: Standard
+ m_aURLToSelect = ITEM_TOOLBAR_URL;
+ m_aURLToSelect += "standardbar";
+ }
+
+ m_xTopLevelListBox->set_active(nPos);
+ SelectElement();
+
+ m_xCommandCategoryListBox->Init(comphelper::getProcessComponentContext(), m_xFrame,
+ m_aModuleId);
+ m_xCommandCategoryListBox->categorySelected(m_xFunctions.get(), OUString(), GetSaveInData());
+ SelectFunctionHdl(m_xFunctions->get_widget());
+}
+
+SaveInData* SvxToolbarConfigPage::CreateSaveInData(
+ const css::uno::Reference<css::ui::XUIConfigurationManager>& xCfgMgr,
+ const css::uno::Reference<css::ui::XUIConfigurationManager>& xParentCfgMgr,
+ const OUString& aModuleId, bool bDocConfig)
+{
+ return static_cast<SaveInData*>(
+ new ToolbarSaveInData(xCfgMgr, xParentCfgMgr, aModuleId, bDocConfig));
+}
+
+IMPL_LINK_NOARG(SvxToolbarConfigPage, SelectToolbarEntry, weld::TreeView&, void)
+{
+ UpdateButtonStates();
+}
+
+IMPL_LINK(SvxToolbarConfigPage, GearHdl, const OString&, rIdent, void)
+{
+ SvxConfigEntry* pCurrentToolbar = GetTopLevelSelection();
+
+ if (rIdent == "toolbar_gear_add")
+ {
+ OUString prefix = CuiResId(RID_CUISTR_NEW_TOOLBAR);
+
+ OUString aNewName
+ = SvxConfigPageHelper::generateCustomName(prefix, GetSaveInData()->GetEntries());
+
+ OUString aNewURL = SvxConfigPageHelper::generateCustomURL(GetSaveInData()->GetEntries());
+
+ SvxNewToolbarDialog aNameDialog(GetFrameWeld(), aNewName);
+
+ // Reflect the actual m_xSaveInListBox into the new toolbar dialog
+ for (int i = 0, nCount = m_xSaveInListBox->get_count(); i < nCount; ++i)
+ aNameDialog.m_xSaveInListBox->append_text(m_xSaveInListBox->get_text(i));
+
+ aNameDialog.m_xSaveInListBox->set_active(m_xSaveInListBox->get_active());
+
+ if (aNameDialog.run() == RET_OK)
+ {
+ aNewName = aNameDialog.GetName();
+
+ // Where to save the new toolbar? (i.e. Modulewise or documentwise)
+ int nInsertPos = aNameDialog.m_xSaveInListBox->get_active();
+
+ ToolbarSaveInData* pData
+ = weld::fromId<ToolbarSaveInData*>(m_xSaveInListBox->get_id(nInsertPos));
+
+ if (GetSaveInData() != pData)
+ {
+ m_xSaveInListBox->set_active(nInsertPos);
+ SelectSaveInLocation(*m_xSaveInListBox);
+ }
+
+ SvxConfigEntry* pToolbar = new SvxConfigEntry(aNewName, aNewURL, true, false);
+
+ pToolbar->SetUserDefined();
+ pToolbar->SetMain();
+
+ pData->CreateToolbar(pToolbar);
+
+ OUString sId(weld::toId(pToolbar));
+ m_xTopLevelListBox->append(sId, pToolbar->GetName());
+ m_xTopLevelListBox->set_active_id(sId);
+ SelectElement();
+
+ pData->SetModified();
+ }
+ }
+ else if (rIdent == "toolbar_gear_delete")
+ {
+ if (pCurrentToolbar && pCurrentToolbar->IsDeletable())
+ {
+ DeleteSelectedTopLevel();
+ UpdateButtonStates();
+ }
+ }
+ else if (rIdent == "toolbar_gear_rename")
+ {
+ sal_Int32 nSelectionPos = m_xTopLevelListBox->get_active();
+ SvxConfigEntry* pToolbar
+ = weld::fromId<SvxConfigEntry*>(m_xTopLevelListBox->get_id(nSelectionPos));
+ ToolbarSaveInData* pSaveInData = static_cast<ToolbarSaveInData*>(GetSaveInData());
+
+ //Rename the toolbar
+ OUString sCurrentName(SvxConfigPageHelper::stripHotKey(pToolbar->GetName()));
+ OUString sDesc = CuiResId(RID_CUISTR_LABEL_NEW_NAME);
+
+ SvxNameDialog aNameDialog(GetFrameWeld(), sCurrentName, sDesc);
+ aNameDialog.set_help_id(HID_SVX_CONFIG_RENAME_TOOLBAR);
+ aNameDialog.set_title(CuiResId(RID_CUISTR_RENAME_TOOLBAR));
+
+ if (aNameDialog.run() == RET_OK)
+ {
+ OUString sNewName = aNameDialog.GetName();
+
+ if (sCurrentName == sNewName)
+ return;
+
+ pToolbar->SetName(sNewName);
+ pSaveInData->ApplyToolbar(pToolbar);
+
+ // have to use remove and insert to change the name
+ m_xTopLevelListBox->remove(nSelectionPos);
+ OUString sId(weld::toId(pToolbar));
+ m_xTopLevelListBox->insert(nSelectionPos, sNewName, &sId, nullptr, nullptr);
+ m_xTopLevelListBox->set_active_id(sId);
+ }
+ }
+ else if (rIdent == "toolbar_gear_iconOnly" || rIdent == "toolbar_gear_textOnly"
+ || rIdent == "toolbar_gear_iconAndText")
+ {
+ ToolbarSaveInData* pSaveInData = static_cast<ToolbarSaveInData*>(GetSaveInData());
+
+ if (pCurrentToolbar == nullptr || pSaveInData == nullptr)
+ {
+ SAL_WARN("cui.customize", "NULL toolbar or savein data");
+ return;
+ }
+
+ sal_Int32 nStyle = 0;
+ if (rIdent == "toolbar_gear_iconOnly")
+ nStyle = 0;
+ else if (rIdent == "toolbar_gear_textOnly")
+ nStyle = 1;
+ else if (rIdent == "toolbar_gear_iconAndText")
+ nStyle = 2;
+
+ pCurrentToolbar->SetStyle(nStyle);
+ pSaveInData->SetSystemStyle(m_xFrame, pCurrentToolbar->GetCommand(), nStyle);
+
+ SelectElement();
+ }
+ else
+ {
+ //This block should never be reached
+ SAL_WARN("cui.customize", "Unknown gear menu option: " << rIdent);
+ return;
+ }
+}
+
+IMPL_LINK_NOARG(SvxToolbarConfigPage, SelectCategory, weld::ComboBox&, void)
+{
+ OUString aSearchTerm(m_xSearchEdit->get_text());
+
+ m_xCommandCategoryListBox->categorySelected(m_xFunctions.get(), aSearchTerm, GetSaveInData());
+
+ SelectFunctionHdl(m_xFunctions->get_widget());
+}
+
+IMPL_LINK_NOARG(SvxToolbarConfigPage, AddCommandHdl, weld::Button&, void) { AddFunction(); }
+
+IMPL_LINK_NOARG(SvxToolbarConfigPage, RemoveCommandHdl, weld::Button&, void)
+{
+ DeleteSelectedContent();
+}
+
+IMPL_LINK(SvxToolbarConfigPage, InsertHdl, const OString&, rIdent, void)
+{
+ if (rIdent == "insertseparator")
+ {
+ // Get the currently selected toolbar
+ SvxConfigEntry* pToolbar = GetTopLevelSelection();
+
+ SvxConfigEntry* pNewEntryData = new SvxConfigEntry;
+ pNewEntryData->SetUserDefined();
+
+ int nPos = AppendEntry(pNewEntryData, -1);
+ InsertEntryIntoUI(pNewEntryData, m_xContentsListBox->get_widget(), nPos);
+
+ static_cast<ToolbarSaveInData*>(GetSaveInData())->ApplyToolbar(pToolbar);
+
+ UpdateButtonStates();
+ }
+ else
+ {
+ //This block should never be reached
+ SAL_WARN("cui.customize", "Unknown insert option: " << rIdent);
+ return;
+ }
+}
+
+IMPL_LINK(SvxToolbarConfigPage, ModifyItemHdl, const OString&, rIdent, void)
+{
+ bool bNeedsApply = false;
+
+ // get currently selected toolbar
+ SvxConfigEntry* pToolbar = GetTopLevelSelection();
+
+ if (rIdent.isEmpty() || pToolbar == nullptr)
+ {
+ SAL_WARN("cui.customize", "No toolbar selected, or empty rIdent!");
+ return;
+ }
+
+ if (rIdent == "renameItem")
+ {
+ int nActEntry = m_xContentsListBox->get_selected_index();
+ SvxConfigEntry* pEntry
+ = weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nActEntry));
+
+ OUString aNewName(SvxConfigPageHelper::stripHotKey(pEntry->GetName()));
+ OUString aDesc = CuiResId(RID_CUISTR_LABEL_NEW_NAME);
+
+ SvxNameDialog aNameDialog(GetFrameWeld(), aNewName, aDesc);
+ aNameDialog.set_help_id(HID_SVX_CONFIG_RENAME_TOOLBAR_ITEM);
+ aNameDialog.set_title(CuiResId(RID_CUISTR_RENAME_TOOLBAR));
+
+ if (aNameDialog.run() == RET_OK)
+ {
+ aNewName = aNameDialog.GetName();
+
+ if (aNewName.isEmpty()) // tdf#80758 - Accelerator character ("~") is passed as
+ pEntry->SetName("~"); // the button name in case of empty values.
+ else
+ pEntry->SetName(aNewName);
+
+ m_xContentsListBox->set_text(nActEntry, aNewName, 0);
+ bNeedsApply = true;
+ }
+ }
+ else if (rIdent == "changeIcon")
+ {
+ int nActEntry = m_xContentsListBox->get_selected_index();
+ SvxConfigEntry* pEntry
+ = weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nActEntry));
+
+ SvxIconSelectorDialog aIconDialog(GetFrameWeld(), GetSaveInData()->GetImageManager(),
+ GetSaveInData()->GetParentImageManager());
+
+ if (aIconDialog.run() == RET_OK)
+ {
+ css::uno::Reference<css::graphic::XGraphic> newgraphic = aIconDialog.GetSelectedIcon();
+
+ if (newgraphic.is())
+ {
+ css::uno::Sequence<OUString> aURLSeq{ pEntry->GetCommand() };
+
+ if (!pEntry->GetBackupGraphic().is())
+ {
+ css::uno::Reference<css::graphic::XGraphic> backup
+ = SvxConfigPageHelper::GetGraphic(GetSaveInData()->GetImageManager(),
+ aURLSeq[0]);
+
+ if (backup.is())
+ {
+ pEntry->SetBackupGraphic(backup);
+ }
+ }
+
+ css::uno::Sequence<css::uno::Reference<css::graphic::XGraphic>> aGraphicSeq{
+ newgraphic
+ };
+ try
+ {
+ GetSaveInData()->GetImageManager()->replaceImages(
+ SvxConfigPageHelper::GetImageType(), aURLSeq, aGraphicSeq);
+
+ m_xContentsListBox->remove(nActEntry);
+
+ OUString sId(weld::toId(pEntry));
+ m_xContentsListBox->insert(nActEntry, sId);
+ m_xContentsListBox->set_toggle(nActEntry, pEntry->IsVisible() ? TRISTATE_TRUE
+ : TRISTATE_FALSE);
+ InsertEntryIntoUI(pEntry, m_xContentsListBox->get_widget(), nActEntry);
+
+ m_xContentsListBox->select(nActEntry);
+ m_xContentsListBox->scroll_to_row(nActEntry);
+
+ GetSaveInData()->PersistChanges(GetSaveInData()->GetImageManager());
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("cui.customize", "Error replacing image");
+ }
+ }
+ }
+ }
+ else if (rIdent == "resetIcon")
+ {
+ int nActEntry = m_xContentsListBox->get_selected_index();
+ SvxConfigEntry* pEntry
+ = weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nActEntry));
+
+ css::uno::Reference<css::graphic::XGraphic> backup = pEntry->GetBackupGraphic();
+
+ css::uno::Sequence<css::uno::Reference<css::graphic::XGraphic>> aGraphicSeq{ backup };
+
+ css::uno::Sequence<OUString> aURLSeq{ pEntry->GetCommand() };
+
+ try
+ {
+ GetSaveInData()->GetImageManager()->replaceImages(SvxConfigPageHelper::GetImageType(),
+ aURLSeq, aGraphicSeq);
+
+ m_xContentsListBox->remove(nActEntry);
+
+ OUString sId(weld::toId(pEntry));
+ m_xContentsListBox->insert(nActEntry, sId);
+ m_xContentsListBox->set_toggle(nActEntry,
+ pEntry->IsVisible() ? TRISTATE_TRUE : TRISTATE_FALSE);
+ InsertEntryIntoUI(pEntry, m_xContentsListBox->get_widget(), nActEntry);
+
+ m_xContentsListBox->select(nActEntry);
+ m_xContentsListBox->scroll_to_row(nActEntry);
+
+ // reset backup in entry
+ pEntry->SetBackupGraphic(css::uno::Reference<css::graphic::XGraphic>());
+
+ GetSaveInData()->PersistChanges(GetSaveInData()->GetImageManager());
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("cui.customize", "Error resetting image");
+ }
+ }
+ else if (rIdent == "restoreItem")
+ {
+ int nActEntry = m_xContentsListBox->get_selected_index();
+ SvxConfigEntry* pEntry
+ = weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nActEntry));
+
+ ToolbarSaveInData* pSaveInData = static_cast<ToolbarSaveInData*>(GetSaveInData());
+
+ OUString aSystemName = pSaveInData->GetSystemUIName(pEntry->GetCommand());
+
+ if (!pEntry->GetName().equals(aSystemName))
+ {
+ pEntry->SetName(aSystemName);
+ m_xContentsListBox->set_text(nActEntry, SvxConfigPageHelper::stripHotKey(aSystemName),
+ 0);
+ bNeedsApply = true;
+ }
+
+ css::uno::Sequence<OUString> aURLSeq{ pEntry->GetCommand() };
+
+ try
+ {
+ GetSaveInData()->GetImageManager()->removeImages(SvxConfigPageHelper::GetImageType(),
+ aURLSeq);
+
+ // reset backup in entry
+ pEntry->SetBackupGraphic(css::uno::Reference<css::graphic::XGraphic>());
+
+ GetSaveInData()->PersistChanges(GetSaveInData()->GetImageManager());
+
+ m_xContentsListBox->remove(nActEntry);
+
+ OUString sId(weld::toId(pEntry));
+ m_xContentsListBox->insert(nActEntry, sId);
+ m_xContentsListBox->set_toggle(nActEntry,
+ pEntry->IsVisible() ? TRISTATE_TRUE : TRISTATE_FALSE);
+ InsertEntryIntoUI(pEntry, m_xContentsListBox->get_widget(), nActEntry);
+
+ m_xContentsListBox->select(nActEntry);
+ m_xContentsListBox->scroll_to_row(nActEntry);
+
+ bNeedsApply = true;
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("cui.customize", "Error restoring image");
+ }
+ }
+ else
+ {
+ //This block should never be reached
+ SAL_WARN("cui.customize", "Unknown insert option: " << rIdent);
+ return;
+ }
+
+ if (bNeedsApply)
+ {
+ static_cast<ToolbarSaveInData*>(GetSaveInData())->ApplyToolbar(pToolbar);
+ UpdateButtonStates();
+ }
+}
+
+IMPL_LINK_NOARG(SvxToolbarConfigPage, ResetToolbarHdl, weld::Button&, void)
+{
+ sal_Int32 nSelectionPos = m_xTopLevelListBox->get_active();
+
+ SvxConfigEntry* pToolbar
+ = weld::fromId<SvxConfigEntry*>(m_xTopLevelListBox->get_id(nSelectionPos));
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(
+ GetFrameWeld(), VclMessageType::Question, VclButtonsType::YesNo,
+ CuiResId(RID_CUISTR_CONFIRM_RESTORE_DEFAULT)));
+ if (xQueryBox->run() == RET_YES)
+ {
+ ToolbarSaveInData* pSaveInData = static_cast<ToolbarSaveInData*>(GetSaveInData());
+
+ pSaveInData->RestoreToolbar(pToolbar);
+
+ SelectElement();
+ }
+}
+
+void SvxToolbarConfigPage::UpdateButtonStates()
+{
+ SvxConfigEntry* pToolbar = GetTopLevelSelection();
+ int selection = m_xContentsListBox->get_selected_index();
+
+ bool bIsSeparator
+ = selection != -1
+ && weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(selection))->IsSeparator();
+ bool bIsValidSelection = (m_xContentsListBox->n_children() != 0 && selection != -1);
+
+ m_xMoveUpButton->set_sensitive(bIsValidSelection);
+ m_xMoveDownButton->set_sensitive(bIsValidSelection);
+
+ m_xRemoveCommandButton->set_sensitive(bIsValidSelection);
+
+ m_xModifyBtn->set_sensitive(bIsValidSelection && !bIsSeparator);
+
+ // Handle the gear button
+ // "toolbar_gear_add" option is always enabled
+ m_xGearBtn->set_item_sensitive("toolbar_gear_delete", pToolbar && pToolbar->IsDeletable());
+ m_xGearBtn->set_item_sensitive("toolbar_gear_rename", pToolbar && pToolbar->IsRenamable());
+}
+
+short SvxToolbarConfigPage::QueryReset()
+{
+ OUString msg = CuiResId(RID_CUISTR_CONFIRM_TOOLBAR_RESET);
+
+ OUString saveInName = m_xSaveInListBox->get_active_text();
+
+ OUString label = SvxConfigPageHelper::replaceSaveInName(msg, saveInName);
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(
+ GetFrameWeld(), VclMessageType::Question, VclButtonsType::YesNo, label));
+ return xQueryBox->run();
+}
+
+void SvxToolbarConfigPage::SelectElement()
+{
+ m_xContentsListBox->clear();
+
+ SvxConfigEntry* pToolbar = GetTopLevelSelection();
+ if (pToolbar == nullptr)
+ {
+ //TODO: Disable related buttons
+ m_xInsertBtn->set_sensitive(false);
+ m_xResetBtn->set_sensitive(false);
+ m_xGearBtn->set_sensitive(false);
+
+ return;
+ }
+ else
+ {
+ m_xInsertBtn->set_sensitive(true);
+ m_xResetBtn->set_sensitive(true);
+ m_xGearBtn->set_sensitive(true);
+ }
+
+ switch (pToolbar->GetStyle())
+ {
+ case 0:
+ {
+ m_xGearBtn->set_item_active("toolbar_gear_iconOnly", true);
+ break;
+ }
+ case 1:
+ {
+ m_xGearBtn->set_item_active("toolbar_gear_textOnly", true);
+ break;
+ }
+ case 2:
+ {
+ m_xGearBtn->set_item_active("toolbar_gear_iconAndText", true);
+ break;
+ }
+ }
+
+ int i = 0;
+ SvxEntries* pEntries = pToolbar->GetEntries();
+ for (auto const& entry : *pEntries)
+ {
+ OUString sId(weld::toId(entry));
+ m_xContentsListBox->insert(i, sId);
+ if (entry->IsBinding() && !entry->IsSeparator())
+ m_xContentsListBox->set_toggle(i, entry->IsVisible() ? TRISTATE_TRUE : TRISTATE_FALSE);
+ InsertEntryIntoUI(entry, m_xContentsListBox->get_widget(), i);
+ ++i;
+ }
+
+ UpdateButtonStates();
+}
+
+void SvxToolbarConfigPage::AddFunction(int nTarget)
+{
+ SvxConfigEntry* pToolbar = GetTopLevelSelection();
+
+ if (pToolbar == nullptr)
+ return;
+
+ // Add the command to the contents listbox of the selected toolbar
+ int nNewLBEntry = SvxConfigPage::AddFunction(nTarget, true /*bAllowDuplicates*/);
+
+ if (nNewLBEntry == -1)
+ return;
+
+ SvxConfigEntry* pEntry = weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nNewLBEntry));
+
+ if (pEntry->IsBinding()) //TODO sep ?
+ {
+ pEntry->SetVisible(true);
+ m_xContentsListBox->set_toggle(nNewLBEntry, TRISTATE_TRUE);
+ }
+
+ InsertEntryIntoUI(pEntry, m_xContentsListBox->get_widget(), nNewLBEntry);
+
+ // Changes are not visible on the toolbar until this point
+ // TODO: Figure out a way to show the changes on the toolbar, but revert if
+ // the dialog is closed by pressing "Cancel"
+ // get currently selected toolbar and apply change
+ if (pToolbar != nullptr)
+ {
+ static_cast<ToolbarSaveInData*>(GetSaveInData())->ApplyToolbar(pToolbar);
+ }
+}
+
+SvxToolbarEntriesListBox::SvxToolbarEntriesListBox(std::unique_ptr<weld::TreeView> xParent,
+ SvxToolbarConfigPage* pPg)
+ : SvxMenuEntriesListBox(std::move(xParent), pPg)
+{
+ m_xControl->connect_toggled(LINK(this, SvxToolbarEntriesListBox, CheckButtonHdl));
+ m_xControl->connect_key_press(
+ Link<const KeyEvent&, bool>()); //acknowledge we first remove the old one
+ m_xControl->connect_key_press(
+ LINK(this, SvxToolbarEntriesListBox, KeyInputHdl)); // then add the new one
+}
+
+SvxToolbarEntriesListBox::~SvxToolbarEntriesListBox() {}
+
+void SvxToolbarEntriesListBox::ChangedVisibility(int nRow)
+{
+ SvxConfigEntry* pEntryData = weld::fromId<SvxConfigEntry*>(m_xControl->get_id(nRow));
+
+ if (pEntryData->IsBinding())
+ {
+ pEntryData->SetVisible(m_xControl->get_toggle(nRow) == TRISTATE_TRUE);
+
+ SvxConfigEntry* pToolbar = m_pPage->GetTopLevelSelection();
+
+ ToolbarSaveInData* pToolbarSaveInData
+ = static_cast<ToolbarSaveInData*>(m_pPage->GetSaveInData());
+
+ pToolbarSaveInData->ApplyToolbar(pToolbar);
+ }
+}
+
+IMPL_LINK(SvxToolbarEntriesListBox, CheckButtonHdl, const weld::TreeView::iter_col&, rRowCol, void)
+{
+ ChangedVisibility(m_xControl->get_iter_index_in_parent(rRowCol.first));
+}
+
+IMPL_LINK(SvxToolbarEntriesListBox, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ // space key will change visibility of toolbar items
+ if (rKeyEvent.GetKeyCode() == KEY_SPACE)
+ {
+ int nRow = m_xControl->get_selected_index();
+ SvxConfigEntry* pEntryData = weld::fromId<SvxConfigEntry*>(m_xControl->get_id(nRow));
+ if (pEntryData->IsBinding() && !pEntryData->IsSeparator())
+ {
+ m_xControl->set_toggle(nRow, m_xControl->get_toggle(nRow) == TRISTATE_TRUE
+ ? TRISTATE_FALSE
+ : TRISTATE_TRUE);
+ ChangedVisibility(nRow);
+ }
+ return true;
+ }
+ return SvxMenuEntriesListBox::KeyInputHdl(rKeyEvent);
+}
+
+IMPL_LINK(SvxToolbarConfigPage, ContentContextMenuHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
+
+ // Select clicked entry
+ std::unique_ptr<weld::TreeIter> xIter(rTreeView.make_iterator());
+ if (!rTreeView.get_dest_row_at_pos(rCEvt.GetMousePosPixel(), xIter.get(), false))
+ return false;
+ rTreeView.select(*xIter);
+ SelectToolbarEntry(rTreeView);
+
+ int nSelectIndex = m_xContentsListBox->get_selected_index();
+
+ bool bIsSeparator
+ = nSelectIndex != -1
+ && weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nSelectIndex))->IsSeparator();
+ bool bIsValidSelection = (m_xContentsListBox->n_children() != 0 && nSelectIndex != -1);
+
+ std::unique_ptr<weld::Builder> xBuilder(
+ Application::CreateBuilder(&rTreeView, "cui/ui/entrycontextmenu.ui"));
+ auto xContextMenu = xBuilder->weld_menu("menu");
+ xContextMenu->set_visible("add", false);
+ xContextMenu->set_visible("remove", bIsValidSelection);
+ xContextMenu->set_visible("rename", bIsValidSelection && !bIsSeparator);
+ xContextMenu->set_visible("changeIcon", bIsValidSelection && !bIsSeparator);
+ xContextMenu->set_visible("resetIcon", bIsValidSelection && !bIsSeparator);
+ xContextMenu->set_visible("restoreDefault", bIsValidSelection && !bIsSeparator);
+ OString sCommand(xContextMenu->popup_at_rect(
+ &rTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1))));
+
+ if (sCommand == "remove")
+ RemoveCommandHdl(*m_xRemoveCommandButton);
+ else if (sCommand == "rename")
+ ModifyItemHdl("renameItem");
+ else if (sCommand == "changeIcon")
+ ModifyItemHdl("changeIcon");
+ else if (sCommand == "resetIcon")
+ ModifyItemHdl("resetIcon");
+ else if (sCommand == "restoreDefault")
+ ModifyItemHdl("restoreItem");
+ else if (!sCommand.isEmpty())
+ SAL_WARN("cui.customize", "Unknown context menu action: " << sCommand);
+ return true;
+}
+
+IMPL_LINK(SvxToolbarConfigPage, FunctionContextMenuHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ weld::TreeView& rTreeView = m_xFunctions->get_widget();
+
+ // Select clicked entry
+ std::unique_ptr<weld::TreeIter> xIter(rTreeView.make_iterator());
+ if (!rTreeView.get_dest_row_at_pos(rCEvt.GetMousePosPixel(), xIter.get(), false))
+ return false;
+ rTreeView.select(*xIter);
+ SelectFunctionHdl(rTreeView);
+ std::unique_ptr<weld::Builder> xBuilder(
+ Application::CreateBuilder(&rTreeView, "cui/ui/entrycontextmenu.ui"));
+ auto xContextMenu = xBuilder->weld_menu("menu");
+ xContextMenu->set_visible("add", true);
+ xContextMenu->set_visible("remove", false);
+ xContextMenu->set_visible("rename", false);
+ xContextMenu->set_visible("changeIcon", false);
+ xContextMenu->set_visible("resetIcon", false);
+ xContextMenu->set_visible("restoreDefault", false);
+ OString sCommand(xContextMenu->popup_at_rect(
+ &rTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1))));
+
+ if (sCommand == "add")
+ AddCommandHdl(*m_xAddCommandButton);
+ else if (!sCommand.isEmpty())
+ SAL_WARN("cui.customize", "Unknown context menu action: " << sCommand);
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/customize/acccfg.cxx b/cui/source/customize/acccfg.cxx
new file mode 100644
index 000000000..080cab981
--- /dev/null
+++ b/cui/source/customize/acccfg.cxx
@@ -0,0 +1,1600 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// include own files
+
+#include <acccfg.hxx>
+#include <cfgutil.hxx>
+#include <dialmgr.hxx>
+
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/minfitem.hxx>
+#include <sfx2/sfxresid.hxx>
+
+#include <sal/macros.h>
+#include <vcl/event.hxx>
+
+#include <strings.hrc>
+#include <sfx2/strings.hrc>
+#include <svx/svxids.hrc>
+
+// include interface declarations
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/StorageFactory.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/form/XReset.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/theUICommandDescription.hpp>
+#include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/UIConfigurationManager.hpp>
+#include <com/sun/star/ui/XUIConfigurationManager.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+
+// include search util
+#include <com/sun/star/util/SearchFlags.hpp>
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+#include <unotools/textsearch.hxx>
+
+// include other projects
+#include <comphelper/processfactory.hxx>
+#include <svtools/acceleratorexecute.hxx>
+#include <vcl/svapp.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <config_features.h>
+
+// namespaces
+
+using namespace css;
+
+constexpr OUStringLiteral FOLDERNAME_UICONFIG = u"Configurations2";
+
+constexpr OUStringLiteral MEDIATYPE_PROPNAME = u"MediaType";
+
+const sal_uInt16 KEYCODE_ARRAY[] = { KEY_F1,
+ KEY_F2,
+ KEY_F3,
+ KEY_F4,
+ KEY_F5,
+ KEY_F6,
+ KEY_F7,
+ KEY_F8,
+ KEY_F9,
+ KEY_F10,
+ KEY_F11,
+ KEY_F12,
+ KEY_F13,
+ KEY_F14,
+ KEY_F15,
+ KEY_F16,
+
+ KEY_DOWN,
+ KEY_UP,
+ KEY_LEFT,
+ KEY_RIGHT,
+ KEY_HOME,
+ KEY_END,
+ KEY_PAGEUP,
+ KEY_PAGEDOWN,
+ KEY_RETURN,
+ KEY_ESCAPE,
+ KEY_BACKSPACE,
+ KEY_INSERT,
+ KEY_DELETE,
+
+ KEY_OPEN,
+ KEY_CUT,
+ KEY_COPY,
+ KEY_PASTE,
+ KEY_UNDO,
+ KEY_REPEAT,
+ KEY_FIND,
+ KEY_PROPERTIES,
+ KEY_FRONT,
+ KEY_CONTEXTMENU,
+ KEY_MENU,
+ KEY_HELP,
+
+ KEY_SHIFT | KEY_F1,
+ KEY_SHIFT | KEY_F2,
+ KEY_SHIFT | KEY_F3,
+ KEY_SHIFT | KEY_F4,
+ KEY_SHIFT | KEY_F5,
+ KEY_SHIFT | KEY_F6,
+ KEY_SHIFT | KEY_F7,
+ KEY_SHIFT | KEY_F8,
+ KEY_SHIFT | KEY_F9,
+ KEY_SHIFT | KEY_F10,
+ KEY_SHIFT | KEY_F11,
+ KEY_SHIFT | KEY_F12,
+ KEY_SHIFT | KEY_F13,
+ KEY_SHIFT | KEY_F14,
+ KEY_SHIFT | KEY_F15,
+ KEY_SHIFT | KEY_F16,
+
+ KEY_SHIFT | KEY_DOWN,
+ KEY_SHIFT | KEY_UP,
+ KEY_SHIFT | KEY_LEFT,
+ KEY_SHIFT | KEY_RIGHT,
+ KEY_SHIFT | KEY_HOME,
+ KEY_SHIFT | KEY_END,
+ KEY_SHIFT | KEY_PAGEUP,
+ KEY_SHIFT | KEY_PAGEDOWN,
+ KEY_SHIFT | KEY_RETURN,
+ KEY_SHIFT | KEY_SPACE,
+ KEY_SHIFT | KEY_ESCAPE,
+ KEY_SHIFT | KEY_BACKSPACE,
+ KEY_SHIFT | KEY_INSERT,
+ KEY_SHIFT | KEY_DELETE,
+ KEY_SHIFT | KEY_EQUAL,
+
+ KEY_MOD1 | KEY_0,
+ KEY_MOD1 | KEY_1,
+ KEY_MOD1 | KEY_2,
+ KEY_MOD1 | KEY_3,
+ KEY_MOD1 | KEY_4,
+ KEY_MOD1 | KEY_5,
+ KEY_MOD1 | KEY_6,
+ KEY_MOD1 | KEY_7,
+ KEY_MOD1 | KEY_8,
+ KEY_MOD1 | KEY_9,
+ KEY_MOD1 | KEY_A,
+ KEY_MOD1 | KEY_B,
+ KEY_MOD1 | KEY_C,
+ KEY_MOD1 | KEY_D,
+ KEY_MOD1 | KEY_E,
+ KEY_MOD1 | KEY_F,
+ KEY_MOD1 | KEY_G,
+ KEY_MOD1 | KEY_H,
+ KEY_MOD1 | KEY_I,
+ KEY_MOD1 | KEY_J,
+ KEY_MOD1 | KEY_K,
+ KEY_MOD1 | KEY_L,
+ KEY_MOD1 | KEY_M,
+ KEY_MOD1 | KEY_N,
+ KEY_MOD1 | KEY_O,
+ KEY_MOD1 | KEY_P,
+ KEY_MOD1 | KEY_Q,
+ KEY_MOD1 | KEY_R,
+ KEY_MOD1 | KEY_S,
+ KEY_MOD1 | KEY_T,
+ KEY_MOD1 | KEY_U,
+ KEY_MOD1 | KEY_V,
+ KEY_MOD1 | KEY_W,
+ KEY_MOD1 | KEY_X,
+ KEY_MOD1 | KEY_Y,
+ KEY_MOD1 | KEY_Z,
+ KEY_MOD1 | KEY_SEMICOLON,
+ KEY_MOD1 | KEY_QUOTELEFT,
+ KEY_MOD1 | KEY_QUOTERIGHT,
+ KEY_MOD1 | KEY_BRACKETLEFT,
+ KEY_MOD1 | KEY_BRACKETRIGHT,
+ KEY_MOD1 | KEY_POINT,
+ KEY_MOD1 | KEY_COMMA,
+ KEY_MOD1 | KEY_TILDE,
+ KEY_MOD1 | KEY_TAB,
+
+ KEY_MOD1 | KEY_F1,
+ KEY_MOD1 | KEY_F2,
+ KEY_MOD1 | KEY_F3,
+ KEY_MOD1 | KEY_F4,
+ KEY_MOD1 | KEY_F5,
+ KEY_MOD1 | KEY_F6,
+ KEY_MOD1 | KEY_F7,
+ KEY_MOD1 | KEY_F8,
+ KEY_MOD1 | KEY_F9,
+ KEY_MOD1 | KEY_F10,
+ KEY_MOD1 | KEY_F11,
+ KEY_MOD1 | KEY_F12,
+ KEY_MOD1 | KEY_F13,
+ KEY_MOD1 | KEY_F14,
+ KEY_MOD1 | KEY_F15,
+ KEY_MOD1 | KEY_F16,
+
+ KEY_MOD1 | KEY_DOWN,
+ KEY_MOD1 | KEY_UP,
+ KEY_MOD1 | KEY_LEFT,
+ KEY_MOD1 | KEY_RIGHT,
+ KEY_MOD1 | KEY_HOME,
+ KEY_MOD1 | KEY_END,
+ KEY_MOD1 | KEY_PAGEUP,
+ KEY_MOD1 | KEY_PAGEDOWN,
+ KEY_MOD1 | KEY_RETURN,
+ KEY_MOD1 | KEY_SPACE,
+ KEY_MOD1 | KEY_BACKSPACE,
+ KEY_MOD1 | KEY_INSERT,
+ KEY_MOD1 | KEY_DELETE,
+
+ KEY_MOD1 | KEY_ADD,
+ KEY_MOD1 | KEY_SUBTRACT,
+ KEY_MOD1 | KEY_MULTIPLY,
+ KEY_MOD1 | KEY_DIVIDE,
+ KEY_MOD1 | KEY_EQUAL,
+
+ KEY_SHIFT | KEY_MOD1 | KEY_0,
+ KEY_SHIFT | KEY_MOD1 | KEY_1,
+ KEY_SHIFT | KEY_MOD1 | KEY_2,
+ KEY_SHIFT | KEY_MOD1 | KEY_3,
+ KEY_SHIFT | KEY_MOD1 | KEY_4,
+ KEY_SHIFT | KEY_MOD1 | KEY_5,
+ KEY_SHIFT | KEY_MOD1 | KEY_6,
+ KEY_SHIFT | KEY_MOD1 | KEY_7,
+ KEY_SHIFT | KEY_MOD1 | KEY_8,
+ KEY_SHIFT | KEY_MOD1 | KEY_9,
+ KEY_SHIFT | KEY_MOD1 | KEY_A,
+ KEY_SHIFT | KEY_MOD1 | KEY_B,
+ KEY_SHIFT | KEY_MOD1 | KEY_C,
+ KEY_SHIFT | KEY_MOD1 | KEY_D,
+ KEY_SHIFT | KEY_MOD1 | KEY_E,
+ KEY_SHIFT | KEY_MOD1 | KEY_F,
+ KEY_SHIFT | KEY_MOD1 | KEY_G,
+ KEY_SHIFT | KEY_MOD1 | KEY_H,
+ KEY_SHIFT | KEY_MOD1 | KEY_I,
+ KEY_SHIFT | KEY_MOD1 | KEY_J,
+ KEY_SHIFT | KEY_MOD1 | KEY_K,
+ KEY_SHIFT | KEY_MOD1 | KEY_L,
+ KEY_SHIFT | KEY_MOD1 | KEY_M,
+ KEY_SHIFT | KEY_MOD1 | KEY_N,
+ KEY_SHIFT | KEY_MOD1 | KEY_O,
+ KEY_SHIFT | KEY_MOD1 | KEY_P,
+ KEY_SHIFT | KEY_MOD1 | KEY_Q,
+ KEY_SHIFT | KEY_MOD1 | KEY_R,
+ KEY_SHIFT | KEY_MOD1 | KEY_S,
+ KEY_SHIFT | KEY_MOD1 | KEY_T,
+ KEY_SHIFT | KEY_MOD1 | KEY_U,
+ KEY_SHIFT | KEY_MOD1 | KEY_V,
+ KEY_SHIFT | KEY_MOD1 | KEY_W,
+ KEY_SHIFT | KEY_MOD1 | KEY_X,
+ KEY_SHIFT | KEY_MOD1 | KEY_Y,
+ KEY_SHIFT | KEY_MOD1 | KEY_Z,
+ KEY_SHIFT | KEY_MOD1 | KEY_SEMICOLON,
+ KEY_SHIFT | KEY_MOD1 | KEY_QUOTELEFT,
+ KEY_SHIFT | KEY_MOD1 | KEY_QUOTERIGHT,
+ KEY_SHIFT | KEY_MOD1 | KEY_BRACKETLEFT,
+ KEY_SHIFT | KEY_MOD1 | KEY_BRACKETRIGHT,
+ KEY_SHIFT | KEY_MOD1 | KEY_POINT,
+ KEY_SHIFT | KEY_MOD1 | KEY_COMMA,
+ KEY_SHIFT | KEY_MOD1 | KEY_TILDE,
+ KEY_SHIFT | KEY_MOD1 | KEY_TAB,
+
+ KEY_SHIFT | KEY_MOD1 | KEY_F1,
+ KEY_SHIFT | KEY_MOD1 | KEY_F2,
+ KEY_SHIFT | KEY_MOD1 | KEY_F3,
+ KEY_SHIFT | KEY_MOD1 | KEY_F4,
+ KEY_SHIFT | KEY_MOD1 | KEY_F5,
+ KEY_SHIFT | KEY_MOD1 | KEY_F6,
+ KEY_SHIFT | KEY_MOD1 | KEY_F7,
+ KEY_SHIFT | KEY_MOD1 | KEY_F8,
+ KEY_SHIFT | KEY_MOD1 | KEY_F9,
+ KEY_SHIFT | KEY_MOD1 | KEY_F10,
+ KEY_SHIFT | KEY_MOD1 | KEY_F11,
+ KEY_SHIFT | KEY_MOD1 | KEY_F12,
+ KEY_SHIFT | KEY_MOD1 | KEY_F13,
+ KEY_SHIFT | KEY_MOD1 | KEY_F14,
+ KEY_SHIFT | KEY_MOD1 | KEY_F15,
+ KEY_SHIFT | KEY_MOD1 | KEY_F16,
+
+ KEY_SHIFT | KEY_MOD1 | KEY_DOWN,
+ KEY_SHIFT | KEY_MOD1 | KEY_UP,
+ KEY_SHIFT | KEY_MOD1 | KEY_LEFT,
+ KEY_SHIFT | KEY_MOD1 | KEY_RIGHT,
+ KEY_SHIFT | KEY_MOD1 | KEY_HOME,
+ KEY_SHIFT | KEY_MOD1 | KEY_END,
+ KEY_SHIFT | KEY_MOD1 | KEY_PAGEUP,
+ KEY_SHIFT | KEY_MOD1 | KEY_PAGEDOWN,
+ KEY_SHIFT | KEY_MOD1 | KEY_RETURN,
+ KEY_SHIFT | KEY_MOD1 | KEY_ESCAPE,
+ KEY_SHIFT | KEY_MOD1 | KEY_SPACE,
+ KEY_SHIFT | KEY_MOD1 | KEY_BACKSPACE,
+ KEY_SHIFT | KEY_MOD1 | KEY_INSERT,
+ KEY_SHIFT | KEY_MOD1 | KEY_DELETE,
+ KEY_SHIFT | KEY_MOD1 | KEY_EQUAL,
+
+ KEY_MOD2 | KEY_0,
+ KEY_MOD2 | KEY_1,
+ KEY_MOD2 | KEY_2,
+ KEY_MOD2 | KEY_3,
+ KEY_MOD2 | KEY_4,
+ KEY_MOD2 | KEY_5,
+ KEY_MOD2 | KEY_6,
+ KEY_MOD2 | KEY_7,
+ KEY_MOD2 | KEY_8,
+ KEY_MOD2 | KEY_9,
+ KEY_MOD2 | KEY_A,
+ KEY_MOD2 | KEY_B,
+ KEY_MOD2 | KEY_C,
+ KEY_MOD2 | KEY_D,
+ KEY_MOD2 | KEY_E,
+ KEY_MOD2 | KEY_F,
+ KEY_MOD2 | KEY_G,
+ KEY_MOD2 | KEY_H,
+ KEY_MOD2 | KEY_I,
+ KEY_MOD2 | KEY_J,
+ KEY_MOD2 | KEY_K,
+ KEY_MOD2 | KEY_L,
+ KEY_MOD2 | KEY_M,
+ KEY_MOD2 | KEY_N,
+ KEY_MOD2 | KEY_O,
+ KEY_MOD2 | KEY_P,
+ KEY_MOD2 | KEY_Q,
+ KEY_MOD2 | KEY_R,
+ KEY_MOD2 | KEY_S,
+ KEY_MOD2 | KEY_T,
+ KEY_MOD2 | KEY_U,
+ KEY_MOD2 | KEY_V,
+ KEY_MOD2 | KEY_W,
+ KEY_MOD2 | KEY_X,
+ KEY_MOD2 | KEY_Y,
+ KEY_MOD2 | KEY_Z,
+ KEY_MOD2 | KEY_SEMICOLON,
+ KEY_MOD2 | KEY_QUOTELEFT,
+ KEY_MOD2 | KEY_QUOTERIGHT,
+ KEY_MOD2 | KEY_BRACKETLEFT,
+ KEY_MOD2 | KEY_BRACKETRIGHT,
+ KEY_MOD2 | KEY_POINT,
+ KEY_MOD2 | KEY_COMMA,
+ KEY_MOD2 | KEY_TILDE,
+
+ KEY_MOD2 | KEY_F1,
+ KEY_MOD2 | KEY_F2,
+ KEY_MOD2 | KEY_F3,
+ KEY_MOD2 | KEY_F4,
+ KEY_MOD2 | KEY_F5,
+ KEY_MOD2 | KEY_F6,
+ KEY_MOD2 | KEY_F7,
+ KEY_MOD2 | KEY_F8,
+ KEY_MOD2 | KEY_F9,
+ KEY_MOD2 | KEY_F10,
+ KEY_MOD2 | KEY_F11,
+ KEY_MOD2 | KEY_F12,
+ KEY_MOD2 | KEY_F13,
+ KEY_MOD2 | KEY_F14,
+ KEY_MOD2 | KEY_F15,
+ KEY_MOD2 | KEY_F16,
+
+ KEY_MOD2 | KEY_DOWN,
+ KEY_MOD2 | KEY_UP,
+ KEY_MOD2 | KEY_LEFT,
+ KEY_MOD2 | KEY_RIGHT,
+ KEY_MOD2 | KEY_HOME,
+ KEY_MOD2 | KEY_END,
+ KEY_MOD2 | KEY_PAGEUP,
+ KEY_MOD2 | KEY_PAGEDOWN,
+ KEY_MOD2 | KEY_RETURN,
+ KEY_MOD2 | KEY_SPACE,
+ KEY_MOD2 | KEY_BACKSPACE,
+ KEY_MOD2 | KEY_INSERT,
+ KEY_MOD2 | KEY_DELETE,
+ KEY_MOD2 | KEY_EQUAL,
+
+ KEY_SHIFT | KEY_MOD2 | KEY_0,
+ KEY_SHIFT | KEY_MOD2 | KEY_1,
+ KEY_SHIFT | KEY_MOD2 | KEY_2,
+ KEY_SHIFT | KEY_MOD2 | KEY_3,
+ KEY_SHIFT | KEY_MOD2 | KEY_4,
+ KEY_SHIFT | KEY_MOD2 | KEY_5,
+ KEY_SHIFT | KEY_MOD2 | KEY_6,
+ KEY_SHIFT | KEY_MOD2 | KEY_7,
+ KEY_SHIFT | KEY_MOD2 | KEY_8,
+ KEY_SHIFT | KEY_MOD2 | KEY_9,
+ KEY_SHIFT | KEY_MOD2 | KEY_A,
+ KEY_SHIFT | KEY_MOD2 | KEY_B,
+ KEY_SHIFT | KEY_MOD2 | KEY_C,
+ KEY_SHIFT | KEY_MOD2 | KEY_D,
+ KEY_SHIFT | KEY_MOD2 | KEY_E,
+ KEY_SHIFT | KEY_MOD2 | KEY_F,
+ KEY_SHIFT | KEY_MOD2 | KEY_G,
+ KEY_SHIFT | KEY_MOD2 | KEY_H,
+ KEY_SHIFT | KEY_MOD2 | KEY_I,
+ KEY_SHIFT | KEY_MOD2 | KEY_J,
+ KEY_SHIFT | KEY_MOD2 | KEY_K,
+ KEY_SHIFT | KEY_MOD2 | KEY_L,
+ KEY_SHIFT | KEY_MOD2 | KEY_M,
+ KEY_SHIFT | KEY_MOD2 | KEY_N,
+ KEY_SHIFT | KEY_MOD2 | KEY_O,
+ KEY_SHIFT | KEY_MOD2 | KEY_P,
+ KEY_SHIFT | KEY_MOD2 | KEY_Q,
+ KEY_SHIFT | KEY_MOD2 | KEY_R,
+ KEY_SHIFT | KEY_MOD2 | KEY_S,
+ KEY_SHIFT | KEY_MOD2 | KEY_T,
+ KEY_SHIFT | KEY_MOD2 | KEY_U,
+ KEY_SHIFT | KEY_MOD2 | KEY_V,
+ KEY_SHIFT | KEY_MOD2 | KEY_W,
+ KEY_SHIFT | KEY_MOD2 | KEY_X,
+ KEY_SHIFT | KEY_MOD2 | KEY_Y,
+ KEY_SHIFT | KEY_MOD2 | KEY_Z,
+ KEY_SHIFT | KEY_MOD2 | KEY_SEMICOLON,
+ KEY_SHIFT | KEY_MOD2 | KEY_QUOTELEFT,
+ KEY_SHIFT | KEY_MOD2 | KEY_QUOTERIGHT,
+ KEY_SHIFT | KEY_MOD2 | KEY_BRACKETLEFT,
+ KEY_SHIFT | KEY_MOD2 | KEY_BRACKETRIGHT,
+ KEY_SHIFT | KEY_MOD2 | KEY_POINT,
+ KEY_SHIFT | KEY_MOD2 | KEY_COMMA,
+ KEY_SHIFT | KEY_MOD2 | KEY_TILDE,
+
+ KEY_SHIFT | KEY_MOD2 | KEY_F1,
+ KEY_SHIFT | KEY_MOD2 | KEY_F2,
+ KEY_SHIFT | KEY_MOD2 | KEY_F3,
+ KEY_SHIFT | KEY_MOD2 | KEY_F4,
+ KEY_SHIFT | KEY_MOD2 | KEY_F5,
+ KEY_SHIFT | KEY_MOD2 | KEY_F6,
+ KEY_SHIFT | KEY_MOD2 | KEY_F7,
+ KEY_SHIFT | KEY_MOD2 | KEY_F8,
+ KEY_SHIFT | KEY_MOD2 | KEY_F9,
+ KEY_SHIFT | KEY_MOD2 | KEY_F10,
+ KEY_SHIFT | KEY_MOD2 | KEY_F11,
+ KEY_SHIFT | KEY_MOD2 | KEY_F12,
+ KEY_SHIFT | KEY_MOD2 | KEY_F13,
+ KEY_SHIFT | KEY_MOD2 | KEY_F14,
+ KEY_SHIFT | KEY_MOD2 | KEY_F15,
+ KEY_SHIFT | KEY_MOD2 | KEY_F16,
+
+ KEY_SHIFT | KEY_MOD2 | KEY_DOWN,
+ KEY_SHIFT | KEY_MOD2 | KEY_UP,
+ KEY_SHIFT | KEY_MOD2 | KEY_LEFT,
+ KEY_SHIFT | KEY_MOD2 | KEY_RIGHT,
+ KEY_SHIFT | KEY_MOD2 | KEY_HOME,
+ KEY_SHIFT | KEY_MOD2 | KEY_END,
+ KEY_SHIFT | KEY_MOD2 | KEY_PAGEUP,
+ KEY_SHIFT | KEY_MOD2 | KEY_PAGEDOWN,
+ KEY_SHIFT | KEY_MOD2 | KEY_RETURN,
+ KEY_SHIFT | KEY_MOD2 | KEY_ESCAPE,
+ KEY_SHIFT | KEY_MOD2 | KEY_SPACE,
+ KEY_SHIFT | KEY_MOD2 | KEY_BACKSPACE,
+ KEY_SHIFT | KEY_MOD2 | KEY_INSERT,
+ KEY_SHIFT | KEY_MOD2 | KEY_DELETE,
+ KEY_SHIFT | KEY_MOD2 | KEY_EQUAL,
+
+ KEY_MOD1 | KEY_MOD2 | KEY_0,
+ KEY_MOD1 | KEY_MOD2 | KEY_1,
+ KEY_MOD1 | KEY_MOD2 | KEY_2,
+ KEY_MOD1 | KEY_MOD2 | KEY_3,
+ KEY_MOD1 | KEY_MOD2 | KEY_4,
+ KEY_MOD1 | KEY_MOD2 | KEY_5,
+ KEY_MOD1 | KEY_MOD2 | KEY_6,
+ KEY_MOD1 | KEY_MOD2 | KEY_7,
+ KEY_MOD1 | KEY_MOD2 | KEY_8,
+ KEY_MOD1 | KEY_MOD2 | KEY_9,
+ KEY_MOD1 | KEY_MOD2 | KEY_A,
+ KEY_MOD1 | KEY_MOD2 | KEY_B,
+ KEY_MOD1 | KEY_MOD2 | KEY_C,
+ KEY_MOD1 | KEY_MOD2 | KEY_D,
+ KEY_MOD1 | KEY_MOD2 | KEY_E,
+ KEY_MOD1 | KEY_MOD2 | KEY_F,
+ KEY_MOD1 | KEY_MOD2 | KEY_G,
+ KEY_MOD1 | KEY_MOD2 | KEY_H,
+ KEY_MOD1 | KEY_MOD2 | KEY_I,
+ KEY_MOD1 | KEY_MOD2 | KEY_J,
+ KEY_MOD1 | KEY_MOD2 | KEY_K,
+ KEY_MOD1 | KEY_MOD2 | KEY_L,
+ KEY_MOD1 | KEY_MOD2 | KEY_M,
+ KEY_MOD1 | KEY_MOD2 | KEY_N,
+ KEY_MOD1 | KEY_MOD2 | KEY_O,
+ KEY_MOD1 | KEY_MOD2 | KEY_P,
+ KEY_MOD1 | KEY_MOD2 | KEY_Q,
+ KEY_MOD1 | KEY_MOD2 | KEY_R,
+ KEY_MOD1 | KEY_MOD2 | KEY_S,
+ KEY_MOD1 | KEY_MOD2 | KEY_T,
+ KEY_MOD1 | KEY_MOD2 | KEY_U,
+ KEY_MOD1 | KEY_MOD2 | KEY_V,
+ KEY_MOD1 | KEY_MOD2 | KEY_W,
+ KEY_MOD1 | KEY_MOD2 | KEY_X,
+ KEY_MOD1 | KEY_MOD2 | KEY_Y,
+ KEY_MOD1 | KEY_MOD2 | KEY_Z,
+ KEY_MOD1 | KEY_MOD2 | KEY_SEMICOLON,
+ KEY_MOD1 | KEY_MOD2 | KEY_QUOTELEFT,
+ KEY_MOD1 | KEY_MOD2 | KEY_QUOTERIGHT,
+ KEY_MOD1 | KEY_MOD2 | KEY_BRACKETLEFT,
+ KEY_MOD1 | KEY_MOD2 | KEY_BRACKETRIGHT,
+ KEY_MOD1 | KEY_MOD2 | KEY_POINT,
+ KEY_MOD1 | KEY_MOD2 | KEY_COMMA,
+ KEY_MOD1 | KEY_MOD2 | KEY_TILDE,
+ KEY_MOD1 | KEY_MOD2 | KEY_EQUAL,
+
+ KEY_MOD1 | KEY_MOD2 | KEY_F1,
+ KEY_MOD1 | KEY_MOD2 | KEY_F2,
+ KEY_MOD1 | KEY_MOD2 | KEY_F3,
+ KEY_MOD1 | KEY_MOD2 | KEY_F4,
+ KEY_MOD1 | KEY_MOD2 | KEY_F5,
+ KEY_MOD1 | KEY_MOD2 | KEY_F6,
+ KEY_MOD1 | KEY_MOD2 | KEY_F7,
+ KEY_MOD1 | KEY_MOD2 | KEY_F8,
+ KEY_MOD1 | KEY_MOD2 | KEY_F9,
+ KEY_MOD1 | KEY_MOD2 | KEY_F10,
+ KEY_MOD1 | KEY_MOD2 | KEY_F11,
+ KEY_MOD1 | KEY_MOD2 | KEY_F12,
+ KEY_MOD1 | KEY_MOD2 | KEY_F13,
+ KEY_MOD1 | KEY_MOD2 | KEY_F14,
+ KEY_MOD1 | KEY_MOD2 | KEY_F15,
+ KEY_MOD1 | KEY_MOD2 | KEY_F16,
+
+ KEY_MOD1 | KEY_MOD2 | KEY_DOWN,
+ KEY_MOD1 | KEY_MOD2 | KEY_UP,
+ KEY_MOD1 | KEY_MOD2 | KEY_LEFT,
+ KEY_MOD1 | KEY_MOD2 | KEY_RIGHT,
+ KEY_MOD1 | KEY_MOD2 | KEY_HOME,
+ KEY_MOD1 | KEY_MOD2 | KEY_END,
+ KEY_MOD1 | KEY_MOD2 | KEY_PAGEUP,
+ KEY_MOD1 | KEY_MOD2 | KEY_PAGEDOWN,
+ KEY_MOD1 | KEY_MOD2 | KEY_RETURN,
+ KEY_MOD1 | KEY_MOD2 | KEY_SPACE,
+ KEY_MOD1 | KEY_MOD2 | KEY_BACKSPACE,
+ KEY_MOD1 | KEY_MOD2 | KEY_INSERT,
+ KEY_MOD1 | KEY_MOD2 | KEY_DELETE,
+
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_0,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_1,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_2,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_3,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_4,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_5,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_6,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_7,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_8,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_9,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_A,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_B,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_C,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_D,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_E,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_G,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_H,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_I,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_J,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_K,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_L,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_M,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_N,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_O,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_P,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_Q,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_R,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_S,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_T,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_U,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_V,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_W,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_X,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_Y,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_Z,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_SEMICOLON,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_QUOTELEFT,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_QUOTERIGHT,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_BRACKETLEFT,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_BRACKETRIGHT,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_POINT,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_COMMA,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_TILDE,
+
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F1,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F2,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F3,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F4,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F5,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F6,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F7,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F8,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F9,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F10,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F11,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F12,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F13,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F14,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F15,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_F16,
+
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_DOWN,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_UP,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_LEFT,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_RIGHT,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_HOME,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_END,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_PAGEUP,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_PAGEDOWN,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_RETURN,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_SPACE,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_BACKSPACE,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_INSERT,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_DELETE,
+ KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_EQUAL
+
+#ifdef __APPLE__
+ ,
+ KEY_MOD3 | KEY_0,
+ KEY_MOD3 | KEY_1,
+ KEY_MOD3 | KEY_2,
+ KEY_MOD3 | KEY_3,
+ KEY_MOD3 | KEY_4,
+ KEY_MOD3 | KEY_5,
+ KEY_MOD3 | KEY_6,
+ KEY_MOD3 | KEY_7,
+ KEY_MOD3 | KEY_8,
+ KEY_MOD3 | KEY_9,
+ KEY_MOD3 | KEY_A,
+ KEY_MOD3 | KEY_B,
+ KEY_MOD3 | KEY_C,
+ KEY_MOD3 | KEY_D,
+ KEY_MOD3 | KEY_E,
+ KEY_MOD3 | KEY_F,
+ KEY_MOD3 | KEY_G,
+ KEY_MOD3 | KEY_H,
+ KEY_MOD3 | KEY_I,
+ KEY_MOD3 | KEY_J,
+ KEY_MOD3 | KEY_K,
+ KEY_MOD3 | KEY_L,
+ KEY_MOD3 | KEY_M,
+ KEY_MOD3 | KEY_N,
+ KEY_MOD3 | KEY_O,
+ KEY_MOD3 | KEY_P,
+ KEY_MOD3 | KEY_Q,
+ KEY_MOD3 | KEY_R,
+ KEY_MOD3 | KEY_S,
+ KEY_MOD3 | KEY_T,
+ KEY_MOD3 | KEY_U,
+ KEY_MOD3 | KEY_V,
+ KEY_MOD3 | KEY_W,
+ KEY_MOD3 | KEY_X,
+ KEY_MOD3 | KEY_Y,
+ KEY_MOD3 | KEY_Z,
+ KEY_MOD3 | KEY_SEMICOLON,
+ KEY_MOD3 | KEY_QUOTELEFT,
+ KEY_MOD3 | KEY_QUOTERIGHT,
+ KEY_MOD3 | KEY_BRACKETLEFT,
+ KEY_MOD3 | KEY_BRACKETRIGHT,
+ KEY_MOD3 | KEY_POINT,
+ KEY_MOD3 | KEY_COMMA,
+ KEY_MOD3 | KEY_TILDE,
+ KEY_MOD3 | KEY_TAB,
+
+ KEY_MOD3 | KEY_F1,
+ KEY_MOD3 | KEY_F2,
+ KEY_MOD3 | KEY_F3,
+ KEY_MOD3 | KEY_F4,
+ KEY_MOD3 | KEY_F5,
+ KEY_MOD3 | KEY_F6,
+ KEY_MOD3 | KEY_F7,
+ KEY_MOD3 | KEY_F8,
+ KEY_MOD3 | KEY_F9,
+ KEY_MOD3 | KEY_F10,
+ KEY_MOD3 | KEY_F11,
+ KEY_MOD3 | KEY_F12,
+ KEY_MOD3 | KEY_F13,
+ KEY_MOD3 | KEY_F14,
+ KEY_MOD3 | KEY_F15,
+ KEY_MOD3 | KEY_F16,
+
+ KEY_MOD3 | KEY_DOWN,
+ KEY_MOD3 | KEY_UP,
+ KEY_MOD3 | KEY_LEFT,
+ KEY_MOD3 | KEY_RIGHT,
+ KEY_MOD3 | KEY_HOME,
+ KEY_MOD3 | KEY_END,
+ KEY_MOD3 | KEY_PAGEUP,
+ KEY_MOD3 | KEY_PAGEDOWN,
+ KEY_MOD3 | KEY_RETURN,
+ KEY_MOD3 | KEY_SPACE,
+ KEY_MOD3 | KEY_BACKSPACE,
+ KEY_MOD3 | KEY_INSERT,
+ KEY_MOD3 | KEY_DELETE,
+
+ KEY_MOD3 | KEY_ADD,
+ KEY_MOD3 | KEY_SUBTRACT,
+ KEY_MOD3 | KEY_MULTIPLY,
+ KEY_MOD3 | KEY_DIVIDE,
+ KEY_MOD3 | KEY_EQUAL,
+
+ KEY_SHIFT | KEY_MOD3 | KEY_0,
+ KEY_SHIFT | KEY_MOD3 | KEY_1,
+ KEY_SHIFT | KEY_MOD3 | KEY_2,
+ KEY_SHIFT | KEY_MOD3 | KEY_3,
+ KEY_SHIFT | KEY_MOD3 | KEY_4,
+ KEY_SHIFT | KEY_MOD3 | KEY_5,
+ KEY_SHIFT | KEY_MOD3 | KEY_6,
+ KEY_SHIFT | KEY_MOD3 | KEY_7,
+ KEY_SHIFT | KEY_MOD3 | KEY_8,
+ KEY_SHIFT | KEY_MOD3 | KEY_9,
+ KEY_SHIFT | KEY_MOD3 | KEY_A,
+ KEY_SHIFT | KEY_MOD3 | KEY_B,
+ KEY_SHIFT | KEY_MOD3 | KEY_C,
+ KEY_SHIFT | KEY_MOD3 | KEY_D,
+ KEY_SHIFT | KEY_MOD3 | KEY_E,
+ KEY_SHIFT | KEY_MOD3 | KEY_F,
+ KEY_SHIFT | KEY_MOD3 | KEY_G,
+ KEY_SHIFT | KEY_MOD3 | KEY_H,
+ KEY_SHIFT | KEY_MOD3 | KEY_I,
+ KEY_SHIFT | KEY_MOD3 | KEY_J,
+ KEY_SHIFT | KEY_MOD3 | KEY_K,
+ KEY_SHIFT | KEY_MOD3 | KEY_L,
+ KEY_SHIFT | KEY_MOD3 | KEY_M,
+ KEY_SHIFT | KEY_MOD3 | KEY_N,
+ KEY_SHIFT | KEY_MOD3 | KEY_O,
+ KEY_SHIFT | KEY_MOD3 | KEY_P,
+ KEY_SHIFT | KEY_MOD3 | KEY_Q,
+ KEY_SHIFT | KEY_MOD3 | KEY_R,
+ KEY_SHIFT | KEY_MOD3 | KEY_S,
+ KEY_SHIFT | KEY_MOD3 | KEY_T,
+ KEY_SHIFT | KEY_MOD3 | KEY_U,
+ KEY_SHIFT | KEY_MOD3 | KEY_V,
+ KEY_SHIFT | KEY_MOD3 | KEY_W,
+ KEY_SHIFT | KEY_MOD3 | KEY_X,
+ KEY_SHIFT | KEY_MOD3 | KEY_Y,
+ KEY_SHIFT | KEY_MOD3 | KEY_Z,
+ KEY_SHIFT | KEY_MOD3 | KEY_SEMICOLON,
+ KEY_SHIFT | KEY_MOD3 | KEY_QUOTELEFT,
+ KEY_SHIFT | KEY_MOD3 | KEY_QUOTERIGHT,
+ KEY_SHIFT | KEY_MOD3 | KEY_BRACKETLEFT,
+ KEY_SHIFT | KEY_MOD3 | KEY_BRACKETRIGHT,
+ KEY_SHIFT | KEY_MOD3 | KEY_POINT,
+ KEY_SHIFT | KEY_MOD3 | KEY_COMMA,
+ KEY_SHIFT | KEY_MOD3 | KEY_TILDE,
+ KEY_SHIFT | KEY_MOD3 | KEY_TAB,
+
+ KEY_SHIFT | KEY_MOD3 | KEY_F1,
+ KEY_SHIFT | KEY_MOD3 | KEY_F2,
+ KEY_SHIFT | KEY_MOD3 | KEY_F3,
+ KEY_SHIFT | KEY_MOD3 | KEY_F4,
+ KEY_SHIFT | KEY_MOD3 | KEY_F5,
+ KEY_SHIFT | KEY_MOD3 | KEY_F6,
+ KEY_SHIFT | KEY_MOD3 | KEY_F7,
+ KEY_SHIFT | KEY_MOD3 | KEY_F8,
+ KEY_SHIFT | KEY_MOD3 | KEY_F9,
+ KEY_SHIFT | KEY_MOD3 | KEY_F10,
+ KEY_SHIFT | KEY_MOD3 | KEY_F11,
+ KEY_SHIFT | KEY_MOD3 | KEY_F12,
+ KEY_SHIFT | KEY_MOD3 | KEY_F13,
+ KEY_SHIFT | KEY_MOD3 | KEY_F14,
+ KEY_SHIFT | KEY_MOD3 | KEY_F15,
+ KEY_SHIFT | KEY_MOD3 | KEY_F16,
+
+ KEY_SHIFT | KEY_MOD3 | KEY_DOWN,
+ KEY_SHIFT | KEY_MOD3 | KEY_UP,
+ KEY_SHIFT | KEY_MOD3 | KEY_LEFT,
+ KEY_SHIFT | KEY_MOD3 | KEY_RIGHT,
+ KEY_SHIFT | KEY_MOD3 | KEY_HOME,
+ KEY_SHIFT | KEY_MOD3 | KEY_END,
+ KEY_SHIFT | KEY_MOD3 | KEY_PAGEUP,
+ KEY_SHIFT | KEY_MOD3 | KEY_PAGEDOWN,
+ KEY_SHIFT | KEY_MOD3 | KEY_RETURN,
+ KEY_SHIFT | KEY_MOD3 | KEY_ESCAPE,
+ KEY_SHIFT | KEY_MOD3 | KEY_SPACE,
+ KEY_SHIFT | KEY_MOD3 | KEY_BACKSPACE,
+ KEY_SHIFT | KEY_MOD3 | KEY_INSERT,
+ KEY_SHIFT | KEY_MOD3 | KEY_DELETE,
+ KEY_SHIFT | KEY_MOD3 | KEY_EQUAL
+#endif
+};
+
+const sal_uInt16 KEYCODE_ARRAY_SIZE = std::size(KEYCODE_ARRAY);
+
+/** select the entry, which match the current key input ... excepting
+ keys, which are used for the dialog itself.
+ */
+IMPL_LINK(SfxAcceleratorConfigPage, KeyInputHdl, const KeyEvent&, rKey, bool)
+{
+ vcl::KeyCode aCode1 = rKey.GetKeyCode();
+ sal_uInt16 nCode1 = aCode1.GetCode();
+ sal_uInt16 nMod1 = aCode1.GetModifier();
+
+ // is it related to our list box ?
+ if ((nCode1 == KEY_DOWN) || (nCode1 == KEY_UP) || (nCode1 == KEY_LEFT) || (nCode1 == KEY_RIGHT)
+ || (nCode1 == KEY_PAGEUP) || (nCode1 == KEY_PAGEDOWN))
+ // no - handle it as normal dialog input
+ return false;
+
+ for (int i = 0, nCount = m_xEntriesBox->n_children(); i < nCount; ++i)
+ {
+ TAccInfo* pUserData = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(i));
+ if (pUserData)
+ {
+ sal_uInt16 nCode2 = pUserData->m_aKey.GetCode();
+ sal_uInt16 nMod2 = pUserData->m_aKey.GetModifier();
+
+ if (nCode1 == nCode2 && nMod1 == nMod2)
+ {
+ m_xEntriesBox->select(i);
+ m_xEntriesBox->scroll_to_row(i);
+ return true;
+ }
+ }
+ }
+
+ // no - handle it as normal dialog input
+ return false;
+}
+
+SfxAcceleratorConfigPage::SfxAcceleratorConfigPage(weld::Container* pPage,
+ weld::DialogController* pController,
+ const SfxItemSet& aSet)
+ : SfxTabPage(pPage, pController, "cui/ui/accelconfigpage.ui", "AccelConfigPage", &aSet)
+#if HAVE_FEATURE_SCRIPTING
+ , m_pMacroInfoItem()
+#endif
+ , aLoadAccelConfigStr(CuiResId(RID_CUISTR_LOADACCELCONFIG))
+ , aSaveAccelConfigStr(CuiResId(RID_CUISTR_SAVEACCELCONFIG))
+ , aFilterAllStr(SfxResId(STR_SFX_FILTERNAME_ALL))
+ , aFilterCfgStr(CuiResId(RID_CUISTR_FILTERNAME_CFG))
+ , m_bStylesInfoInitialized(false)
+ , m_aUpdateDataTimer("SfxAcceleratorConfigPage UpdateDataTimer")
+ , m_aFillGroupIdle("SfxAcceleratorConfigPage m_aFillGroupIdle")
+ , m_xEntriesBox(m_xBuilder->weld_tree_view("shortcuts"))
+ , m_xOfficeButton(m_xBuilder->weld_radio_button("office"))
+ , m_xModuleButton(m_xBuilder->weld_radio_button("module"))
+ , m_xChangeButton(m_xBuilder->weld_button("change"))
+ , m_xRemoveButton(m_xBuilder->weld_button("delete"))
+ , m_xGroupLBox(new CuiConfigGroupListBox(m_xBuilder->weld_tree_view("category")))
+ , m_xFunctionBox(new CuiConfigFunctionListBox(m_xBuilder->weld_tree_view("function")))
+ , m_xKeyBox(m_xBuilder->weld_tree_view("keys"))
+ , m_xSearchEdit(m_xBuilder->weld_entry("searchEntry"))
+ , m_xLoadButton(m_xBuilder->weld_button("load"))
+ , m_xSaveButton(m_xBuilder->weld_button("save"))
+ , m_xResetButton(m_xBuilder->weld_button("reset"))
+{
+ Size aSize(m_xEntriesBox->get_approximate_digit_width() * 40,
+ m_xEntriesBox->get_height_rows(10));
+ m_xEntriesBox->set_size_request(aSize.Width(), aSize.Height());
+ aSize = Size(m_xEntriesBox->get_approximate_digit_width() * 19,
+ m_xEntriesBox->get_height_rows(9));
+ m_xGroupLBox->set_size_request(aSize.Width(), aSize.Height());
+ aSize = Size(m_xEntriesBox->get_approximate_digit_width() * 21,
+ m_xEntriesBox->get_height_rows(9));
+ m_xFunctionBox->set_size_request(aSize.Width(), aSize.Height());
+ aSize = Size(m_xEntriesBox->get_approximate_digit_width() * 20,
+ m_xEntriesBox->get_height_rows(9));
+ m_xKeyBox->set_size_request(aSize.Width(), aSize.Height());
+
+ // install handler functions
+ m_xChangeButton->connect_clicked(LINK(this, SfxAcceleratorConfigPage, ChangeHdl));
+ m_xRemoveButton->connect_clicked(LINK(this, SfxAcceleratorConfigPage, RemoveHdl));
+ m_xEntriesBox->connect_changed(LINK(this, SfxAcceleratorConfigPage, SelectHdl));
+ m_xEntriesBox->connect_key_press(LINK(this, SfxAcceleratorConfigPage, KeyInputHdl));
+ m_xGroupLBox->connect_changed(LINK(this, SfxAcceleratorConfigPage, SelectHdl));
+ m_xFunctionBox->connect_changed(LINK(this, SfxAcceleratorConfigPage, SelectHdl));
+ m_xKeyBox->connect_changed(LINK(this, SfxAcceleratorConfigPage, SelectHdl));
+ m_xLoadButton->connect_clicked(LINK(this, SfxAcceleratorConfigPage, Load));
+ m_xSaveButton->connect_clicked(LINK(this, SfxAcceleratorConfigPage, Save));
+ m_xResetButton->connect_clicked(LINK(this, SfxAcceleratorConfigPage, Default));
+ m_xOfficeButton->connect_toggled(LINK(this, SfxAcceleratorConfigPage, RadioHdl));
+ m_xSearchEdit->connect_changed(LINK(this, SfxAcceleratorConfigPage, SearchUpdateHdl));
+ m_xSearchEdit->connect_focus_out(LINK(this, SfxAcceleratorConfigPage, FocusOut_Impl));
+
+ // detect max keyname width
+ int nMaxWidth = 0;
+ for (unsigned short i : KEYCODE_ARRAY)
+ {
+ int nTmp = m_xEntriesBox->get_pixel_size(vcl::KeyCode(i).GetName()).Width();
+ if (nTmp > nMaxWidth)
+ nMaxWidth = nTmp;
+ }
+ // recalc second tab
+ auto nNewTab = nMaxWidth + 5; // additional space
+
+ // initialize Entriesbox
+ std::vector<int> aWidths{ nNewTab };
+ m_xEntriesBox->set_column_fixed_widths(aWidths);
+
+ //Initialize search util
+ m_options.AlgorithmType2 = util::SearchAlgorithms2::ABSOLUTE;
+ m_options.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
+ m_options.searchFlag
+ |= (util::SearchFlags::REG_NOT_BEGINOFLINE | util::SearchFlags::REG_NOT_ENDOFLINE);
+ // initialize GroupBox
+ m_xGroupLBox->SetFunctionListBox(m_xFunctionBox.get());
+
+ // initialize KeyBox
+ m_xKeyBox->make_sorted();
+
+ m_aUpdateDataTimer.SetInvokeHandler(LINK(this, SfxAcceleratorConfigPage, ImplUpdateDataHdl));
+ m_aUpdateDataTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT);
+
+ m_aFillGroupIdle.SetInvokeHandler(LINK(this, SfxAcceleratorConfigPage, TimeOut_Impl));
+ m_aFillGroupIdle.SetPriority(TaskPriority::HIGHEST);
+}
+
+SfxAcceleratorConfigPage::~SfxAcceleratorConfigPage()
+{
+ m_aFillGroupIdle.Stop();
+
+ // free memory - remove all dynamic user data
+ for (int i = 0, nCount = m_xEntriesBox->n_children(); i < nCount; ++i)
+ {
+ TAccInfo* pUserData = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(i));
+ delete pUserData;
+ }
+}
+
+void SfxAcceleratorConfigPage::InitAccCfg()
+{
+ // already initialized ?
+ if (m_xContext.is())
+ return; // yes -> do nothing
+
+ try
+ {
+ // no - initialize this instance
+ m_xContext = comphelper::getProcessComponentContext();
+
+ m_xUICmdDescription = frame::theUICommandDescription::get(m_xContext);
+
+ // get the current active frame, which should be our "parent"
+ // for this session
+ m_xFrame = GetFrame();
+ if (!m_xFrame.is())
+ {
+ uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(m_xContext);
+ m_xFrame = xDesktop->getActiveFrame();
+ }
+
+ // identify module
+ uno::Reference<frame::XModuleManager2> xModuleManager
+ = frame::ModuleManager::create(m_xContext);
+ m_sModuleLongName = xModuleManager->identify(m_xFrame);
+ comphelper::SequenceAsHashMap lModuleProps(xModuleManager->getByName(m_sModuleLongName));
+ m_sModuleUIName
+ = lModuleProps.getUnpackedValueOrDefault("ooSetupFactoryUIName", OUString());
+
+ // get global accelerator configuration
+ m_xGlobal = css::ui::GlobalAcceleratorConfiguration::create(m_xContext);
+
+ // get module accelerator configuration
+
+ uno::Reference<ui::XModuleUIConfigurationManagerSupplier> xModuleCfgSupplier(
+ ui::theModuleUIConfigurationManagerSupplier::get(m_xContext));
+ uno::Reference<ui::XUIConfigurationManager> xUICfgManager
+ = xModuleCfgSupplier->getUIConfigurationManager(m_sModuleLongName);
+ m_xModule = xUICfgManager->getShortCutManager();
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ m_xContext.clear();
+ }
+}
+
+void SfxAcceleratorConfigPage::Init(const uno::Reference<ui::XAcceleratorConfiguration>& xAccMgr)
+{
+ if (!xAccMgr.is())
+ return;
+
+ if (!m_bStylesInfoInitialized)
+ {
+ uno::Reference<frame::XController> xController;
+ uno::Reference<frame::XModel> xModel;
+ if (m_xFrame.is())
+ xController = m_xFrame->getController();
+ if (xController.is())
+ xModel = xController->getModel();
+
+ m_aStylesInfo.init(m_sModuleLongName, xModel);
+ m_xGroupLBox->SetStylesInfo(&m_aStylesInfo);
+ m_bStylesInfoInitialized = true;
+ }
+
+ // Insert all editable accelerators into list box. It is possible
+ // that some accelerators are not mapped on the current system/keyboard
+ // but we don't want to lose these mappings.
+ for (sal_Int32 i1 = 0; i1 < KEYCODE_ARRAY_SIZE; ++i1)
+ {
+ vcl::KeyCode aKey = KEYCODE_ARRAY[i1];
+ OUString sKey = aKey.GetName();
+ if (sKey.isEmpty())
+ continue;
+ TAccInfo* pEntry = new TAccInfo(i1, 0 /*nListPos*/, aKey);
+ m_xEntriesBox->append(weld::toId(pEntry), sKey);
+ int nPos = m_xEntriesBox->n_children() - 1;
+ m_xEntriesBox->set_text(nPos, OUString(), 1);
+ m_xEntriesBox->set_sensitive(nPos, true);
+ }
+
+ // Assign all commands to its shortcuts - reading the accelerator config.
+ uno::Sequence<awt::KeyEvent> lKeys = xAccMgr->getAllKeyEvents();
+ sal_Int32 c2 = lKeys.getLength();
+ sal_Int32 i2 = 0;
+
+ for (i2 = 0; i2 < c2; ++i2)
+ {
+ const awt::KeyEvent& aAWTKey = lKeys[i2];
+ OUString sCommand = xAccMgr->getCommandByKeyEvent(aAWTKey);
+ OUString sLabel = GetLabel4Command(sCommand);
+ vcl::KeyCode aKeyCode = svt::AcceleratorExecute::st_AWTKey2VCLKey(aAWTKey);
+ sal_Int32 nPos = MapKeyCodeToPos(aKeyCode);
+
+ if (nPos == -1)
+ continue;
+
+ m_xEntriesBox->set_text(nPos, sLabel, 1);
+
+ TAccInfo* pEntry = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(nPos));
+ pEntry->m_bIsConfigurable = true;
+
+ pEntry->m_sCommand = sCommand;
+ }
+
+ // Map the VCL hardcoded key codes and mark them as not changeable
+ size_t c3 = Application::GetReservedKeyCodeCount();
+ size_t i3 = 0;
+ for (i3 = 0; i3 < c3; ++i3)
+ {
+ const vcl::KeyCode* pKeyCode = Application::GetReservedKeyCode(i3);
+ sal_Int32 nPos = MapKeyCodeToPos(*pKeyCode);
+
+ if (nPos == -1)
+ continue;
+
+ // Hardcoded function mapped so no ID possible and mark entry as not changeable
+ TAccInfo* pEntry = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(nPos));
+ pEntry->m_bIsConfigurable = false;
+
+ m_xEntriesBox->set_sensitive(nPos, false);
+ }
+}
+
+void SfxAcceleratorConfigPage::Apply(const uno::Reference<ui::XAcceleratorConfiguration>& xAccMgr)
+{
+ if (!xAccMgr.is())
+ return;
+
+ // Go through the list from the bottom to the top ...
+ // because logical accelerator must be preferred instead of
+ // physical ones!
+ for (int i = 0, nCount = m_xEntriesBox->n_children(); i < nCount; ++i)
+ {
+ TAccInfo* pUserData = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(i));
+ OUString sCommand;
+ awt::KeyEvent aAWTKey;
+
+ if (pUserData)
+ {
+ sCommand = pUserData->m_sCommand;
+ aAWTKey = svt::AcceleratorExecute::st_VCLKey2AWTKey(pUserData->m_aKey);
+ }
+
+ try
+ {
+ if (!sCommand.isEmpty())
+ xAccMgr->setKeyEvent(aAWTKey, sCommand);
+ else
+ xAccMgr->removeKeyEvent(aAWTKey);
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+}
+
+void SfxAcceleratorConfigPage::ResetConfig() { m_xEntriesBox->clear(); }
+
+IMPL_LINK_NOARG(SfxAcceleratorConfigPage, ImplUpdateDataHdl, Timer*, void)
+{
+ SelectHdl(m_xGroupLBox->get_widget());
+}
+
+IMPL_LINK_NOARG(SfxAcceleratorConfigPage, SearchUpdateHdl, weld::Entry&, void)
+{
+ m_aUpdateDataTimer.Start();
+}
+
+IMPL_LINK_NOARG(SfxAcceleratorConfigPage, FocusOut_Impl, weld::Widget&, void)
+{
+ if (m_aUpdateDataTimer.IsActive())
+ {
+ m_aUpdateDataTimer.Stop();
+ m_aUpdateDataTimer.Invoke();
+ }
+}
+
+IMPL_LINK_NOARG(SfxAcceleratorConfigPage, Load, weld::Button&, void)
+{
+ // ask for filename, where we should load the new config data from
+ StartFileDialog(StartFileDialogType::Open, aLoadAccelConfigStr);
+}
+
+IMPL_LINK_NOARG(SfxAcceleratorConfigPage, Save, weld::Button&, void)
+{
+ StartFileDialog(StartFileDialogType::SaveAs, aSaveAccelConfigStr);
+}
+
+IMPL_LINK_NOARG(SfxAcceleratorConfigPage, Default, weld::Button&, void)
+{
+ uno::Reference<form::XReset> xReset(m_xAct, uno::UNO_QUERY);
+ if (xReset.is())
+ xReset->reset();
+
+ m_xEntriesBox->freeze();
+ ResetConfig();
+ Init(m_xAct);
+ m_xEntriesBox->thaw();
+ m_xEntriesBox->select(0);
+ SelectHdl(*m_xEntriesBox);
+}
+
+IMPL_LINK_NOARG(SfxAcceleratorConfigPage, ChangeHdl, weld::Button&, void)
+{
+ int nPos = m_xEntriesBox->get_selected_index();
+ if (nPos == -1)
+ return;
+
+ TAccInfo* pEntry = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(nPos));
+ OUString sNewCommand = m_xFunctionBox->GetCurCommand();
+ OUString sLabel = m_xFunctionBox->GetCurLabel();
+ if (sLabel.isEmpty())
+ sLabel = GetLabel4Command(sNewCommand);
+
+ pEntry->m_sCommand = sNewCommand;
+ m_xEntriesBox->set_text(nPos, sLabel, 1);
+
+ SelectHdl(m_xFunctionBox->get_widget());
+}
+
+IMPL_LINK_NOARG(SfxAcceleratorConfigPage, RemoveHdl, weld::Button&, void)
+{
+ // get selected entry
+ int nPos = m_xEntriesBox->get_selected_index();
+ if (nPos == -1)
+ return;
+
+ TAccInfo* pEntry = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(nPos));
+
+ // remove function name from selected entry
+ m_xEntriesBox->set_text(nPos, OUString(), 1);
+ pEntry->m_sCommand.clear();
+
+ SelectHdl(m_xFunctionBox->get_widget());
+}
+
+IMPL_LINK(SfxAcceleratorConfigPage, SelectHdl, weld::TreeView&, rListBox, void)
+{
+ if (&rListBox == m_xEntriesBox.get())
+ {
+ TAccInfo* pEntry = weld::fromId<TAccInfo*>(m_xEntriesBox->get_selected_id());
+
+ OUString sPossibleNewCommand = m_xFunctionBox->GetCurCommand();
+
+ m_xRemoveButton->set_sensitive(false);
+ m_xChangeButton->set_sensitive(false);
+
+ if (pEntry && pEntry->m_bIsConfigurable)
+ {
+ if (pEntry->isConfigured())
+ m_xRemoveButton->set_sensitive(true);
+ m_xChangeButton->set_sensitive(pEntry->m_sCommand != sPossibleNewCommand);
+ }
+ }
+ else if (&rListBox == &m_xGroupLBox->get_widget())
+ {
+ m_xGroupLBox->GroupSelected();
+
+ // Pause redraw (Do not redraw at each removal)
+ m_xFunctionBox->freeze();
+ // Apply the search filter to the functions list
+ OUString aSearchTerm(m_xSearchEdit->get_text());
+ int nMatchFound = applySearchFilter(aSearchTerm);
+ // Resume redraw
+ m_xFunctionBox->thaw();
+ if (nMatchFound != -1)
+ {
+ m_xFunctionBox->select(nMatchFound);
+ SelectHdl(m_xFunctionBox->get_widget());
+ }
+ else
+ {
+ m_xKeyBox->clear();
+ m_xChangeButton->set_sensitive(false);
+ }
+ }
+ else if (&rListBox == &m_xFunctionBox->get_widget())
+ {
+ m_xRemoveButton->set_sensitive(false);
+ m_xChangeButton->set_sensitive(false);
+
+ // #i36994 First selected can return null!
+ TAccInfo* pEntry = weld::fromId<TAccInfo*>(m_xEntriesBox->get_selected_id());
+ if (pEntry)
+ {
+ OUString sPossibleNewCommand = m_xFunctionBox->GetCurCommand();
+
+ if (pEntry->m_bIsConfigurable)
+ {
+ if (pEntry->isConfigured())
+ m_xRemoveButton->set_sensitive(true);
+ m_xChangeButton->set_sensitive(pEntry->m_sCommand != sPossibleNewCommand
+ && !sPossibleNewCommand.isEmpty());
+ }
+
+ // update key box
+ m_xKeyBox->clear();
+ if (!sPossibleNewCommand.isEmpty())
+ {
+ for (int i = 0, nCount = m_xEntriesBox->n_children(); i < nCount; ++i)
+ {
+ TAccInfo* pUserData = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(i));
+ if (pUserData && pUserData->m_sCommand == sPossibleNewCommand)
+ {
+ m_xKeyBox->append(weld::toId(pUserData), pUserData->m_aKey.GetName());
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // goto selected "key" entry of the key box
+ int nP2 = -1;
+ TAccInfo* pU2 = weld::fromId<TAccInfo*>(m_xKeyBox->get_selected_id());
+ if (pU2)
+ nP2 = MapKeyCodeToPos(pU2->m_aKey);
+ if (nP2 != -1)
+ {
+ m_xEntriesBox->select(nP2);
+ m_xEntriesBox->scroll_to_row(nP2);
+ SelectHdl(*m_xEntriesBox);
+ }
+ }
+}
+
+IMPL_LINK_NOARG(SfxAcceleratorConfigPage, RadioHdl, weld::Toggleable&, void)
+{
+ uno::Reference<ui::XAcceleratorConfiguration> xOld = m_xAct;
+
+ if (m_xOfficeButton->get_active())
+ m_xAct = m_xGlobal;
+ else if (m_xModuleButton->get_active())
+ m_xAct = m_xModule;
+
+ // nothing changed? => do nothing!
+ if (m_xAct.is() && (xOld == m_xAct))
+ return;
+
+ m_xEntriesBox->freeze();
+ ResetConfig();
+ Init(m_xAct);
+ m_xEntriesBox->thaw();
+
+ m_xGroupLBox->Init(m_xContext, m_xFrame, m_sModuleLongName, true);
+
+ // pb: #133213# do not select NULL entries
+ if (m_xEntriesBox->n_children())
+ m_xEntriesBox->select(0);
+
+ m_aFillGroupIdle.Start();
+}
+
+IMPL_LINK_NOARG(SfxAcceleratorConfigPage, TimeOut_Impl, Timer*, void)
+{
+ // activating the selection, typically "all commands", can take a long time
+ // -> show wait cursor and disable input
+ weld::WaitObject aWaitObject(GetFrameWeld());
+
+ weld::TreeView& rTreeView = m_xGroupLBox->get_widget();
+ SelectHdl(rTreeView);
+}
+
+IMPL_LINK_NOARG(SfxAcceleratorConfigPage, LoadHdl, sfx2::FileDialogHelper*, void)
+{
+ assert(m_pFileDlg);
+
+ OUString sCfgName;
+ if (ERRCODE_NONE == m_pFileDlg->GetError())
+ sCfgName = m_pFileDlg->GetPath();
+
+ if (sCfgName.isEmpty())
+ return;
+
+ weld::WaitObject aWaitObject(GetFrameWeld());
+
+ uno::Reference<ui::XUIConfigurationManager> xCfgMgr;
+ uno::Reference<embed::XStorage>
+ xRootStorage; // we must hold the root storage alive, if xCfgMgr is used!
+
+ try
+ {
+ // don't forget to release the storage afterwards!
+ uno::Reference<lang::XSingleServiceFactory> xStorageFactory(
+ embed::StorageFactory::create(m_xContext));
+ uno::Sequence<uno::Any> lArgs{ uno::Any(sCfgName),
+ uno::Any(css::embed::ElementModes::READ) };
+
+ xRootStorage.set(xStorageFactory->createInstanceWithArguments(lArgs), uno::UNO_QUERY_THROW);
+ uno::Reference<embed::XStorage> xUIConfig
+ = xRootStorage->openStorageElement(FOLDERNAME_UICONFIG, embed::ElementModes::READ);
+ if (xUIConfig.is())
+ {
+ uno::Reference<ui::XUIConfigurationManager2> xCfgMgr2
+ = ui::UIConfigurationManager::create(m_xContext);
+ xCfgMgr2->setStorage(xUIConfig);
+ xCfgMgr.set(xCfgMgr2, uno::UNO_QUERY_THROW);
+ }
+
+ if (xCfgMgr.is())
+ {
+ // open the configuration and update our UI
+ uno::Reference<ui::XAcceleratorConfiguration> xTempAccMgr(xCfgMgr->getShortCutManager(),
+ uno::UNO_SET_THROW);
+
+ m_xEntriesBox->freeze();
+ ResetConfig();
+ Init(xTempAccMgr);
+ m_xEntriesBox->thaw();
+ if (m_xEntriesBox->n_children())
+ {
+ m_xEntriesBox->select(0);
+ SelectHdl(m_xFunctionBox->get_widget());
+ }
+ }
+
+ // don't forget to close the new opened storage!
+ // We are the owner of it.
+ if (xRootStorage.is())
+ {
+ uno::Reference<lang::XComponent> xComponent;
+ xComponent.set(xCfgMgr, uno::UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+ xRootStorage->dispose();
+ }
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+IMPL_LINK_NOARG(SfxAcceleratorConfigPage, SaveHdl, sfx2::FileDialogHelper*, void)
+{
+ assert(m_pFileDlg);
+
+ OUString sCfgName;
+ if (ERRCODE_NONE == m_pFileDlg->GetError())
+ sCfgName = m_pFileDlg->GetPath();
+
+ if (sCfgName.isEmpty())
+ return;
+
+ weld::WaitObject aWaitObject(GetFrameWeld());
+
+ uno::Reference<embed::XStorage> xRootStorage;
+
+ try
+ {
+ uno::Reference<lang::XSingleServiceFactory> xStorageFactory(
+ embed::StorageFactory::create(m_xContext));
+ uno::Sequence<uno::Any> lArgs{ uno::Any(sCfgName), uno::Any(embed::ElementModes::WRITE) };
+
+ xRootStorage.set(xStorageFactory->createInstanceWithArguments(lArgs), uno::UNO_QUERY_THROW);
+
+ uno::Reference<embed::XStorage> xUIConfig(
+ xRootStorage->openStorageElement(FOLDERNAME_UICONFIG, embed::ElementModes::WRITE),
+ uno::UNO_SET_THROW);
+ uno::Reference<beans::XPropertySet> xUIConfigProps(xUIConfig, uno::UNO_QUERY_THROW);
+
+ // set the correct media type if the storage was new created
+ OUString sMediaType;
+ xUIConfigProps->getPropertyValue(MEDIATYPE_PROPNAME) >>= sMediaType;
+ if (sMediaType.isEmpty())
+ xUIConfigProps->setPropertyValue(
+ MEDIATYPE_PROPNAME, uno::Any(OUString("application/vnd.sun.xml.ui.configuration")));
+
+ uno::Reference<ui::XUIConfigurationManager2> xCfgMgr
+ = ui::UIConfigurationManager::create(m_xContext);
+ xCfgMgr->setStorage(xUIConfig);
+
+ // get the target configuration access and update with all shortcuts
+ // which are set currently at the UI!
+ // Don't copy the m_xAct content to it... because m_xAct will be updated
+ // from the UI on pressing the button "OK" only. And inbetween it's not up to date!
+ uno::Reference<ui::XAcceleratorConfiguration> xTargetAccMgr(xCfgMgr->getShortCutManager(),
+ uno::UNO_SET_THROW);
+ Apply(xTargetAccMgr);
+
+ // commit (order is important!)
+ uno::Reference<ui::XUIConfigurationPersistence> xCommit1(xTargetAccMgr,
+ uno::UNO_QUERY_THROW);
+ uno::Reference<ui::XUIConfigurationPersistence> xCommit2(xCfgMgr, uno::UNO_QUERY_THROW);
+ xCommit1->store();
+ xCommit2->store();
+
+ if (xRootStorage.is())
+ {
+ // Commit root storage
+ uno::Reference<embed::XTransactedObject> xCommit3(xRootStorage, uno::UNO_QUERY_THROW);
+ xCommit3->commit();
+ }
+
+ if (xRootStorage.is())
+ {
+ if (xCfgMgr.is())
+ xCfgMgr->dispose();
+ xRootStorage->dispose();
+ }
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+void SfxAcceleratorConfigPage::StartFileDialog(StartFileDialogType nType, const OUString& rTitle)
+{
+ bool bSave = nType == StartFileDialogType::SaveAs;
+ short nDialogType = bSave ? ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION
+ : ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE;
+ m_pFileDlg.reset(
+ new sfx2::FileDialogHelper(nDialogType, FileDialogFlags::NONE, GetFrameWeld()));
+
+ m_pFileDlg->SetTitle(rTitle);
+ m_pFileDlg->AddFilter(aFilterAllStr, FILEDIALOG_FILTER_ALL);
+ m_pFileDlg->AddFilter(aFilterCfgStr, "*.cfg");
+ m_pFileDlg->SetCurrentFilter(aFilterCfgStr);
+ m_pFileDlg->SetContext(sfx2::FileDialogHelper::AcceleratorConfig);
+
+ Link<sfx2::FileDialogHelper*, void> aDlgClosedLink
+ = bSave ? LINK(this, SfxAcceleratorConfigPage, SaveHdl)
+ : LINK(this, SfxAcceleratorConfigPage, LoadHdl);
+ m_pFileDlg->StartExecuteModal(aDlgClosedLink);
+}
+
+bool SfxAcceleratorConfigPage::FillItemSet(SfxItemSet*)
+{
+ Apply(m_xAct);
+ try
+ {
+ m_xAct->store();
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void SfxAcceleratorConfigPage::Reset(const SfxItemSet* rSet)
+{
+ // open accelerator configs
+ // Note: It initialize some other members too, which are needed here ...
+ // e.g. m_sModuleUIName!
+ InitAccCfg();
+
+ // change the description of the radio button, which switch to the module
+ // dependent accelerator configuration
+ OUString sButtonText = m_xModuleButton->get_label();
+ sButtonText
+ = m_xModuleButton->strip_mnemonic(sButtonText).replaceFirst("$(MODULE)", m_sModuleUIName);
+ m_xModuleButton->set_label(sButtonText);
+
+ if (m_xModule.is())
+ m_xModuleButton->set_active(true);
+ else
+ {
+ m_xModuleButton->hide();
+ m_xOfficeButton->set_active(true);
+ }
+
+ RadioHdl(*m_xOfficeButton);
+
+#if HAVE_FEATURE_SCRIPTING
+ if (const SfxMacroInfoItem* pMacroItem = rSet->GetItemIfSet(SID_MACROINFO))
+ {
+ m_pMacroInfoItem = pMacroItem;
+ m_xGroupLBox->SelectMacro(m_pMacroInfoItem);
+ }
+#else
+ (void)rSet;
+#endif
+}
+
+sal_Int32 SfxAcceleratorConfigPage::MapKeyCodeToPos(const vcl::KeyCode& aKey) const
+{
+ sal_uInt16 nCode1 = aKey.GetCode() + aKey.GetModifier();
+ for (int i = 0, nCount = m_xEntriesBox->n_children(); i < nCount; ++i)
+ {
+ TAccInfo* pUserData = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(i));
+ if (pUserData)
+ {
+ sal_uInt16 nCode2 = pUserData->m_aKey.GetCode() + pUserData->m_aKey.GetModifier();
+ if (nCode1 == nCode2)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+OUString SfxAcceleratorConfigPage::GetLabel4Command(const OUString& sCommand)
+{
+ try
+ {
+ // check global command configuration first
+ uno::Reference<container::XNameAccess> xModuleConf;
+ m_xUICmdDescription->getByName(m_sModuleLongName) >>= xModuleConf;
+ if (xModuleConf.is())
+ {
+ ::comphelper::SequenceAsHashMap lProps(xModuleConf->getByName(sCommand));
+ OUString sLabel = lProps.getUnpackedValueOrDefault("Name", OUString());
+ if (!sLabel.isEmpty())
+ return sLabel;
+ }
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ // may be it's a style URL .. they must be handled special
+ SfxStyleInfo_Impl aStyle;
+ aStyle.sCommand = sCommand;
+ if (SfxStylesInfo_Impl::parseStyleCommand(aStyle))
+ {
+ m_aStylesInfo.getLabel4Style(aStyle);
+ return aStyle.sLabel;
+ }
+
+ return sCommand;
+}
+
+/*
+ * Remove entries which doesn't contain the search term
+ */
+int SfxAcceleratorConfigPage::applySearchFilter(OUString const& rSearchTerm)
+{
+ if (rSearchTerm.isEmpty())
+ return -1;
+
+ m_options.searchString = rSearchTerm;
+ utl::TextSearch textSearch(m_options);
+
+ for (int i = m_xFunctionBox->n_children(); i > 0; --i)
+ {
+ int nEntry = i - 1;
+ OUString aStr = m_xFunctionBox->get_text(nEntry);
+ sal_Int32 aStartPos = 0;
+ sal_Int32 aEndPos = aStr.getLength();
+
+ if (!textSearch.SearchForward(aStr, &aStartPos, &aEndPos))
+ m_xFunctionBox->remove(nEntry);
+ }
+
+ return m_xFunctionBox->n_children() ? 0 : -1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/customize/cfg.cxx b/cui/source/customize/cfg.cxx
new file mode 100644
index 000000000..4c8192b61
--- /dev/null
+++ b/cui/source/customize/cfg.cxx
@@ -0,0 +1,3221 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <cassert>
+#include <stdlib.h>
+#include <typeinfo>
+
+#include <utility>
+#include <vcl/stdtext.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/event.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/virdev.hxx>
+
+#include <sfx2/sfxhelp.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/stritem.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <algorithm>
+#include <strings.hrc>
+
+#include <acccfg.hxx>
+#include <cfg.hxx>
+#include <CustomNotebookbarGenerator.hxx>
+#include <SvxMenuConfigPage.hxx>
+#include <SvxToolbarConfigPage.hxx>
+#include <SvxNotebookbarConfigPage.hxx>
+#include <SvxConfigPageHelper.hxx>
+#include "eventdlg.hxx"
+#include <dialmgr.hxx>
+
+#include <unotools/configmgr.hxx>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/FileSystemStorageFactory.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XFrames.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/theUICommandDescription.hpp>
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <com/sun/star/ui/ItemStyle.hpp>
+#include <com/sun/star/ui/ImageManager.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/XUIConfigurationPersistence.hpp>
+#include <com/sun/star/ui/XUIElement.hpp>
+#include <com/sun/star/ui/UIElementType.hpp>
+#include <com/sun/star/ui/ImageType.hpp>
+#include <com/sun/star/ui/theWindowStateConfiguration.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/util/thePathSettings.hpp>
+#include <comphelper/documentinfo.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/processfactory.hxx>
+
+namespace uno = com::sun::star::uno;
+namespace frame = com::sun::star::frame;
+namespace lang = com::sun::star::lang;
+namespace container = com::sun::star::container;
+namespace beans = com::sun::star::beans;
+namespace graphic = com::sun::star::graphic;
+
+#if OSL_DEBUG_LEVEL > 1
+
+void printPropertySet(
+ const OUString& prefix,
+ const uno::Reference< beans::XPropertySet >& xPropSet )
+{
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo =
+ xPropSet->getPropertySetInfo();
+
+ const uno::Sequence< beans::Property >& aPropDetails =
+ xPropSetInfo->getProperties();
+
+ SAL_WARN("cui", "printPropertySet: " << aPropDetails.getLength() << " properties" );
+
+ for ( beans::Property const & aPropDetail : aPropDetails )
+ {
+ OUString tmp;
+ sal_Int32 ival;
+
+ uno::Any a = xPropSet->getPropertyValue( aPropDetail.Name );
+
+ if ( a >>= tmp )
+ {
+ SAL_WARN("cui", prefix << ": Got property: " << aPropDetail.Name << tmp);
+ }
+ else if ( ( a >>= ival ) )
+ {
+ SAL_WARN("cui", prefix << ": Got property: " << aPropDetail.Name << " = " << ival);
+ }
+ else
+ {
+ SAL_WARN("cui", prefix << ": Got property: " << aPropDetail.Name << " of type " << a.getValueTypeName());
+ }
+ }
+}
+
+void printProperties(
+ const OUString& prefix,
+ const uno::Sequence< beans::PropertyValue >& aProp )
+{
+ for (beans::PropertyValue const & aPropVal : aProp)
+ {
+ OUString tmp;
+
+ aPropVal.Value >>= tmp;
+
+ SAL_WARN("cui", prefix << ": Got property: " << aPropVal.Name << " = " << tmp);
+ }
+}
+
+void printEntries(SvxEntries* entries)
+{
+ for (auto const& entry : *entries)
+ {
+ SAL_WARN("cui", "printEntries: " << entry->GetName());
+ }
+}
+
+#endif
+
+bool
+SvxConfigPage::CanConfig( std::u16string_view aModuleId )
+{
+ return aModuleId != u"com.sun.star.script.BasicIDE" && aModuleId != u"com.sun.star.frame.Bibliography";
+}
+
+static std::unique_ptr<SfxTabPage> CreateSvxMenuConfigPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet )
+{
+ return std::make_unique<SvxMenuConfigPage>(pPage, pController, *rSet);
+}
+
+static std::unique_ptr<SfxTabPage> CreateSvxContextMenuConfigPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet )
+{
+ return std::make_unique<SvxMenuConfigPage>(pPage, pController, *rSet, false);
+}
+
+static std::unique_ptr<SfxTabPage> CreateKeyboardConfigPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet )
+{
+ return std::make_unique<SfxAcceleratorConfigPage>(pPage, pController, *rSet);
+}
+
+static std::unique_ptr<SfxTabPage> CreateSvxNotebookbarConfigPage(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rSet)
+{
+ return std::make_unique<SvxNotebookbarConfigPage>(pPage, pController, *rSet);
+}
+
+static std::unique_ptr<SfxTabPage> CreateSvxToolbarConfigPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet )
+{
+ return std::make_unique<SvxToolbarConfigPage>(pPage, pController, *rSet);
+}
+
+static std::unique_ptr<SfxTabPage> CreateSvxEventConfigPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet )
+{
+ return std::make_unique<SvxEventConfigPage>(pPage, pController, *rSet, SvxEventConfigPage::EarlyInit());
+}
+
+/******************************************************************************
+ *
+ * SvxConfigDialog is the configuration dialog which is brought up from the
+ * Tools menu. It includes tabs for customizing menus, toolbars, events and
+ * key bindings.
+ *
+ *****************************************************************************/
+SvxConfigDialog::SvxConfigDialog(weld::Window * pParent, const SfxItemSet* pInSet)
+ : SfxTabDialogController(pParent, "cui/ui/customizedialog.ui", "CustomizeDialog", pInSet)
+{
+ SvxConfigPageHelper::InitImageType();
+
+ AddTabPage("menus", CreateSvxMenuConfigPage, nullptr);
+ AddTabPage("toolbars", CreateSvxToolbarConfigPage, nullptr);
+ AddTabPage("notebookbar", CreateSvxNotebookbarConfigPage, nullptr);
+ AddTabPage("contextmenus", CreateSvxContextMenuConfigPage, nullptr);
+ AddTabPage("keyboard", CreateKeyboardConfigPage, nullptr);
+ AddTabPage("events", CreateSvxEventConfigPage, nullptr);
+ m_xTabCtrl->connect_enter_page(LINK(this, SvxConfigDialog, ActivatePageHdl));
+
+ const SfxPoolItem* pItem = pInSet->GetItem( SID_CONFIG );
+
+ if ( pItem )
+ {
+ OUString text = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ if (text.startsWith( ITEM_TOOLBAR_URL ) )
+ {
+ SetCurPageId("toolbars");
+ }
+ }
+}
+
+IMPL_LINK(SvxConfigDialog, ActivatePageHdl, const OString&, rPage, void)
+{
+ GetResetButton()->set_visible(rPage != "keyboard");
+}
+
+void SvxConfigDialog::SetFrame(const css::uno::Reference<css::frame::XFrame>& xFrame)
+{
+ m_xFrame = xFrame;
+ OUString aModuleId = SvxConfigPage::GetFrameWithDefaultAndIdentify(m_xFrame);
+
+ if (aModuleId != "com.sun.star.text.TextDocument" &&
+ aModuleId != "com.sun.star.sheet.SpreadsheetDocument" &&
+ aModuleId != "com.sun.star.presentation.PresentationDocument" &&
+ aModuleId != "com.sun.star.drawing.DrawingDocument")
+ RemoveTabPage("notebookbar");
+
+ if (aModuleId == "com.sun.star.frame.StartModule")
+ RemoveTabPage("keyboard");
+}
+
+void SvxConfigDialog::PageCreated(const OString &rId, SfxTabPage& rPage)
+{
+ if (rId == "menus" || rId == "keyboard" || rId == "notebookbar"
+ || rId == "toolbars" || rId == "contextmenus")
+ {
+ rPage.SetFrame(m_xFrame);
+ }
+ else if (rId == "events")
+ {
+ dynamic_cast< SvxEventConfigPage& >( rPage ).LateInit( m_xFrame );
+ }
+}
+
+/******************************************************************************
+ *
+ * The SaveInData class is used to hold data for entries in the Save In
+ * ListBox controls in the menu and toolbar tabs
+ *
+ ******************************************************************************/
+
+// Initialize static variable which holds default XImageManager
+uno::Reference< css::ui::XImageManager>* SaveInData::xDefaultImgMgr = nullptr;
+
+SaveInData::SaveInData(
+ uno::Reference< css::ui::XUIConfigurationManager > xCfgMgr,
+ uno::Reference< css::ui::XUIConfigurationManager > xParentCfgMgr,
+ const OUString& aModuleId,
+ bool isDocConfig )
+ :
+ bModified( false ),
+ bDocConfig( isDocConfig ),
+ bReadOnly( false ),
+ m_xCfgMgr(std::move( xCfgMgr )),
+ m_xParentCfgMgr(std::move( xParentCfgMgr )),
+ m_aSeparatorSeq{ comphelper::makePropertyValue(ITEM_DESCRIPTOR_TYPE,
+ css::ui::ItemType::SEPARATOR_LINE) }
+{
+ if ( bDocConfig )
+ {
+ uno::Reference< css::ui::XUIConfigurationPersistence >
+ xDocPersistence( GetConfigManager(), uno::UNO_QUERY );
+
+ bReadOnly = xDocPersistence->isReadOnly();
+ }
+
+ uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
+
+ uno::Reference< container::XNameAccess > xNameAccess(
+ css::frame::theUICommandDescription::get(xContext) );
+
+ xNameAccess->getByName( aModuleId ) >>= m_xCommandToLabelMap;
+
+ if ( !m_xImgMgr.is() )
+ {
+ m_xImgMgr.set( GetConfigManager()->getImageManager(), uno::UNO_QUERY );
+ }
+
+ if ( !IsDocConfig() )
+ {
+ // If this is not a document configuration then it is the settings
+ // for the module (writer, calc, impress etc.) Use this as the default
+ // XImageManager instance
+ xDefaultImgMgr = &m_xImgMgr;
+ }
+ else
+ {
+ // If this is a document configuration then use the module image manager
+ // as default.
+ if ( m_xParentCfgMgr.is() )
+ {
+ m_xParentImgMgr.set( m_xParentCfgMgr->getImageManager(), uno::UNO_QUERY );
+ xDefaultImgMgr = &m_xParentImgMgr;
+ }
+ }
+}
+
+uno::Reference<graphic::XGraphic> SaveInData::GetImage(const OUString& rCommandURL)
+{
+ uno::Reference< graphic::XGraphic > xGraphic =
+ SvxConfigPageHelper::GetGraphic( m_xImgMgr, rCommandURL );
+
+ if (!xGraphic.is() && xDefaultImgMgr != nullptr && (*xDefaultImgMgr).is())
+ {
+ xGraphic = SvxConfigPageHelper::GetGraphic( (*xDefaultImgMgr), rCommandURL );
+ }
+
+ return xGraphic;
+}
+
+bool SaveInData::PersistChanges(
+ const uno::Reference< uno::XInterface >& xManager )
+{
+ bool result = true;
+
+ try
+ {
+ if ( xManager.is() && !IsReadOnly() )
+ {
+ uno::Reference< css::ui::XUIConfigurationPersistence >
+ xConfigPersistence( xManager, uno::UNO_QUERY );
+
+ if ( xConfigPersistence->isModified() )
+ {
+ xConfigPersistence->store();
+ }
+ }
+ }
+ catch ( css::io::IOException& )
+ {
+ result = false;
+ }
+
+ return result;
+}
+
+/******************************************************************************
+ *
+ * The MenuSaveInData class extends SaveInData and provides menu specific
+ * load and store functionality.
+ *
+ ******************************************************************************/
+
+// Initialize static variable which holds default Menu data
+MenuSaveInData* MenuSaveInData::pDefaultData = nullptr;
+
+MenuSaveInData::MenuSaveInData(
+ const uno::Reference< css::ui::XUIConfigurationManager >& cfgmgr,
+ const uno::Reference< css::ui::XUIConfigurationManager >& xParentCfgMgr,
+ const OUString& aModuleId,
+ bool isDocConfig )
+ :
+ SaveInData( cfgmgr, xParentCfgMgr, aModuleId, isDocConfig ),
+ m_aMenuResourceURL(
+ ITEM_MENUBAR_URL ),
+ m_aDescriptorContainer(
+ ITEM_DESCRIPTOR_CONTAINER )
+{
+ try
+ {
+ m_xMenuSettings = GetConfigManager()->getSettings( ITEM_MENUBAR_URL, false );
+ }
+ catch ( container::NoSuchElementException& )
+ {
+ // will use menu settings for the module
+ }
+
+ // If this is not a document configuration then it is the settings
+ // for the module (writer, calc, impress etc.). These settings should
+ // be set as the default to be used for SaveIn locations that do not
+ // have custom settings
+ if ( !IsDocConfig() )
+ {
+ SetDefaultData( this );
+ }
+}
+
+MenuSaveInData::~MenuSaveInData()
+{
+}
+
+SvxEntries*
+MenuSaveInData::GetEntries()
+{
+ if ( pRootEntry == nullptr )
+ {
+ pRootEntry.reset( new SvxConfigEntry( "MainMenus", OUString(), true, /*bParentData*/false) );
+
+ if ( m_xMenuSettings.is() )
+ {
+ LoadSubMenus( m_xMenuSettings, OUString(), pRootEntry.get(), false );
+ }
+ else if ( GetDefaultData() != nullptr )
+ {
+ // If the doc has no config settings use module config settings
+ LoadSubMenus( GetDefaultData()->m_xMenuSettings, OUString(), pRootEntry.get(), false );
+ }
+ }
+
+ return pRootEntry->GetEntries();
+}
+
+void
+MenuSaveInData::SetEntries( std::unique_ptr<SvxEntries> pNewEntries )
+{
+ pRootEntry->SetEntries( std::move(pNewEntries) );
+}
+
+void SaveInData::LoadSubMenus( const uno::Reference< container::XIndexAccess >& xMenuSettings,
+ const OUString& rBaseTitle, SvxConfigEntry const * pParentData, bool bContextMenu )
+{
+ SvxEntries* pEntries = pParentData->GetEntries();
+
+ // Don't access non existing menu configuration!
+ if ( !xMenuSettings.is() )
+ return;
+
+ for ( sal_Int32 nIndex = 0; nIndex < xMenuSettings->getCount(); ++nIndex )
+ {
+ uno::Reference< container::XIndexAccess > xSubMenu;
+ OUString aCommandURL;
+ OUString aLabel;
+
+ sal_uInt16 nType( css::ui::ItemType::DEFAULT );
+ sal_Int32 nStyle(0);
+
+ bool bItem = SvxConfigPageHelper::GetMenuItemData( xMenuSettings, nIndex,
+ aCommandURL, aLabel, nType, nStyle, xSubMenu );
+
+ if ( bItem )
+ {
+ bool bIsUserDefined = true;
+
+ if ( nType == css::ui::ItemType::DEFAULT )
+ {
+ uno::Any a;
+ try
+ {
+ a = m_xCommandToLabelMap->getByName( aCommandURL );
+ bIsUserDefined = false;
+ }
+ catch ( container::NoSuchElementException& )
+ {
+ bIsUserDefined = true;
+ }
+
+ bool bUseDefaultLabel = false;
+ // If custom label not set retrieve it from the command
+ // to info service
+ if ( aLabel.isEmpty() )
+ {
+ bUseDefaultLabel = true;
+ uno::Sequence< beans::PropertyValue > aPropSeq;
+ if ( a >>= aPropSeq )
+ {
+ OUString aMenuLabel;
+ for ( const beans::PropertyValue& prop : std::as_const(aPropSeq) )
+ {
+ if ( bContextMenu )
+ {
+ if ( prop.Name == "PopupLabel" )
+ {
+ prop.Value >>= aLabel;
+ break;
+ }
+ else if ( prop.Name == "Label" )
+ {
+ prop.Value >>= aMenuLabel;
+ }
+ }
+ else if ( prop.Name == "Label" )
+ {
+ prop.Value >>= aLabel;
+ break;
+ }
+ }
+ if ( aLabel.isEmpty() )
+ aLabel = aMenuLabel;
+ }
+ }
+
+ SvxConfigEntry* pEntry = new SvxConfigEntry(
+ aLabel, aCommandURL, xSubMenu.is(), /*bParentData*/false );
+
+ pEntry->SetStyle( nStyle );
+ pEntry->SetUserDefined( bIsUserDefined );
+ if ( !bUseDefaultLabel )
+ pEntry->SetName( aLabel );
+
+ pEntries->push_back( pEntry );
+
+ if ( xSubMenu.is() )
+ {
+ // popup menu
+ OUString subMenuTitle( rBaseTitle );
+
+ if ( !subMenuTitle.isEmpty() )
+ {
+ subMenuTitle += aMenuSeparatorStr;
+ }
+ else
+ {
+ pEntry->SetMain();
+ }
+
+ subMenuTitle += SvxConfigPageHelper::stripHotKey( aLabel );
+
+ LoadSubMenus( xSubMenu, subMenuTitle, pEntry, bContextMenu );
+ }
+ }
+ else
+ {
+ SvxConfigEntry* pEntry = new SvxConfigEntry;
+ pEntry->SetUserDefined( bIsUserDefined );
+ pEntries->push_back( pEntry );
+ }
+ }
+ }
+}
+
+bool MenuSaveInData::Apply()
+{
+ bool result = false;
+
+ if ( IsModified() )
+ {
+ // Apply new menu bar structure to our settings container
+ m_xMenuSettings = GetConfigManager()->createSettings();
+
+ uno::Reference< container::XIndexContainer > xIndexContainer (
+ m_xMenuSettings, uno::UNO_QUERY );
+
+ uno::Reference< lang::XSingleComponentFactory > xFactory (
+ m_xMenuSettings, uno::UNO_QUERY );
+
+ Apply( xIndexContainer, xFactory );
+
+ try
+ {
+ if ( GetConfigManager()->hasSettings( m_aMenuResourceURL ) )
+ {
+ GetConfigManager()->replaceSettings(
+ m_aMenuResourceURL, m_xMenuSettings );
+ }
+ else
+ {
+ GetConfigManager()->insertSettings(
+ m_aMenuResourceURL, m_xMenuSettings );
+ }
+ }
+ catch ( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("cui.customize", "caught some other exception saving settings");
+ }
+
+ SetModified( false );
+
+ result = PersistChanges( GetConfigManager() );
+ }
+
+ return result;
+}
+
+void MenuSaveInData::Apply(
+ uno::Reference< container::XIndexContainer > const & rMenuBar,
+ uno::Reference< lang::XSingleComponentFactory >& rFactory )
+{
+ uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
+
+ for (auto const& entryData : *GetEntries())
+ {
+ uno::Sequence< beans::PropertyValue > aPropValueSeq =
+ SvxConfigPageHelper::ConvertSvxConfigEntry(entryData);
+
+ uno::Reference< container::XIndexContainer > xSubMenuBar(
+ rFactory->createInstanceWithContext( xContext ),
+ uno::UNO_QUERY );
+
+ sal_Int32 nIndex = aPropValueSeq.getLength();
+ aPropValueSeq.realloc( nIndex + 1 );
+ auto pPropValueSeq = aPropValueSeq.getArray();
+ pPropValueSeq[nIndex].Name = m_aDescriptorContainer;
+ pPropValueSeq[nIndex].Value <<= xSubMenuBar;
+ rMenuBar->insertByIndex(
+ rMenuBar->getCount(), uno::Any( aPropValueSeq ));
+ ApplyMenu( xSubMenuBar, rFactory, entryData );
+ }
+}
+
+void SaveInData::ApplyMenu(
+ uno::Reference< container::XIndexContainer > const & rMenuBar,
+ uno::Reference< lang::XSingleComponentFactory >& rFactory,
+ SvxConfigEntry* pMenuData )
+{
+ uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
+
+ for (auto const& entry : *pMenuData->GetEntries())
+ {
+ if (entry->IsPopup())
+ {
+ uno::Sequence< beans::PropertyValue > aPropValueSeq =
+ SvxConfigPageHelper::ConvertSvxConfigEntry(entry);
+
+ uno::Reference< container::XIndexContainer > xSubMenuBar(
+ rFactory->createInstanceWithContext( xContext ),
+ uno::UNO_QUERY );
+
+ sal_Int32 nIndex = aPropValueSeq.getLength();
+ aPropValueSeq.realloc( nIndex + 1 );
+ auto pPropValueSeq = aPropValueSeq.getArray();
+ pPropValueSeq[nIndex].Name = ITEM_DESCRIPTOR_CONTAINER;
+ pPropValueSeq[nIndex].Value <<= xSubMenuBar;
+
+ rMenuBar->insertByIndex(
+ rMenuBar->getCount(), uno::Any( aPropValueSeq ));
+
+ ApplyMenu( xSubMenuBar, rFactory, entry );
+ entry->SetModified( false );
+ }
+ else if (entry->IsSeparator())
+ {
+ rMenuBar->insertByIndex(
+ rMenuBar->getCount(), uno::Any( m_aSeparatorSeq ));
+ }
+ else
+ {
+ uno::Sequence< beans::PropertyValue > aPropValueSeq =
+ SvxConfigPageHelper::ConvertSvxConfigEntry(entry);
+ rMenuBar->insertByIndex(
+ rMenuBar->getCount(), uno::Any( aPropValueSeq ));
+ }
+ }
+ pMenuData->SetModified( false );
+}
+
+void
+MenuSaveInData::Reset()
+{
+ try
+ {
+ GetConfigManager()->removeSettings( m_aMenuResourceURL );
+ }
+ catch ( const css::uno::Exception& )
+ {}
+
+ PersistChanges( GetConfigManager() );
+
+ pRootEntry.reset();
+
+ try
+ {
+ m_xMenuSettings = GetConfigManager()->getSettings(
+ m_aMenuResourceURL, false );
+ }
+ catch ( container::NoSuchElementException& )
+ {
+ // will use default settings
+ }
+}
+
+ContextMenuSaveInData::ContextMenuSaveInData(
+ const css::uno::Reference< css::ui::XUIConfigurationManager >& xCfgMgr,
+ const css::uno::Reference< css::ui::XUIConfigurationManager >& xParentCfgMgr,
+ const OUString& aModuleId, bool bIsDocConfig )
+ : SaveInData( xCfgMgr, xParentCfgMgr, aModuleId, bIsDocConfig )
+{
+ css::uno::Reference< css::uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
+ css::uno::Reference< css::container::XNameAccess > xConfig( css::ui::theWindowStateConfiguration::get( xContext ) );
+ xConfig->getByName( aModuleId ) >>= m_xPersistentWindowState;
+}
+
+ContextMenuSaveInData::~ContextMenuSaveInData()
+{
+}
+
+OUString ContextMenuSaveInData::GetUIName( const OUString& rResourceURL )
+{
+ if ( m_xPersistentWindowState.is() )
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aProps;
+ try
+ {
+ m_xPersistentWindowState->getByName( rResourceURL ) >>= aProps;
+ }
+ catch ( const css::uno::Exception& )
+ {}
+
+ for ( const auto& aProp : std::as_const(aProps) )
+ {
+ if ( aProp.Name == ITEM_DESCRIPTOR_UINAME )
+ {
+ OUString aResult;
+ aProp.Value >>= aResult;
+ return aResult;
+ }
+ }
+ }
+ return OUString();
+}
+
+SvxEntries* ContextMenuSaveInData::GetEntries()
+{
+ if ( !m_pRootEntry )
+ {
+ std::unordered_map< OUString, bool > aMenuInfo;
+
+ m_pRootEntry.reset( new SvxConfigEntry( "ContextMenus", OUString(), true, /*bParentData*/false ) );
+ css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > aElementsInfo;
+ try
+ {
+ aElementsInfo = GetConfigManager()->getUIElementsInfo( css::ui::UIElementType::POPUPMENU );
+ }
+ catch ( const css::lang::IllegalArgumentException& )
+ {}
+
+ for ( const auto& aElement : std::as_const(aElementsInfo) )
+ {
+ OUString aUrl;
+ for ( const auto& aElementProp : aElement )
+ {
+ if ( aElementProp.Name == ITEM_DESCRIPTOR_RESOURCEURL )
+ {
+ aElementProp.Value >>= aUrl;
+ break;
+ }
+ }
+
+ css::uno::Reference< css::container::XIndexAccess > xPopupMenu;
+ try
+ {
+ xPopupMenu = GetConfigManager()->getSettings( aUrl, false );
+ }
+ catch ( const css::uno::Exception& )
+ {}
+
+ if ( xPopupMenu.is() )
+ {
+ // insert into std::unordered_map to filter duplicates from the parent
+ aMenuInfo.emplace( aUrl, true );
+
+ OUString aUIMenuName = GetUIName( aUrl );
+ if ( aUIMenuName.isEmpty() )
+ // Menus without UI name aren't supposed to be customized.
+ continue;
+
+ SvxConfigEntry* pEntry = new SvxConfigEntry( aUIMenuName, aUrl, true, /*bParentData*/false );
+ pEntry->SetMain();
+ m_pRootEntry->GetEntries()->push_back( pEntry );
+ LoadSubMenus( xPopupMenu, aUIMenuName, pEntry, true );
+ }
+ }
+
+ // Retrieve also the parent menus, to make it possible to configure module menus and save them into the document.
+ css::uno::Reference< css::ui::XUIConfigurationManager > xParentCfgMgr = GetParentConfigManager();
+ css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > aParentElementsInfo;
+ try
+ {
+ if ( xParentCfgMgr.is() )
+ aParentElementsInfo = xParentCfgMgr->getUIElementsInfo( css::ui::UIElementType::POPUPMENU );
+ }
+ catch ( const css::lang::IllegalArgumentException& )
+ {}
+
+ for ( const auto& aElement : std::as_const(aParentElementsInfo) )
+ {
+ OUString aUrl;
+ for ( const auto& aElementProp : aElement )
+ {
+ if ( aElementProp.Name == ITEM_DESCRIPTOR_RESOURCEURL )
+ {
+ aElementProp.Value >>= aUrl;
+ break;
+ }
+ }
+
+ css::uno::Reference< css::container::XIndexAccess > xPopupMenu;
+ try
+ {
+ if ( aMenuInfo.find( aUrl ) == aMenuInfo.end() )
+ xPopupMenu = xParentCfgMgr->getSettings( aUrl, false );
+ }
+ catch ( const css::uno::Exception& )
+ {}
+
+ if ( xPopupMenu.is() )
+ {
+ OUString aUIMenuName = GetUIName( aUrl );
+ if ( aUIMenuName.isEmpty() )
+ continue;
+
+ SvxConfigEntry* pEntry = new SvxConfigEntry( aUIMenuName, aUrl, true, true );
+ pEntry->SetMain();
+ m_pRootEntry->GetEntries()->push_back( pEntry );
+ LoadSubMenus( xPopupMenu, aUIMenuName, pEntry, true );
+ }
+ }
+ std::sort( m_pRootEntry->GetEntries()->begin(), m_pRootEntry->GetEntries()->end(), SvxConfigPageHelper::EntrySort );
+ }
+ return m_pRootEntry->GetEntries();
+}
+
+void ContextMenuSaveInData::SetEntries( std::unique_ptr<SvxEntries> pNewEntries )
+{
+ m_pRootEntry->SetEntries( std::move(pNewEntries) );
+}
+
+bool ContextMenuSaveInData::HasURL( const OUString& rURL )
+{
+ SvxEntries* pEntries = GetEntries();
+ for ( const auto& pEntry : *pEntries )
+ if ( pEntry->GetCommand() == rURL )
+ return true;
+
+ return false;
+}
+
+bool ContextMenuSaveInData::HasSettings()
+{
+ return m_pRootEntry && !m_pRootEntry->GetEntries()->empty();
+}
+
+bool ContextMenuSaveInData::Apply()
+{
+ if ( !IsModified() )
+ return false;
+
+ SvxEntries* pEntries = GetEntries();
+ for ( const auto& pEntry : *pEntries )
+ {
+ if ( pEntry->IsModified() || SvxConfigPageHelper::SvxConfigEntryModified( pEntry ) )
+ {
+ css::uno::Reference< css::container::XIndexContainer > xIndexContainer = GetConfigManager()->createSettings();
+ css::uno::Reference< css::lang::XSingleComponentFactory > xFactory( xIndexContainer, css::uno::UNO_QUERY );
+ ApplyMenu( xIndexContainer, xFactory, pEntry );
+
+ const OUString& aUrl = pEntry->GetCommand();
+ try
+ {
+ if ( GetConfigManager()->hasSettings( aUrl ) )
+ GetConfigManager()->replaceSettings( aUrl, xIndexContainer );
+ else
+ GetConfigManager()->insertSettings( aUrl, xIndexContainer );
+ }
+ catch ( const css::uno::Exception& )
+ {}
+ }
+ }
+ SetModified( false );
+ return PersistChanges( GetConfigManager() );
+}
+
+void ContextMenuSaveInData::Reset()
+{
+ SvxEntries* pEntries = GetEntries();
+ for ( const auto& pEntry : *pEntries )
+ {
+ try
+ {
+ GetConfigManager()->removeSettings( pEntry->GetCommand() );
+ }
+ catch ( const css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("cui.customize", "Exception caught while resetting context menus");
+ }
+ }
+ PersistChanges( GetConfigManager() );
+ m_pRootEntry.reset();
+}
+
+void ContextMenuSaveInData::ResetContextMenu( const SvxConfigEntry* pEntry )
+{
+ try
+ {
+ GetConfigManager()->removeSettings( pEntry->GetCommand() );
+ }
+ catch ( const css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("cui.customize", "Exception caught while resetting context menu");
+ }
+ PersistChanges( GetConfigManager() );
+ m_pRootEntry.reset();
+}
+
+void SvxMenuEntriesListBox::CreateDropDown()
+{
+ int nWidth = (m_xControl->get_text_height() * 3) / 4;
+ m_xDropDown->SetOutputSizePixel(Size(nWidth, nWidth));
+ DecorationView aDecoView(m_xDropDown.get());
+ aDecoView.DrawSymbol(tools::Rectangle(Point(0, 0), Size(nWidth, nWidth)),
+ SymbolType::SPIN_RIGHT, m_xDropDown->GetTextColor(),
+ DrawSymbolFlags::NONE);
+}
+
+/******************************************************************************
+ *
+ * SvxMenuEntriesListBox is the listbox in which the menu items for a
+ * particular menu are shown. We have a custom listbox because we need
+ * to add drag'n'drop support from the Macro Selector and within the
+ * listbox
+ *
+ *****************************************************************************/
+SvxMenuEntriesListBox::SvxMenuEntriesListBox(std::unique_ptr<weld::TreeView> xControl, SvxConfigPage* pPg)
+ : m_xControl(std::move(xControl))
+ , m_xDropDown(m_xControl->create_virtual_device())
+ , m_pPage(pPg)
+{
+ m_xControl->enable_toggle_buttons(weld::ColumnToggleType::Check);
+ CreateDropDown();
+ m_xControl->connect_key_press(LINK(this, SvxMenuEntriesListBox, KeyInputHdl));
+}
+
+SvxMenuEntriesListBox::~SvxMenuEntriesListBox()
+{
+}
+
+IMPL_LINK(SvxMenuEntriesListBox, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ vcl::KeyCode keycode = rKeyEvent.GetKeyCode();
+
+ // support DELETE for removing the current entry
+ if ( keycode == KEY_DELETE )
+ {
+ m_pPage->DeleteSelectedContent();
+ }
+ // support CTRL+UP and CTRL+DOWN for moving selected entries
+ else if ( keycode.GetCode() == KEY_UP && keycode.IsMod1() )
+ {
+ m_pPage->MoveEntry( true );
+ }
+ else if ( keycode.GetCode() == KEY_DOWN && keycode.IsMod1() )
+ {
+ m_pPage->MoveEntry( false );
+ }
+ else
+ {
+ return false; // pass on to default handler
+ }
+ return true;
+}
+
+/******************************************************************************
+ *
+ * SvxConfigPage is the abstract base class on which the Menu and Toolbar
+ * configuration tabpages are based. It includes methods which are common to
+ * both tabpages to add, delete, move and rename items etc.
+ *
+ *****************************************************************************/
+SvxConfigPage::SvxConfigPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet)
+ : SfxTabPage(pPage, pController, "cui/ui/menuassignpage.ui", "MenuAssignPage", &rSet)
+ , m_aUpdateDataTimer( "SvxConfigPage UpdateDataTimer" )
+ , bInitialised(false)
+ , pCurrentSaveInData(nullptr)
+ , m_xCommandCategoryListBox(new CommandCategoryListBox(m_xBuilder->weld_combo_box("commandcategorylist")))
+ , m_xFunctions(new CuiConfigFunctionListBox(m_xBuilder->weld_tree_view("functions")))
+ , m_xCategoryLabel(m_xBuilder->weld_label("categorylabel"))
+ , m_xDescriptionFieldLb(m_xBuilder->weld_label("descriptionlabel"))
+ , m_xDescriptionField(m_xBuilder->weld_text_view("desc"))
+ , m_xLeftFunctionLabel(m_xBuilder->weld_label("leftfunctionlabel"))
+ , m_xSearchEdit(m_xBuilder->weld_entry("searchEntry"))
+ , m_xSearchLabel(m_xBuilder->weld_label("searchlabel"))
+ , m_xCustomizeLabel(m_xBuilder->weld_label("customizelabel"))
+ , m_xTopLevelListBox(m_xBuilder->weld_combo_box("toplevellist"))
+ , m_xMoveUpButton(m_xBuilder->weld_button("up"))
+ , m_xMoveDownButton(m_xBuilder->weld_button("down"))
+ , m_xSaveInListBox(m_xBuilder->weld_combo_box("savein"))
+ , m_xInsertBtn(m_xBuilder->weld_menu_button("insert"))
+ , m_xModifyBtn(m_xBuilder->weld_menu_button("modify"))
+ , m_xResetBtn(m_xBuilder->weld_button("defaultsbtn"))
+ , m_xAddCommandButton(m_xBuilder->weld_button("add"))
+ , m_xRemoveCommandButton(m_xBuilder->weld_button("remove"))
+{
+ CustomNotebookbarGenerator::getFileNameAndAppName(m_sAppName, m_sFileName);
+
+ m_xTopLevelListBox->connect_changed(LINK(this, SvxConfigPage, SelectElementHdl));
+
+ weld::TreeView& rTreeView = m_xFunctions->get_widget();
+ Size aSize(rTreeView.get_approximate_digit_width() * 40, rTreeView.get_height_rows(8));
+ m_xFunctions->set_size_request(aSize.Width(), aSize.Height());
+ m_xDescriptionField->set_size_request(aSize.Width(), m_xDescriptionField->get_height_rows(3));
+
+ m_aUpdateDataTimer.SetInvokeHandler(LINK(this, SvxConfigPage, ImplUpdateDataHdl));
+ m_aUpdateDataTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT);
+
+ m_xSearchEdit->connect_changed(LINK(this, SvxConfigPage, SearchUpdateHdl));
+ m_xSearchEdit->connect_focus_out(LINK(this, SvxConfigPage, FocusOut_Impl));
+
+ rTreeView.connect_row_activated(LINK(this, SvxConfigPage, FunctionDoubleClickHdl));
+ rTreeView.connect_changed(LINK(this, SvxConfigPage, SelectFunctionHdl));
+}
+
+IMPL_LINK_NOARG(SvxConfigPage, SelectElementHdl, weld::ComboBox&, void)
+{
+ SelectElement();
+}
+
+SvxConfigPage::~SvxConfigPage()
+{
+ int cnt = m_xSaveInListBox->get_count();
+ for(int i=0; i < cnt; ++i)
+ {
+ SaveInData *pData = weld::fromId<SaveInData*>(m_xSaveInListBox->get_id(i));
+ delete pData;
+ }
+}
+
+void SvxConfigPage::Reset( const SfxItemSet* )
+{
+ // If we haven't initialised our XMultiServiceFactory reference
+ // then Reset is being called at the opening of the dialog.
+
+ // Load menu configuration data for the module of the currently
+ // selected document, for the currently selected document, and for
+ // all other open documents of the same module type
+ if ( !bInitialised )
+ {
+ sal_Int32 nPos = 0;
+ uno::Reference < css::ui::XUIConfigurationManager > xCfgMgr;
+ uno::Reference < css::ui::XUIConfigurationManager > xDocCfgMgr;
+
+ uno::Reference< uno::XComponentContext > xContext(
+ ::comphelper::getProcessComponentContext(), uno::UNO_SET_THROW );
+
+ m_xFrame = GetFrame();
+ m_aModuleId = GetFrameWithDefaultAndIdentify( m_xFrame );
+
+ // replace %MODULENAME in the label with the correct module name
+ uno::Reference< css::frame::XModuleManager2 > xModuleManager(
+ css::frame::ModuleManager::create( xContext ));
+ OUString aModuleName = SvxConfigPageHelper::GetUIModuleName( m_aModuleId, xModuleManager );
+
+ uno::Reference< css::ui::XModuleUIConfigurationManagerSupplier >
+ xModuleCfgSupplier( css::ui::theModuleUIConfigurationManagerSupplier::get(xContext) );
+
+ // Set up data for module specific menus
+ SaveInData* pModuleData = nullptr;
+
+ try
+ {
+ xCfgMgr =
+ xModuleCfgSupplier->getUIConfigurationManager( m_aModuleId );
+
+ pModuleData = CreateSaveInData( xCfgMgr,
+ uno::Reference< css::ui::XUIConfigurationManager >(),
+ m_aModuleId,
+ false );
+ }
+ catch ( container::NoSuchElementException& )
+ {
+ }
+
+ if ( pModuleData != nullptr )
+ {
+ OUString sId(weld::toId(pModuleData));
+ m_xSaveInListBox->append(sId, utl::ConfigManager::getProductName() + " " + aModuleName);
+ }
+
+ // try to retrieve the document based ui configuration manager
+ OUString aTitle;
+ uno::Reference< frame::XController > xController =
+ m_xFrame->getController();
+ if ( CanConfig( m_aModuleId ) && xController.is() )
+ {
+ uno::Reference< frame::XModel > xModel( xController->getModel() );
+ if ( xModel.is() )
+ {
+ uno::Reference< css::ui::XUIConfigurationManagerSupplier >
+ xCfgSupplier( xModel, uno::UNO_QUERY );
+
+ if ( xCfgSupplier.is() )
+ {
+ xDocCfgMgr = xCfgSupplier->getUIConfigurationManager();
+ }
+ aTitle = ::comphelper::DocumentInfo::getDocumentTitle( xModel );
+ }
+ }
+
+ SaveInData* pDocData = nullptr;
+ if ( xDocCfgMgr.is() )
+ {
+ pDocData = CreateSaveInData( xDocCfgMgr, xCfgMgr, m_aModuleId, true );
+
+ if ( !pDocData->IsReadOnly() )
+ {
+ OUString sId(weld::toId(pDocData));
+ m_xSaveInListBox->append(sId, aTitle);
+ }
+ }
+
+ // if an item to select has been passed in (eg. the ResourceURL for a
+ // toolbar) then try to select the SaveInData entry that has that item
+ bool bURLToSelectFound = false;
+ if ( !m_aURLToSelect.isEmpty() )
+ {
+ if ( pDocData && pDocData->HasURL( m_aURLToSelect ) )
+ {
+ m_xSaveInListBox->set_active(nPos);
+ pCurrentSaveInData = pDocData;
+ bURLToSelectFound = true;
+ }
+ else if ( pModuleData && pModuleData->HasURL( m_aURLToSelect ) )
+ {
+ m_xSaveInListBox->set_active(0);
+ pCurrentSaveInData = pModuleData;
+ bURLToSelectFound = true;
+ }
+ }
+
+ if ( !bURLToSelectFound )
+ {
+ // if the document has menu configuration settings select it
+ // it the SaveIn listbox, otherwise select the module data
+ if ( pDocData != nullptr && pDocData->HasSettings() )
+ {
+ m_xSaveInListBox->set_active(nPos);
+ pCurrentSaveInData = pDocData;
+ }
+ else
+ {
+ m_xSaveInListBox->set_active(0);
+ pCurrentSaveInData = pModuleData;
+ }
+ }
+
+#ifdef DBG_UTIL
+ DBG_ASSERT( pCurrentSaveInData, "SvxConfigPage::Reset(): no SaveInData" );
+#endif
+
+ if ( CanConfig( m_aModuleId ) )
+ {
+ // Load configuration for other open documents which have
+ // same module type
+ uno::Sequence< uno::Reference< frame::XFrame > > aFrameList;
+ try
+ {
+ uno::Reference< frame::XDesktop2 > xFramesSupplier = frame::Desktop::create(
+ xContext );
+
+ uno::Reference< frame::XFrames > xFrames =
+ xFramesSupplier->getFrames();
+
+ aFrameList = xFrames->queryFrames(
+ frame::FrameSearchFlag::ALL & ~frame::FrameSearchFlag::SELF );
+
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("cui.customize");
+ }
+
+ for ( uno::Reference < frame::XFrame > const & xf : std::as_const(aFrameList) )
+ {
+ if ( xf.is() && xf != m_xFrame )
+ {
+ OUString aCheckId;
+ try{
+ aCheckId = xModuleManager->identify( xf );
+ } catch(const uno::Exception&)
+ { aCheckId.clear(); }
+
+ if ( m_aModuleId == aCheckId )
+ {
+ // try to get the document based ui configuration manager
+ OUString aTitle2;
+ uno::Reference< frame::XController > xController_ =
+ xf->getController();
+
+ if ( xController_.is() )
+ {
+ uno::Reference< frame::XModel > xModel(
+ xController_->getModel() );
+
+ if ( xModel.is() )
+ {
+ uno::Reference<
+ css::ui::XUIConfigurationManagerSupplier >
+ xCfgSupplier( xModel, uno::UNO_QUERY );
+
+ if ( xCfgSupplier.is() )
+ {
+ xDocCfgMgr =
+ xCfgSupplier->getUIConfigurationManager();
+ }
+ aTitle2 = ::comphelper::DocumentInfo::getDocumentTitle( xModel );
+ }
+ }
+
+ if ( xDocCfgMgr.is() )
+ {
+ SaveInData* pData = CreateSaveInData( xDocCfgMgr, xCfgMgr, m_aModuleId, true );
+
+ if ( pData && !pData->IsReadOnly() )
+ {
+ OUString sId(weld::toId(pData));
+ m_xSaveInListBox->append(sId, aTitle2);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ m_xSaveInListBox->connect_changed(
+ LINK( this, SvxConfigPage, SelectSaveInLocation ) );
+
+ bInitialised = true;
+
+ Init();
+ }
+ else
+ {
+ if ( QueryReset() == RET_YES )
+ {
+ // Reset menu configuration for currently selected SaveInData
+ GetSaveInData()->Reset();
+
+ Init();
+ }
+ }
+}
+
+OUString SvxConfigPage::GetFrameWithDefaultAndIdentify( uno::Reference< frame::XFrame >& _inout_rxFrame )
+{
+ OUString sModuleID;
+ try
+ {
+ uno::Reference< uno::XComponentContext > xContext(
+ ::comphelper::getProcessComponentContext() );
+
+ uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(
+ xContext );
+
+ if ( !_inout_rxFrame.is() )
+ _inout_rxFrame = xDesktop->getActiveFrame();
+
+ if ( !_inout_rxFrame.is() )
+ {
+ _inout_rxFrame = xDesktop->getCurrentFrame();
+ }
+
+ if ( !_inout_rxFrame.is() && SfxViewFrame::Current() )
+ _inout_rxFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface();
+
+ if ( !_inout_rxFrame.is() )
+ {
+ SAL_WARN( "cui.customize", "SvxConfigPage::GetFrameWithDefaultAndIdentify(): no frame found!" );
+ return sModuleID;
+ }
+
+ sModuleID = vcl::CommandInfoProvider::GetModuleIdentifier(_inout_rxFrame);
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("cui.customize");
+ }
+
+ return sModuleID;
+}
+
+OUString SvxConfigPage::GetScriptURL() const
+{
+ OUString result;
+
+ SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(m_xFunctions->get_selected_id());
+ if (pData)
+ {
+ if ( ( pData->nKind == SfxCfgKind::FUNCTION_SLOT ) ||
+ ( pData->nKind == SfxCfgKind::FUNCTION_SCRIPT ) ||
+ ( pData->nKind == SfxCfgKind::GROUP_STYLES ) )
+ {
+ result = pData->sCommand;
+ }
+ }
+
+ return result;
+}
+
+OUString SvxConfigPage::GetSelectedDisplayName() const
+{
+ return m_xFunctions->get_selected_text();
+}
+
+bool SvxConfigPage::FillItemSet( SfxItemSet* )
+{
+ bool result = false;
+
+ for (int i = 0, nCount = m_xSaveInListBox->get_count(); i < nCount; ++i)
+ {
+ OUString sId = m_xSaveInListBox->get_id(i);
+ if (sId != notebookbarTabScope)
+ {
+ SaveInData* pData = weld::fromId<SaveInData*>(sId);
+ result = pData->Apply();
+ }
+ }
+ return result;
+}
+
+IMPL_LINK_NOARG(SvxConfigPage, SelectSaveInLocation, weld::ComboBox&, void)
+{
+ OUString sId = m_xSaveInListBox->get_active_id();
+ if (sId != notebookbarTabScope)
+ pCurrentSaveInData = weld::fromId<SaveInData*>(sId);
+ Init();
+}
+
+void SvxConfigPage::ReloadTopLevelListBox( SvxConfigEntry const * pToSelect )
+{
+ int nSelectionPos = m_xTopLevelListBox->get_active();
+ m_xTopLevelListBox->clear();
+
+ if ( GetSaveInData() && GetSaveInData()->GetEntries() )
+ {
+ for (auto const& entryData : *GetSaveInData()->GetEntries())
+ {
+ OUString sId(weld::toId(entryData));
+ m_xTopLevelListBox->append(sId, SvxConfigPageHelper::stripHotKey(entryData->GetName()));
+
+ if (entryData == pToSelect)
+ nSelectionPos = m_xTopLevelListBox->get_count() - 1;
+
+ AddSubMenusToUI( SvxConfigPageHelper::stripHotKey( entryData->GetName() ), entryData );
+ }
+ }
+#ifdef DBG_UTIL
+ else
+ {
+ DBG_ASSERT( GetSaveInData(), "SvxConfigPage::ReloadTopLevelListBox(): no SaveInData" );
+ DBG_ASSERT( GetSaveInData()->GetEntries() ,
+ "SvxConfigPage::ReloadTopLevelListBox(): no SaveInData entries" );
+ }
+#endif
+
+ nSelectionPos = (nSelectionPos != -1 && nSelectionPos < m_xTopLevelListBox->get_count()) ?
+ nSelectionPos : m_xTopLevelListBox->get_count() - 1;
+
+ m_xTopLevelListBox->set_active(nSelectionPos);
+ SelectElement();
+}
+
+void SvxConfigPage::AddSubMenusToUI(
+ std::u16string_view rBaseTitle, SvxConfigEntry const * pParentData )
+{
+ for (auto const& entryData : *pParentData->GetEntries())
+ {
+ if (entryData->IsPopup())
+ {
+ OUString subMenuTitle = OUString::Concat(rBaseTitle) + aMenuSeparatorStr + SvxConfigPageHelper::stripHotKey(entryData->GetName());
+
+ OUString sId(weld::toId(entryData));
+ m_xTopLevelListBox->append(sId, subMenuTitle);
+
+ AddSubMenusToUI( subMenuTitle, entryData );
+ }
+ }
+}
+
+SvxEntries* SvxConfigPage::FindParentForChild(
+ SvxEntries* pRootEntries, SvxConfigEntry* pChildData )
+{
+ for (auto const& entryData : *pRootEntries)
+ {
+
+ if (entryData == pChildData)
+ {
+ return pRootEntries;
+ }
+ else if (entryData->IsPopup())
+ {
+ SvxEntries* result =
+ FindParentForChild( entryData->GetEntries(), pChildData );
+
+ if ( result != nullptr )
+ {
+ return result;
+ }
+ }
+ }
+ return nullptr;
+}
+
+SvxConfigEntry *SvxConfigPage::CreateCommandFromSelection(const OUString &aURL)
+{
+ OUString aDisplayName;
+
+ if ( aURL.isEmpty() ) {
+ return nullptr;
+ }
+
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aURL, m_aModuleId);
+
+ if ( typeid(*pCurrentSaveInData) == typeid(ContextMenuSaveInData) )
+ aDisplayName = vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties);
+ else if ( typeid(*pCurrentSaveInData) == typeid(MenuSaveInData) )
+ aDisplayName = vcl::CommandInfoProvider::GetMenuLabelForCommand(aProperties);
+ else
+ aDisplayName = vcl::CommandInfoProvider::GetLabelForCommand(aProperties);
+
+ SvxConfigEntry* toret =
+ new SvxConfigEntry( aDisplayName, aURL, false, /*bParentData*/false );
+
+ toret->SetUserDefined();
+
+ if ( aDisplayName.isEmpty() )
+ toret->SetName( GetSelectedDisplayName() );
+
+ return toret;
+}
+
+bool SvxConfigPage::IsCommandInMenuList(const SvxConfigEntry *pEntryData,
+ const SvxEntries *pEntries)
+{
+ bool toret = false;
+
+ if ( pEntries != nullptr
+ && pEntryData != nullptr )
+ {
+ for (auto const& entry : *pEntries)
+ {
+ if ( entry->GetCommand() == pEntryData->GetCommand() )
+ {
+ toret = true;
+ break;
+ }
+ }
+ }
+
+ return toret;
+}
+
+int SvxConfigPage::AddFunction(int nTarget, bool bAllowDuplicates)
+{
+ int toret = -1;
+ OUString aURL = GetScriptURL();
+ SvxConfigEntry* pParent = GetTopLevelSelection();
+
+ if ( aURL.isEmpty() || pParent == nullptr )
+ {
+ return -1;
+ }
+
+
+ SvxConfigEntry * pNewEntryData = CreateCommandFromSelection( aURL );
+
+ // check that this function is not already in the menu
+ if ( !bAllowDuplicates
+ && IsCommandInMenuList( pNewEntryData, pParent->GetEntries() )
+ )
+ {
+ delete pNewEntryData;
+ } else {
+ toret = AppendEntry( pNewEntryData, nTarget );
+ }
+
+ UpdateButtonStates();
+ return toret;
+}
+
+int SvxConfigPage::AppendEntry(
+ SvxConfigEntry* pNewEntryData,
+ int nTarget)
+{
+ SvxConfigEntry* pTopLevelSelection = GetTopLevelSelection();
+
+ if (pTopLevelSelection == nullptr)
+ return -1;
+
+ // Grab the entries list for the currently selected menu
+ SvxEntries* pEntries = pTopLevelSelection->GetEntries();
+
+ int nNewEntry = -1;
+ int nCurEntry =
+ nTarget != -1 ? nTarget : m_xContentsListBox->get_selected_index();
+
+ OUString sId(weld::toId(pNewEntryData));
+
+ if (nCurEntry == -1 || nCurEntry == m_xContentsListBox->n_children() - 1)
+ {
+ pEntries->push_back( pNewEntryData );
+ m_xContentsListBox->insert(-1, sId);
+ nNewEntry = m_xContentsListBox->n_children() - 1;
+ }
+ else
+ {
+ SvxConfigEntry* pEntryData =
+ weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nCurEntry));
+
+ SvxEntries::iterator iter = pEntries->begin();
+ SvxEntries::const_iterator end = pEntries->end();
+
+ // Advance the iterator to the data for currently selected entry
+ sal_uInt16 nPos = 0;
+ while (*iter != pEntryData && ++iter != end)
+ {
+ ++nPos;
+ }
+
+ // Now step past it to the entry after the currently selected one
+ ++iter;
+ ++nPos;
+
+ // Now add the new entry to the UI and to the parent's list
+ if ( iter != end )
+ {
+ pEntries->insert( iter, pNewEntryData );
+ m_xContentsListBox->insert(nPos, sId);
+ nNewEntry = nPos;
+ }
+ }
+
+ if (nNewEntry != -1)
+ {
+ m_xContentsListBox->select(nNewEntry);
+ m_xContentsListBox->scroll_to_row(nNewEntry);
+
+ GetSaveInData()->SetModified();
+ GetTopLevelSelection()->SetModified();
+ }
+
+ return nNewEntry;
+}
+
+namespace
+{
+ template<typename itertype> void TmplInsertEntryIntoUI(SvxConfigEntry* pNewEntryData, weld::TreeView& rTreeView, itertype& rIter, SaveInData* pSaveInData,
+ VirtualDevice& rDropDown, bool bMenu)
+ {
+ OUString sId(weld::toId(pNewEntryData));
+
+ rTreeView.set_id(rIter, sId);
+
+ if (pNewEntryData->IsSeparator())
+ {
+ rTreeView.set_text(rIter, "----------------------------------", 0);
+ }
+ else
+ {
+ auto xImage = pSaveInData->GetImage(pNewEntryData->GetCommand());
+ if (xImage.is())
+ rTreeView.set_image(rIter, xImage, -1);
+ OUString aName = SvxConfigPageHelper::stripHotKey( pNewEntryData->GetName() );
+ rTreeView.set_text(rIter, aName, 0);
+ }
+
+ if (bMenu) // menus
+ {
+ if (pNewEntryData->IsPopup() || pNewEntryData->GetStyle() & css::ui::ItemStyle::DROP_DOWN)
+ rTreeView.set_image(rIter, rDropDown, 1);
+ else
+ rTreeView.set_image(rIter, css::uno::Reference<css::graphic::XGraphic>(), 1);
+ }
+ }
+}
+
+void SvxConfigPage::InsertEntryIntoUI(SvxConfigEntry* pNewEntryData, weld::TreeView& rTreeView, int nPos, bool bMenu)
+{
+ TmplInsertEntryIntoUI<int>(pNewEntryData, rTreeView, nPos, GetSaveInData(),
+ m_xContentsListBox->get_dropdown_image(), bMenu);
+}
+
+void SvxConfigPage::InsertEntryIntoUI(SvxConfigEntry* pNewEntryData, weld::TreeView& rTreeView, weld::TreeIter& rIter, bool bMenu)
+{
+ TmplInsertEntryIntoUI<weld::TreeIter>(pNewEntryData, rTreeView, rIter, GetSaveInData(),
+ m_xContentsListBox->get_dropdown_image(), bMenu);
+}
+
+IMPL_LINK(SvxConfigPage, MoveHdl, weld::Button&, rButton, void)
+{
+ MoveEntry(&rButton == m_xMoveUpButton.get());
+}
+
+IMPL_LINK_NOARG(SvxConfigPage, FunctionDoubleClickHdl, weld::TreeView&, bool)
+{
+ if (m_xAddCommandButton->get_sensitive())
+ m_xAddCommandButton->clicked();
+ return true;
+}
+
+IMPL_LINK_NOARG(SvxConfigPage, SelectFunctionHdl, weld::TreeView&, void)
+{
+ // GetScriptURL() returns a non-empty string if a
+ // valid command is selected on the left box
+ OUString aSelectCommand = GetScriptURL();
+ bool bIsValidCommand = !aSelectCommand.isEmpty();
+
+ // Enable/disable Add and Remove buttons depending on current selection
+ if (bIsValidCommand)
+ {
+ m_xAddCommandButton->set_sensitive(true);
+ m_xRemoveCommandButton->set_sensitive(true);
+
+ if (SfxHelp::IsHelpInstalled())
+ {
+ m_xDescriptionField->set_text(m_xFunctions->GetHelpText(false));
+ }
+ else
+ {
+ SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(m_xFunctions->get_selected_id());
+ if (pData)
+ {
+ bool bIsExperimental
+ = vcl::CommandInfoProvider::IsExperimental(pData->sCommand, m_aModuleId);
+
+ OUString aExperimental = "\n" + CuiResId(RID_CUISTR_COMMANDEXPERIMENTAL);
+ OUString aLabel = CuiResId(RID_CUISTR_COMMANDLABEL) + ": " + pData->sLabel + "\n";
+ OUString aName = CuiResId(RID_CUISTR_COMMANDNAME) + ": " + pData->sCommand + "\n";
+ OUString aTip = CuiResId(RID_CUISTR_COMMANDTIP) + ": " + pData->sTooltip;
+ if (bIsExperimental)
+ m_xDescriptionField->set_text(aLabel + aName + aTip + aExperimental);
+ else
+ m_xDescriptionField->set_text(aLabel + aName + aTip);
+ }
+ }
+ }
+ else
+ {
+
+ m_xAddCommandButton->set_sensitive(false);
+ m_xRemoveCommandButton->set_sensitive(false);
+
+ m_xDescriptionField->set_text("");
+ }
+
+ UpdateButtonStates();
+}
+
+IMPL_LINK_NOARG(SvxConfigPage, ImplUpdateDataHdl, Timer*, void)
+{
+ OUString aSearchTerm(m_xSearchEdit->get_text());
+ m_xCommandCategoryListBox->categorySelected(m_xFunctions.get(), aSearchTerm, GetSaveInData());
+ SelectFunctionHdl(m_xFunctions->get_widget());
+}
+
+IMPL_LINK_NOARG(SvxConfigPage, SearchUpdateHdl, weld::Entry&, void)
+{
+ m_aUpdateDataTimer.Start();
+}
+
+IMPL_LINK_NOARG(SvxConfigPage, FocusOut_Impl, weld::Widget&, void)
+{
+ if (m_aUpdateDataTimer.IsActive())
+ {
+ m_aUpdateDataTimer.Stop();
+ m_aUpdateDataTimer.Invoke();
+ }
+}
+
+void SvxConfigPage::MoveEntry(bool bMoveUp)
+{
+ weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
+
+ int nSourceEntry = rTreeView.get_selected_index();
+ int nTargetEntry = -1;
+ int nToSelect = -1;
+
+ if (nSourceEntry == -1)
+ {
+ return;
+ }
+
+ if ( bMoveUp )
+ {
+ // Move Up is just a Move Down with the source and target reversed
+ nTargetEntry = nSourceEntry;
+ nSourceEntry = nTargetEntry - 1;
+ nToSelect = nSourceEntry;
+ }
+ else
+ {
+ nTargetEntry = nSourceEntry + 1;
+ nToSelect = nTargetEntry;
+ }
+
+ if (MoveEntryData(nSourceEntry, nTargetEntry))
+ {
+ rTreeView.swap(nSourceEntry, nTargetEntry);
+ rTreeView.select(nToSelect);
+ rTreeView.scroll_to_row(nToSelect);
+
+ UpdateButtonStates();
+ }
+}
+
+bool SvxConfigPage::MoveEntryData(int nSourceEntry, int nTargetEntry)
+{
+ //#i53677#
+ if (nSourceEntry == -1 || nTargetEntry == -1)
+ {
+ return false;
+ }
+
+ // Grab the entries list for the currently selected menu
+ SvxEntries* pEntries = GetTopLevelSelection()->GetEntries();
+
+ SvxConfigEntry* pSourceData =
+ weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nSourceEntry));
+
+ SvxConfigEntry* pTargetData =
+ weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nTargetEntry));
+
+ if ( pSourceData == nullptr || pTargetData == nullptr )
+ return false;
+
+ // remove the source entry from our list
+ SvxConfigPageHelper::RemoveEntry( pEntries, pSourceData );
+
+ SvxEntries::iterator iter = pEntries->begin();
+ SvxEntries::const_iterator end = pEntries->end();
+
+ // advance the iterator to the position of the target entry
+ while (*iter != pTargetData && ++iter != end) ;
+
+ // insert the source entry at the position after the target
+ pEntries->insert( ++iter, pSourceData );
+
+ GetSaveInData()->SetModified();
+ GetTopLevelSelection()->SetModified();
+
+ return true;
+}
+
+SvxMainMenuOrganizerDialog::SvxMainMenuOrganizerDialog(
+ weld::Window* pParent, SvxEntries* entries,
+ SvxConfigEntry const * selection, bool bCreateMenu )
+ : GenericDialogController(pParent, "cui/ui/movemenu.ui", "MoveMenuDialog")
+ , m_xMenuBox(m_xBuilder->weld_widget("namebox"))
+ , m_xMenuNameEdit(m_xBuilder->weld_entry("menuname"))
+ , m_xMenuListBox(m_xBuilder->weld_tree_view("menulist"))
+ , m_xMoveUpButton(m_xBuilder->weld_button("up"))
+ , m_xMoveDownButton(m_xBuilder->weld_button("down"))
+{
+ m_xMenuListBox->set_size_request(-1, m_xMenuListBox->get_height_rows(12));
+
+ // Copy the entries list passed in
+ if ( entries != nullptr )
+ {
+ mpEntries.reset( new SvxEntries );
+ for (auto const& entry : *entries)
+ {
+ m_xMenuListBox->append(weld::toId(entry),
+ SvxConfigPageHelper::stripHotKey(entry->GetName()));
+ mpEntries->push_back(entry);
+ if (entry == selection)
+ {
+ m_xMenuListBox->select(m_xMenuListBox->n_children() - 1);
+ }
+ }
+ }
+
+ if ( bCreateMenu )
+ {
+ // Generate custom name for new menu
+ OUString prefix = CuiResId( RID_CUISTR_NEW_MENU );
+
+ OUString newname = SvxConfigPageHelper::generateCustomName( prefix, entries );
+ OUString newurl = SvxConfigPageHelper::generateCustomMenuURL( mpEntries.get() );
+
+ SvxConfigEntry* pNewEntryData =
+ new SvxConfigEntry( newname, newurl, true, /*bParentData*/false );
+ pNewEntryData->SetName( newname );
+ pNewEntryData->SetUserDefined();
+ pNewEntryData->SetMain();
+
+ m_sNewMenuEntryId = weld::toId(pNewEntryData);
+ m_xMenuListBox->append(m_sNewMenuEntryId,
+ SvxConfigPageHelper::stripHotKey(pNewEntryData->GetName()));
+ m_xMenuListBox->select(m_xMenuListBox->n_children() - 1);
+
+ if (mpEntries)
+ mpEntries->push_back(pNewEntryData);
+
+ m_xMenuNameEdit->set_text(newname);
+ m_xMenuNameEdit->connect_changed(LINK(this, SvxMainMenuOrganizerDialog, ModifyHdl));
+ }
+ else
+ {
+ // hide name label and textfield
+ m_xMenuBox->hide();
+ // change the title
+ m_xDialog->set_title(CuiResId(RID_CUISTR_MOVE_MENU));
+ }
+
+ m_xMenuListBox->connect_changed(LINK(this, SvxMainMenuOrganizerDialog, SelectHdl));
+
+ m_xMoveUpButton->connect_clicked(LINK( this, SvxMainMenuOrganizerDialog, MoveHdl));
+ m_xMoveDownButton->connect_clicked(LINK( this, SvxMainMenuOrganizerDialog, MoveHdl));
+
+ UpdateButtonStates();
+}
+
+SvxMainMenuOrganizerDialog::~SvxMainMenuOrganizerDialog()
+{
+}
+
+IMPL_LINK_NOARG(SvxMainMenuOrganizerDialog, ModifyHdl, weld::Entry&, void)
+{
+ // if the Edit control is empty do not change the name
+ if (m_xMenuNameEdit->get_text().isEmpty())
+ {
+ return;
+ }
+
+ SvxConfigEntry* pNewEntryData = weld::fromId<SvxConfigEntry*>(m_sNewMenuEntryId);
+ pNewEntryData->SetName(m_xMenuNameEdit->get_text());
+
+ const int nNewMenuPos = m_xMenuListBox->find_id(m_sNewMenuEntryId);
+ const int nOldSelection = m_xMenuListBox->get_selected_index();
+ m_xMenuListBox->remove(nNewMenuPos);
+ m_xMenuListBox->insert(nNewMenuPos, pNewEntryData->GetName(), &m_sNewMenuEntryId, nullptr, nullptr);
+ m_xMenuListBox->select(nOldSelection);
+}
+
+IMPL_LINK_NOARG(SvxMainMenuOrganizerDialog, SelectHdl, weld::TreeView&, void)
+{
+ UpdateButtonStates();
+}
+
+void SvxMainMenuOrganizerDialog::UpdateButtonStates()
+{
+ // Disable Up and Down buttons depending on current selection
+ const int nSelected = m_xMenuListBox->get_selected_index();
+ m_xMoveUpButton->set_sensitive(nSelected > 0);
+ m_xMoveDownButton->set_sensitive(nSelected != -1 && nSelected < m_xMenuListBox->n_children() - 1);
+}
+
+IMPL_LINK( SvxMainMenuOrganizerDialog, MoveHdl, weld::Button&, rButton, void )
+{
+ int nSourceEntry = m_xMenuListBox->get_selected_index();
+ if (nSourceEntry == -1)
+ return;
+
+ int nTargetEntry;
+
+ if (&rButton == m_xMoveDownButton.get())
+ {
+ nTargetEntry = nSourceEntry + 1;
+ }
+ else
+ {
+ // Move Up is just a Move Down with the source and target reversed
+ nTargetEntry = nSourceEntry - 1;
+ }
+
+ OUString sId = m_xMenuListBox->get_id(nSourceEntry);
+ OUString sEntry = m_xMenuListBox->get_text(nSourceEntry);
+ m_xMenuListBox->remove(nSourceEntry);
+ m_xMenuListBox->insert(nTargetEntry, sEntry, &sId, nullptr, nullptr);
+ m_xMenuListBox->select(nTargetEntry);
+
+ std::swap(mpEntries->at(nSourceEntry), mpEntries->at(nTargetEntry));
+
+ UpdateButtonStates();
+}
+
+SvxConfigEntry* SvxMainMenuOrganizerDialog::GetSelectedEntry()
+{
+ const int nSelected(m_xMenuListBox->get_selected_index());
+ if (nSelected == -1)
+ return nullptr;
+ return weld::fromId<SvxConfigEntry*>(m_xMenuListBox->get_id(nSelected));
+}
+
+SvxConfigEntry::SvxConfigEntry( OUString aDisplayName,
+ OUString aCommandURL, bool bPopup, bool bParentData )
+ : nId( 1 )
+ , aLabel(std::move(aDisplayName))
+ , aCommand(std::move(aCommandURL))
+ , bPopUp(bPopup)
+ , bStrEdited( false )
+ , bIsUserDefined( false )
+ , bIsMain( false )
+ , bIsParentData( bParentData )
+ , bIsModified( false )
+ , bIsVisible( true )
+ , nStyle( 0 )
+{
+ if (bPopUp)
+ {
+ mpEntries.reset( new SvxEntries );
+ }
+}
+
+SvxConfigEntry::~SvxConfigEntry()
+{
+ if (mpEntries)
+ {
+ for (auto const& entry : *mpEntries)
+ {
+ delete entry;
+ }
+ }
+}
+
+bool SvxConfigEntry::IsMovable() const
+{
+ return !IsPopup() || IsMain();
+}
+
+bool SvxConfigEntry::IsDeletable() const
+{
+ return !IsMain() || IsUserDefined();
+}
+
+bool SvxConfigEntry::IsRenamable() const
+{
+ return !IsMain() || IsUserDefined();
+}
+
+ToolbarSaveInData::ToolbarSaveInData(
+ const uno::Reference < css::ui::XUIConfigurationManager >& xCfgMgr,
+ const uno::Reference < css::ui::XUIConfigurationManager >& xParentCfgMgr,
+ const OUString& aModuleId,
+ bool docConfig ) :
+
+ SaveInData ( xCfgMgr, xParentCfgMgr, aModuleId, docConfig ),
+ m_aDescriptorContainer ( ITEM_DESCRIPTOR_CONTAINER )
+
+{
+ uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
+ // Initialize the m_xPersistentWindowState variable which is used
+ // to get the default properties of system toolbars such as name
+ uno::Reference< container::XNameAccess > xPWSS = css::ui::theWindowStateConfiguration::get( xContext );
+
+ xPWSS->getByName( aModuleId ) >>= m_xPersistentWindowState;
+}
+
+ToolbarSaveInData::~ToolbarSaveInData()
+{
+}
+
+sal_Int32 ToolbarSaveInData::GetSystemStyle( const OUString& rResourceURL )
+{
+ sal_Int32 result = 0;
+
+ if ( rResourceURL.startsWith( "private" ) &&
+ m_xPersistentWindowState.is() &&
+ m_xPersistentWindowState->hasByName( rResourceURL ) )
+ {
+ try
+ {
+ uno::Sequence< beans::PropertyValue > aProps;
+ uno::Any a( m_xPersistentWindowState->getByName( rResourceURL ) );
+
+ if ( a >>= aProps )
+ {
+ for ( beans::PropertyValue const & prop : std::as_const(aProps) )
+ {
+ if ( prop.Name == ITEM_DESCRIPTOR_STYLE )
+ {
+ prop.Value >>= result;
+ break;
+ }
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ // do nothing, a default value is returned
+ }
+ }
+
+ return result;
+}
+
+void ToolbarSaveInData::SetSystemStyle(
+ const uno::Reference< frame::XFrame >& xFrame,
+ const OUString& rResourceURL,
+ sal_Int32 nStyle )
+{
+ // change the style using the API
+ SetSystemStyle( rResourceURL, nStyle );
+
+ // this code is a temporary hack as the UI is not updating after
+ // changing the toolbar style via the API
+ uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ vcl::Window *window = nullptr;
+
+ uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ uno::Any a = xPropSet->getPropertyValue( "LayoutManager" );
+ a >>= xLayoutManager;
+ }
+
+ if ( xLayoutManager.is() )
+ {
+ uno::Reference< css::ui::XUIElement > xUIElement =
+ xLayoutManager->getElement( rResourceURL );
+
+ // check reference before we call getRealInterface. The layout manager
+ // can only provide references for elements that have been created
+ // before. It's possible that the current element is not available.
+ uno::Reference< css::awt::XWindow > xWindow;
+ if ( xUIElement.is() )
+ xWindow.set( xUIElement->getRealInterface(), uno::UNO_QUERY );
+
+ window = VCLUnoHelper::GetWindow( xWindow );
+ }
+
+ if ( window == nullptr || window->GetType() != WindowType::TOOLBOX )
+ return;
+
+ ToolBox* toolbox = static_cast<ToolBox*>(window);
+
+ if ( nStyle == 0 )
+ {
+ toolbox->SetButtonType( ButtonType::SYMBOLONLY );
+ }
+ else if ( nStyle == 1 )
+ {
+ toolbox->SetButtonType( ButtonType::TEXT );
+ }
+ if ( nStyle == 2 )
+ {
+ toolbox->SetButtonType( ButtonType::SYMBOLTEXT );
+ }
+}
+
+void ToolbarSaveInData::SetSystemStyle(
+ const OUString& rResourceURL,
+ sal_Int32 nStyle )
+{
+ if ( !(rResourceURL.startsWith( "private" ) &&
+ m_xPersistentWindowState.is() &&
+ m_xPersistentWindowState->hasByName( rResourceURL )) )
+ return;
+
+ try
+ {
+ uno::Sequence< beans::PropertyValue > aProps;
+
+ uno::Any a( m_xPersistentWindowState->getByName( rResourceURL ) );
+
+ if ( a >>= aProps )
+ {
+ for ( beans::PropertyValue& prop : asNonConstRange(aProps) )
+ {
+ if ( prop.Name == ITEM_DESCRIPTOR_STYLE )
+ {
+ prop.Value <<= nStyle;
+ break;
+ }
+ }
+ }
+
+ uno::Reference< container::XNameReplace >
+ xNameReplace( m_xPersistentWindowState, uno::UNO_QUERY );
+
+ xNameReplace->replaceByName( rResourceURL, uno::Any( aProps ) );
+ }
+ catch ( uno::Exception& )
+ {
+ // do nothing, a default value is returned
+ TOOLS_WARN_EXCEPTION("cui.customize", "Exception setting toolbar style");
+ }
+}
+
+OUString ToolbarSaveInData::GetSystemUIName( const OUString& rResourceURL )
+{
+ OUString result;
+
+ if ( rResourceURL.startsWith( "private" ) &&
+ m_xPersistentWindowState.is() &&
+ m_xPersistentWindowState->hasByName( rResourceURL ) )
+ {
+ try
+ {
+ uno::Sequence< beans::PropertyValue > aProps;
+ uno::Any a( m_xPersistentWindowState->getByName( rResourceURL ) );
+
+ if ( a >>= aProps )
+ {
+ for ( beans::PropertyValue const & prop : std::as_const(aProps) )
+ {
+ if ( prop.Name == ITEM_DESCRIPTOR_UINAME )
+ {
+ prop.Value >>= result;
+ }
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ // do nothing, an empty UIName will be returned
+ }
+ }
+
+ if ( rResourceURL.startsWith( ".uno" ) &&
+ m_xCommandToLabelMap.is() &&
+ m_xCommandToLabelMap->hasByName( rResourceURL ) )
+ {
+ uno::Any a;
+ try
+ {
+ a = m_xCommandToLabelMap->getByName( rResourceURL );
+
+ uno::Sequence< beans::PropertyValue > aPropSeq;
+ if ( a >>= aPropSeq )
+ {
+ for ( beans::PropertyValue const & prop : std::as_const(aPropSeq) )
+ {
+ if ( prop.Name == ITEM_DESCRIPTOR_LABEL )
+ {
+ prop.Value >>= result;
+ }
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ // not a system command name
+ }
+ }
+
+ return result;
+}
+
+SvxEntries* ToolbarSaveInData::GetEntries()
+{
+ typedef std::unordered_map<OUString, bool > ToolbarInfo;
+
+ ToolbarInfo aToolbarInfo;
+
+ if ( pRootEntry == nullptr )
+ {
+
+ pRootEntry.reset( new SvxConfigEntry( "MainToolbars", OUString(), true, /*bParentData*/false) );
+
+ const uno::Sequence< uno::Sequence < beans::PropertyValue > > info =
+ GetConfigManager()->getUIElementsInfo(
+ css::ui::UIElementType::TOOLBAR );
+
+ for ( uno::Sequence<beans::PropertyValue> const & props : info )
+ {
+ OUString url;
+ OUString systemname;
+ OUString uiname;
+
+ for ( const beans::PropertyValue& prop : props )
+ {
+ if ( prop.Name == ITEM_DESCRIPTOR_RESOURCEURL )
+ {
+ prop.Value >>= url;
+ systemname = url.copy( url.lastIndexOf( '/' ) + 1 );
+ }
+ else if ( prop.Name == ITEM_DESCRIPTOR_UINAME )
+ {
+ prop.Value >>= uiname;
+ }
+ }
+
+ try
+ {
+ uno::Reference< container::XIndexAccess > xToolbarSettings =
+ GetConfigManager()->getSettings( url, false );
+
+ if ( uiname.isEmpty() )
+ {
+ // try to get the name from m_xPersistentWindowState
+ uiname = GetSystemUIName( url );
+
+ if ( uiname.isEmpty() )
+ {
+ uiname = systemname;
+ }
+ }
+
+ SvxConfigEntry* pEntry = new SvxConfigEntry(
+ uiname, url, true, /*bParentData*/false );
+
+ pEntry->SetMain();
+ pEntry->SetStyle( GetSystemStyle( url ) );
+
+
+ // insert into std::unordered_map to filter duplicates from the parent
+ aToolbarInfo.emplace( systemname, true );
+
+ if ( systemname.startsWith( CUSTOM_TOOLBAR_STR ) )
+ {
+ pEntry->SetUserDefined();
+ }
+ else
+ {
+ pEntry->SetUserDefined( false );
+ }
+
+ pRootEntry->GetEntries()->push_back( pEntry );
+
+ LoadToolbar( xToolbarSettings, pEntry );
+ }
+ catch ( container::NoSuchElementException& )
+ {
+ // TODO, handle resourceURL with no settings
+ }
+ }
+
+ uno::Reference< css::ui::XUIConfigurationManager > xParentCfgMgr = GetParentConfigManager();
+ if ( xParentCfgMgr.is() )
+ {
+ // Retrieve also the parent toolbars to make it possible
+ // to configure module toolbars and save them into the document
+ // config manager.
+ const uno::Sequence< uno::Sequence < beans::PropertyValue > > info_ =
+ xParentCfgMgr->getUIElementsInfo(
+ css::ui::UIElementType::TOOLBAR );
+
+ for ( uno::Sequence<beans::PropertyValue> const & props : info_ )
+ {
+ OUString url;
+ OUString systemname;
+ OUString uiname;
+
+ for ( const beans::PropertyValue& prop : props )
+ {
+ if ( prop.Name == ITEM_DESCRIPTOR_RESOURCEURL )
+ {
+ prop.Value >>= url;
+ systemname = url.copy( url.lastIndexOf( '/' ) + 1 );
+ }
+ else if ( prop.Name == ITEM_DESCRIPTOR_UINAME )
+ {
+ prop.Value >>= uiname;
+ }
+ }
+
+ // custom toolbars of the parent are not visible in the document layer
+ OUString custom(CUSTOM_TOOLBAR_STR);
+ if ( systemname.startsWith( custom ) )
+ continue;
+
+ // check if toolbar is already in the document layer
+ ToolbarInfo::const_iterator pIter = aToolbarInfo.find( systemname );
+ if ( pIter == aToolbarInfo.end() )
+ {
+ aToolbarInfo.emplace( systemname, true );
+
+ try
+ {
+ uno::Reference< container::XIndexAccess > xToolbarSettings =
+ xParentCfgMgr->getSettings( url, false );
+
+ if ( uiname.isEmpty() )
+ {
+ // try to get the name from m_xPersistentWindowState
+ uiname = GetSystemUIName( url );
+
+ if ( uiname.isEmpty() )
+ {
+ uiname = systemname;
+ }
+ }
+
+ SvxConfigEntry* pEntry = new SvxConfigEntry(
+ uiname, url, true, true );
+
+ pEntry->SetMain();
+ pEntry->SetStyle( GetSystemStyle( url ) );
+
+ if ( systemname.startsWith( custom ) )
+ {
+ pEntry->SetUserDefined();
+ }
+ else
+ {
+ pEntry->SetUserDefined( false );
+ }
+
+ pRootEntry->GetEntries()->push_back( pEntry );
+
+ LoadToolbar( xToolbarSettings, pEntry );
+ }
+ catch ( container::NoSuchElementException& )
+ {
+ // TODO, handle resourceURL with no settings
+ }
+ }
+ }
+ }
+
+ std::sort( GetEntries()->begin(), GetEntries()->end(), SvxConfigPageHelper::EntrySort );
+ }
+
+ return pRootEntry->GetEntries();
+}
+
+void
+ToolbarSaveInData::SetEntries( std::unique_ptr<SvxEntries> pNewEntries )
+{
+ pRootEntry->SetEntries( std::move(pNewEntries) );
+}
+
+bool
+ToolbarSaveInData::HasURL( const OUString& rURL )
+{
+ for (auto const& entry : *GetEntries())
+ {
+ if (entry->GetCommand() == rURL)
+ {
+ return !entry->IsParentData();
+ }
+ }
+ return false;
+}
+
+bool ToolbarSaveInData::HasSettings()
+{
+ // return true if there is at least one toolbar entry
+ return !GetEntries()->empty();
+}
+
+void ToolbarSaveInData::Reset()
+{
+ // reset each toolbar by calling removeSettings for its toolbar URL
+ for (auto const& entry : *GetEntries())
+ {
+ try
+ {
+ const OUString& url = entry->GetCommand();
+ GetConfigManager()->removeSettings( url );
+ }
+ catch ( uno::Exception& )
+ {
+ // error occurred removing the settings
+ // TODO - add error dialog in future?
+ }
+ }
+
+ // persist changes to toolbar storage
+ PersistChanges( GetConfigManager() );
+
+ // now delete the root SvxConfigEntry the next call to GetEntries()
+ // causes it to be reinitialised
+ pRootEntry.reset();
+
+ // reset all icons to default
+ try
+ {
+ GetImageManager()->reset();
+ PersistChanges( GetImageManager() );
+ }
+ catch ( uno::Exception& )
+ {
+ SAL_WARN("cui.customize", "Error resetting all icons when resetting toolbars");
+ }
+}
+
+bool ToolbarSaveInData::Apply()
+{
+ // toolbar changes are instantly applied
+ return false;
+}
+
+void ToolbarSaveInData::ApplyToolbar(
+ uno::Reference< container::XIndexContainer > const & rToolbarBar,
+ uno::Reference< lang::XSingleComponentFactory >& rFactory,
+ SvxConfigEntry const * pToolbarData )
+{
+ uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
+
+ for (auto const& entry : *pToolbarData->GetEntries())
+ {
+ if (entry->IsPopup())
+ {
+ uno::Sequence< beans::PropertyValue > aPropValueSeq =
+ SvxConfigPageHelper::ConvertToolbarEntry(entry);
+
+ uno::Reference< container::XIndexContainer > xSubMenuBar(
+ rFactory->createInstanceWithContext( xContext ),
+ uno::UNO_QUERY );
+
+ sal_Int32 nIndex = aPropValueSeq.getLength();
+ aPropValueSeq.realloc( nIndex + 1 );
+ auto pPropValueSeq = aPropValueSeq.getArray();
+ pPropValueSeq[nIndex].Name = m_aDescriptorContainer;
+ pPropValueSeq[nIndex].Value <<= xSubMenuBar;
+ rToolbarBar->insertByIndex(
+ rToolbarBar->getCount(), uno::Any( aPropValueSeq ));
+
+ ApplyToolbar(xSubMenuBar, rFactory, entry);
+ }
+ else if (entry->IsSeparator())
+ {
+ rToolbarBar->insertByIndex(
+ rToolbarBar->getCount(), uno::Any( m_aSeparatorSeq ));
+ }
+ else
+ {
+ uno::Sequence< beans::PropertyValue > aPropValueSeq =
+ SvxConfigPageHelper::ConvertToolbarEntry(entry);
+
+ rToolbarBar->insertByIndex(
+ rToolbarBar->getCount(), uno::Any( aPropValueSeq ));
+ }
+ }
+}
+
+void ToolbarSaveInData::ApplyToolbar( SvxConfigEntry* pToolbar )
+{
+ // Apply new toolbar structure to our settings container
+ uno::Reference< container::XIndexAccess > xSettings =
+ GetConfigManager()->createSettings();
+
+ uno::Reference< container::XIndexContainer > xIndexContainer (
+ xSettings, uno::UNO_QUERY );
+
+ uno::Reference< lang::XSingleComponentFactory > xFactory (
+ xSettings, uno::UNO_QUERY );
+
+ ApplyToolbar( xIndexContainer, xFactory, pToolbar );
+
+ uno::Reference< beans::XPropertySet > xProps(
+ xSettings, uno::UNO_QUERY );
+
+ if ( pToolbar->IsUserDefined() )
+ {
+ xProps->setPropertyValue(
+ ITEM_DESCRIPTOR_UINAME,
+ uno::Any( pToolbar->GetName() ) );
+ }
+
+ try
+ {
+ if ( GetConfigManager()->hasSettings( pToolbar->GetCommand() ) )
+ {
+ GetConfigManager()->replaceSettings(
+ pToolbar->GetCommand(), xSettings );
+ }
+ else
+ {
+ GetConfigManager()->insertSettings(
+ pToolbar->GetCommand(), xSettings );
+ if ( pToolbar->IsParentData() )
+ pToolbar->SetParentData( false );
+ }
+ }
+ catch ( css::uno::Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION("cui.customize", "caught exception saving settings");
+ }
+
+ PersistChanges( GetConfigManager() );
+}
+
+void ToolbarSaveInData::CreateToolbar( SvxConfigEntry* pToolbar )
+{
+ // show the new toolbar in the UI also
+ uno::Reference< container::XIndexAccess >
+ xSettings = GetConfigManager()->createSettings();
+
+ uno::Reference< beans::XPropertySet >
+ xPropertySet( xSettings, uno::UNO_QUERY );
+
+ xPropertySet->setPropertyValue(
+ ITEM_DESCRIPTOR_UINAME,
+ uno::Any( pToolbar->GetName() ) );
+
+ try
+ {
+ GetConfigManager()->insertSettings( pToolbar->GetCommand(), xSettings );
+ }
+ catch ( css::uno::Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION("cui.customize", "caught exception saving settings");
+ }
+
+ GetEntries()->push_back( pToolbar );
+
+ PersistChanges( GetConfigManager() );
+}
+
+void ToolbarSaveInData::RemoveToolbar( SvxConfigEntry* pToolbar )
+{
+ try
+ {
+ OUString url = pToolbar->GetCommand();
+ GetConfigManager()->removeSettings( url );
+ SvxConfigPageHelper::RemoveEntry( GetEntries(), pToolbar );
+ delete pToolbar;
+
+ PersistChanges( GetConfigManager() );
+
+ // remove the persistent window state data
+ css::uno::Reference< css::container::XNameContainer > xNameContainer(
+ m_xPersistentWindowState, css::uno::UNO_QUERY_THROW );
+
+ xNameContainer->removeByName( url );
+ }
+ catch ( uno::Exception& )
+ {
+ // error occurred removing the settings
+ }
+}
+
+void ToolbarSaveInData::RestoreToolbar( SvxConfigEntry* pToolbar )
+{
+ OUString url = pToolbar->GetCommand();
+
+ // Restore of toolbar is done by removing it from
+ // its configuration manager and then getting it again
+ bool bParentToolbar = pToolbar->IsParentData();
+
+ // Cannot restore parent toolbar
+ if ( bParentToolbar )
+ return;
+
+ try
+ {
+ GetConfigManager()->removeSettings( url );
+ pToolbar->GetEntries()->clear();
+ PersistChanges( GetConfigManager() );
+ }
+ catch ( uno::Exception& )
+ {
+ // if an error occurs removing the settings then just return
+ return;
+ }
+
+ // Now reload the toolbar settings
+ try
+ {
+ uno::Reference< container::XIndexAccess > xToolbarSettings;
+ if ( IsDocConfig() )
+ {
+ xToolbarSettings = GetParentConfigManager()->getSettings( url, false );
+ pToolbar->SetParentData();
+ }
+ else
+ xToolbarSettings = GetConfigManager()->getSettings( url, false );
+
+ LoadToolbar( xToolbarSettings, pToolbar );
+
+ // After reloading, ensure that the icon is reset of each entry
+ // in the toolbar
+ uno::Sequence< OUString > aURLSeq( 1 );
+ auto pURLSeq = aURLSeq.getArray();
+ for (auto const& entry : *pToolbar->GetEntries())
+ {
+ pURLSeq[ 0 ] = entry->GetCommand();
+
+ try
+ {
+ GetImageManager()->removeImages( SvxConfigPageHelper::GetImageType(), aURLSeq );
+ }
+ catch ( uno::Exception& )
+ {
+ SAL_WARN("cui.customize", "Error restoring icon when resetting toolbar");
+ }
+ }
+ PersistChanges( GetImageManager() );
+ }
+ catch ( container::NoSuchElementException& )
+ {
+ // cannot find the resource URL after removing it
+ // so no entry will appear in the toolbar list
+ }
+}
+
+void ToolbarSaveInData::LoadToolbar(
+ const uno::Reference< container::XIndexAccess >& xToolbarSettings,
+ SvxConfigEntry const * pParentData )
+{
+ SvxEntries* pEntries = pParentData->GetEntries();
+
+ for ( sal_Int32 nIndex = 0; nIndex < xToolbarSettings->getCount(); ++nIndex )
+ {
+ OUString aCommandURL;
+ OUString aLabel;
+ bool bIsVisible;
+ sal_Int32 nStyle;
+
+ sal_uInt16 nType( css::ui::ItemType::DEFAULT );
+
+ bool bItem = SvxConfigPageHelper::GetToolbarItemData( xToolbarSettings, nIndex, aCommandURL,
+ aLabel, nType, bIsVisible, nStyle );
+
+ if ( bItem )
+ {
+ bool bIsUserDefined = true;
+
+ if ( nType == css::ui::ItemType::DEFAULT )
+ {
+ uno::Any a;
+ try
+ {
+ a = m_xCommandToLabelMap->getByName( aCommandURL );
+ bIsUserDefined = false;
+ }
+ catch ( container::NoSuchElementException& )
+ {
+ bIsUserDefined = true;
+ }
+
+ bool bUseDefaultLabel = false;
+ // If custom label not set retrieve it from the command
+ // to info service
+ if ( aLabel.isEmpty() )
+ {
+ bUseDefaultLabel = true;
+ uno::Sequence< beans::PropertyValue > aPropSeq;
+ if ( a >>= aPropSeq )
+ {
+ for ( beans::PropertyValue const & prop : std::as_const(aPropSeq) )
+ {
+ if ( prop.Name == "Name" )
+ {
+ prop.Value >>= aLabel;
+ break;
+ }
+ }
+ }
+ }
+
+ SvxConfigEntry* pEntry = new SvxConfigEntry(
+ aLabel, aCommandURL, false, /*bParentData*/false );
+
+ pEntry->SetUserDefined( bIsUserDefined );
+ pEntry->SetVisible( bIsVisible );
+ pEntry->SetStyle( nStyle );
+
+ if ( !bUseDefaultLabel )
+ pEntry->SetName( aLabel );
+
+ pEntries->push_back( pEntry );
+ }
+ else
+ {
+ SvxConfigEntry* pEntry = new SvxConfigEntry;
+ pEntry->SetUserDefined( bIsUserDefined );
+ pEntries->push_back( pEntry );
+ }
+ }
+ }
+}
+
+SvxNewToolbarDialog::SvxNewToolbarDialog(weld::Window* pWindow, const OUString& rName)
+ : GenericDialogController(pWindow, "cui/ui/newtoolbardialog.ui", "NewToolbarDialog")
+ , m_xEdtName(m_xBuilder->weld_entry("edit"))
+ , m_xBtnOK(m_xBuilder->weld_button("ok"))
+ , m_xSaveInListBox(m_xBuilder->weld_combo_box("savein"))
+{
+ m_xEdtName->set_text(rName);
+ m_xEdtName->select_region(0, -1);
+}
+
+SvxNewToolbarDialog::~SvxNewToolbarDialog()
+{
+}
+
+/*******************************************************************************
+*
+* The SvxIconSelectorDialog class
+*
+*******************************************************************************/
+SvxIconSelectorDialog::SvxIconSelectorDialog(weld::Window *pWindow,
+ uno::Reference< css::ui::XImageManager > xImageManager,
+ uno::Reference< css::ui::XImageManager > xParentImageManager)
+ : GenericDialogController(pWindow, "cui/ui/iconselectordialog.ui", "IconSelector")
+ , m_xImageManager(std::move(xImageManager))
+ , m_xParentImageManager(std::move(xParentImageManager))
+ , m_xTbSymbol(new ValueSet(m_xBuilder->weld_scrolled_window("symbolswin", true)))
+ , m_xTbSymbolWin(new weld::CustomWeld(*m_xBuilder, "symbolsToolbar", *m_xTbSymbol))
+ , m_xFtNote(m_xBuilder->weld_label("noteLabel"))
+ , m_xBtnImport(m_xBuilder->weld_button("importButton"))
+ , m_xBtnDelete(m_xBuilder->weld_button("deleteButton"))
+{
+ typedef std::unordered_map< OUString, bool > ImageInfo;
+
+ m_nExpectedSize = 16;
+ if (SvxConfigPageHelper::GetImageType() & css::ui::ImageType::SIZE_LARGE)
+ m_nExpectedSize = 24;
+ else if (SvxConfigPageHelper::GetImageType() & css::ui::ImageType::SIZE_32)
+ m_nExpectedSize = 32;
+
+ if ( m_nExpectedSize != 16 )
+ {
+ m_xFtNote->set_label(SvxConfigPageHelper::replaceSixteen(m_xFtNote->get_label(), m_nExpectedSize));
+ }
+
+ m_xTbSymbol->SetStyle(m_xTbSymbol->GetStyle() | WB_ITEMBORDER | WB_VSCROLL);
+ m_xTbSymbol->SetColCount(11);
+ m_xTbSymbol->SetLineCount(5);
+ m_xTbSymbol->SetItemWidth(m_nExpectedSize);
+ m_xTbSymbol->SetItemHeight(m_nExpectedSize);
+ m_xTbSymbol->SetExtraSpacing(6);
+ Size aSize(m_xTbSymbol->CalcWindowSizePixel(Size(m_nExpectedSize, m_nExpectedSize), 11, 5));
+ m_xTbSymbol->set_size_request(aSize.Width(), aSize.Height());
+
+ uno::Reference< uno::XComponentContext > xComponentContext =
+ ::comphelper::getProcessComponentContext();
+
+ m_xGraphProvider.set( graphic::GraphicProvider::create( xComponentContext ) );
+
+ uno::Reference< css::util::XPathSettings > xPathSettings =
+ css::util::thePathSettings::get( xComponentContext );
+
+
+ OUString aDirectory = xPathSettings->getUserConfig();
+
+ sal_Int32 aCount = aDirectory.getLength();
+
+ if ( aCount > 0 )
+ {
+ sal_Unicode aChar = aDirectory[ aCount-1 ];
+ if ( aChar != '/')
+ {
+ aDirectory += "/";
+ }
+ }
+ else
+ {
+ m_xBtnImport->set_sensitive(false);
+ }
+
+ aDirectory += "soffice.cfg/import";
+
+ uno::Reference< lang::XSingleServiceFactory > xStorageFactory(
+ css::embed::FileSystemStorageFactory::create( xComponentContext ) );
+
+ uno::Sequence< uno::Any > aArgs{ uno::Any(aDirectory),
+ uno::Any(css::embed::ElementModes::READWRITE) };
+
+ uno::Reference< css::embed::XStorage > xStorage(
+ xStorageFactory->createInstanceWithArguments( aArgs ), uno::UNO_QUERY );
+
+ uno::Sequence<uno::Any> aProp(comphelper::InitAnyPropertySequence(
+ {
+ {"UserConfigStorage", uno::Any(xStorage)},
+ {"OpenMode", uno::Any(css::embed::ElementModes::READWRITE)}
+ }));
+ m_xImportedImageManager = css::ui::ImageManager::create( xComponentContext );
+ m_xImportedImageManager->initialize(aProp);
+
+ ImageInfo aImageInfo1;
+ if ( m_xImportedImageManager.is() )
+ {
+ const uno::Sequence< OUString > names = m_xImportedImageManager->getAllImageNames( SvxConfigPageHelper::GetImageType() );
+ for (auto const & name : names )
+ aImageInfo1.emplace( name, false );
+ }
+
+ uno::Sequence< OUString > name( 1 );
+ auto pname = name.getArray();
+ for (auto const& elem : aImageInfo1)
+ {
+ pname[ 0 ] = elem.first;
+ uno::Sequence< uno::Reference< graphic::XGraphic> > graphics = m_xImportedImageManager->getImages( SvxConfigPageHelper::GetImageType(), name );
+ if ( graphics.hasElements() )
+ {
+ m_aGraphics.push_back(graphics[0]);
+ Image img(graphics[0]);
+ m_xTbSymbol->InsertItem(m_aGraphics.size(), img, elem.first);
+ }
+ }
+
+ ImageInfo aImageInfo;
+
+ if ( m_xParentImageManager.is() )
+ {
+ const uno::Sequence< OUString > names = m_xParentImageManager->getAllImageNames( SvxConfigPageHelper::GetImageType() );
+ for ( auto const & i : names )
+ aImageInfo.emplace( i, false );
+ }
+
+ const uno::Sequence< OUString > names = m_xImageManager->getAllImageNames( SvxConfigPageHelper::GetImageType() );
+ for ( auto const & i : names )
+ {
+ ImageInfo::iterator pIter = aImageInfo.find( i );
+ if ( pIter != aImageInfo.end() )
+ pIter->second = true;
+ else
+ aImageInfo.emplace( i, true );
+ }
+
+ // large growth factor, expecting many entries
+ for (auto const& elem : aImageInfo)
+ {
+ pname[ 0 ] = elem.first;
+
+ uno::Sequence< uno::Reference< graphic::XGraphic> > graphics;
+ try
+ {
+ if (elem.second)
+ graphics = m_xImageManager->getImages( SvxConfigPageHelper::GetImageType(), name );
+ else
+ graphics = m_xParentImageManager->getImages( SvxConfigPageHelper::GetImageType(), name );
+ }
+ catch ( uno::Exception& )
+ {
+ // can't get sequence for this name so it will not be
+ // added to the list
+ }
+
+ if ( graphics.hasElements() )
+ {
+ Image img(graphics[0]);
+ if (!img.GetBitmapEx().IsEmpty())
+ {
+ m_aGraphics.push_back(graphics[0]);
+ m_xTbSymbol->InsertItem(m_aGraphics.size(), img, elem.first);
+ }
+ }
+ }
+
+ m_xBtnDelete->set_sensitive( false );
+ m_xTbSymbol->SetSelectHdl( LINK(this, SvxIconSelectorDialog, SelectHdl) );
+ m_xBtnImport->connect_clicked( LINK(this, SvxIconSelectorDialog, ImportHdl) );
+ m_xBtnDelete->connect_clicked( LINK(this, SvxIconSelectorDialog, DeleteHdl) );
+}
+
+SvxIconSelectorDialog::~SvxIconSelectorDialog()
+{
+}
+
+uno::Reference< graphic::XGraphic> SvxIconSelectorDialog::GetSelectedIcon()
+{
+ uno::Reference<graphic::XGraphic> result;
+
+ sal_uInt16 nId = m_xTbSymbol->GetSelectedItemId();
+
+ if (nId)
+ {
+ result = m_aGraphics[nId - 1];
+ }
+
+ return result;
+}
+
+IMPL_LINK_NOARG(SvxIconSelectorDialog, SelectHdl, ValueSet*, void)
+{
+ sal_uInt16 nId = m_xTbSymbol->GetSelectedItemId();
+
+ if (!nId)
+ {
+ m_xBtnDelete->set_sensitive(false);
+ return;
+ }
+
+ OUString aSelImageText = m_xTbSymbol->GetItemText(nId);
+ if (m_xImportedImageManager->hasImage(SvxConfigPageHelper::GetImageType(), aSelImageText))
+ {
+ m_xBtnDelete->set_sensitive(true);
+ }
+ else
+ {
+ m_xBtnDelete->set_sensitive(false);
+ }
+}
+
+IMPL_LINK_NOARG(SvxIconSelectorDialog, ImportHdl, weld::Button&, void)
+{
+ sfx2::FileDialogHelper aImportDialog(
+ css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW,
+ FileDialogFlags::Graphic | FileDialogFlags::MultiSelection, m_xDialog.get());
+ aImportDialog.SetContext(sfx2::FileDialogHelper::IconImport);
+
+ // disable the link checkbox in the dialog
+ uno::Reference< css::ui::dialogs::XFilePickerControlAccess >
+ xController( aImportDialog.GetFilePicker(), uno::UNO_QUERY);
+ if ( xController.is() )
+ {
+ xController->enableControl(
+ css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK,
+ false);
+ }
+
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ sal_uInt16 nFilter = rFilter.GetImportFormatNumberForShortName(u"png");
+ aImportDialog.SetCurrentFilter(rFilter.GetImportFormatName(nFilter));
+
+ if ( ERRCODE_NONE == aImportDialog.Execute() )
+ {
+ uno::Sequence< OUString > paths = aImportDialog.GetMPath();
+ ImportGraphics ( paths );
+ }
+}
+
+IMPL_LINK_NOARG(SvxIconSelectorDialog, DeleteHdl, weld::Button&, void)
+{
+ OUString message = CuiResId( RID_CUISTR_DELETE_ICON_CONFIRM );
+
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::OkCancel,
+ message));
+ if (xWarn->run() != RET_OK)
+ return;
+
+ sal_uInt16 nId = m_xTbSymbol->GetSelectedItemId();
+
+ OUString aSelImageText = m_xTbSymbol->GetItemText( nId );
+ uno::Sequence< OUString > URLs { aSelImageText };
+ m_xTbSymbol->RemoveItem(nId);
+ m_xImportedImageManager->removeImages( SvxConfigPageHelper::GetImageType(), URLs );
+ if ( m_xImportedImageManager->isModified() )
+ {
+ m_xImportedImageManager->store();
+ }
+}
+
+bool SvxIconSelectorDialog::ReplaceGraphicItem(
+ const OUString& aURL )
+{
+ uno::Reference< graphic::XGraphic > xGraphic;
+ uno::Sequence< beans::PropertyValue > aMediaProps{ comphelper::makePropertyValue("URL", aURL) };
+
+ css::awt::Size aSize;
+ bool bOK = false;
+ try
+ {
+ xGraphic = m_xGraphProvider->queryGraphic( aMediaProps );
+
+ uno::Reference< beans::XPropertySet > props =
+ m_xGraphProvider->queryGraphicDescriptor( aMediaProps );
+ uno::Any a = props->getPropertyValue( "SizePixel" );
+ a >>= aSize;
+ if (0 == aSize.Width || 0 == aSize.Height)
+ return false;
+ else
+ bOK = true;
+ }
+ catch ( uno::Exception& )
+ {
+ return false;
+ }
+
+ bool bResult( false );
+ size_t nCount = m_xTbSymbol->GetItemCount();
+ for (size_t n = 0; n < nCount; ++n)
+ {
+ sal_uInt16 nId = m_xTbSymbol->GetItemId( n );
+
+ if ( m_xTbSymbol->GetItemText( nId ) == aURL )
+ {
+ try
+ {
+ // replace/insert image with provided URL
+ size_t nPos = nId - 1;
+ assert(nPos == m_xTbSymbol->GetItemPos(nId));
+ m_xTbSymbol->RemoveItem(nId);
+
+ Image aImage( xGraphic );
+ if ( bOK && ((aSize.Width != m_nExpectedSize) || (aSize.Height != m_nExpectedSize)) )
+ {
+ BitmapEx aBitmap = aImage.GetBitmapEx();
+ BitmapEx aBitmapex = BitmapEx::AutoScaleBitmap(aBitmap, m_nExpectedSize);
+ aImage = Image( aBitmapex);
+ }
+ m_xTbSymbol->InsertItem(nId, aImage, aURL, nPos); //modify
+
+ m_aGraphics[nPos] = Graphic(aImage.GetBitmapEx()).GetXGraphic();
+
+ m_xImportedImageManager->replaceImages( SvxConfigPageHelper::GetImageType(), { aURL }, { xGraphic } );
+ m_xImportedImageManager->store();
+
+ bResult = true;
+ break;
+ }
+ catch ( css::uno::Exception& )
+ {
+ break;
+ }
+ }
+ }
+
+ return bResult;
+}
+
+namespace
+{
+ OUString ReplaceIconName(std::u16string_view rMessage)
+ {
+ OUString name;
+ OUString message = CuiResId( RID_CUISTR_REPLACE_ICON_WARNING );
+ OUString placeholder("%ICONNAME" );
+ sal_Int32 pos = message.indexOf( placeholder );
+ if ( pos != -1 )
+ {
+ name = message.replaceAt(
+ pos, placeholder.getLength(), rMessage );
+ }
+ return name;
+ }
+
+ class SvxIconReplacementDialog
+ {
+ private:
+ std::unique_ptr<weld::MessageDialog> m_xQueryBox;
+ public:
+ SvxIconReplacementDialog(weld::Window *pParent, std::u16string_view rMessage, bool bYestoAll)
+ : m_xQueryBox(Application::CreateMessageDialog(pParent, VclMessageType::Warning, VclButtonsType::NONE, ReplaceIconName(rMessage)))
+ {
+ m_xQueryBox->set_title(CuiResId(RID_CUISTR_REPLACE_ICON_CONFIRM));
+ m_xQueryBox->add_button(GetStandardText(StandardButtonType::Yes), 2);
+ if (bYestoAll)
+ m_xQueryBox->add_button(CuiResId(RID_CUISTR_YESTOALL), 5);
+ m_xQueryBox->add_button(GetStandardText(StandardButtonType::No), 4);
+ m_xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), 6);
+ m_xQueryBox->set_default_response(2);
+ }
+ short run() { return m_xQueryBox->run(); }
+ };
+}
+
+void SvxIconSelectorDialog::ImportGraphics(
+ const uno::Sequence< OUString >& rPaths )
+{
+ std::vector< OUString > rejected( rPaths.getLength() );
+ sal_Int32 rejectedCount = 0;
+
+ sal_uInt16 ret = 0;
+ sal_Int32 aIndex;
+ OUString aIconName;
+
+ if ( rPaths.getLength() == 1 )
+ {
+ if ( m_xImportedImageManager->hasImage( SvxConfigPageHelper::GetImageType(), rPaths[0] ) )
+ {
+ aIndex = rPaths[0].lastIndexOf( '/' );
+ aIconName = rPaths[0].copy( aIndex+1 );
+ SvxIconReplacementDialog aDlg(m_xDialog.get(), aIconName, false);
+ ret = aDlg.run();
+ if ( ret == 2 )
+ {
+ ReplaceGraphicItem( rPaths[0] );
+ }
+ }
+ else
+ {
+ if ( !ImportGraphic( rPaths[0] ) )
+ {
+ rejected[0] = rPaths[0];
+ rejectedCount = 1;
+ }
+ }
+ }
+ else
+ {
+ OUString aSourcePath( rPaths[0] );
+ if ( rPaths[0].lastIndexOf( '/' ) != rPaths[0].getLength() -1 )
+ aSourcePath = rPaths[0] + "/";
+
+ for ( sal_Int32 i = 1; i < rPaths.getLength(); ++i )
+ {
+ OUString aPath = aSourcePath + rPaths[i];
+ if ( m_xImportedImageManager->hasImage( SvxConfigPageHelper::GetImageType(), aPath ) )
+ {
+ aIndex = rPaths[i].lastIndexOf( '/' );
+ aIconName = rPaths[i].copy( aIndex+1 );
+ SvxIconReplacementDialog aDlg(m_xDialog.get(), aIconName, true);
+ ret = aDlg.run();
+ if ( ret == 2 )
+ {
+ ReplaceGraphicItem( aPath );
+ }
+ else if ( ret == 5 )
+ {
+ for ( sal_Int32 k = i; k < rPaths.getLength(); ++k )
+ {
+ aPath = aSourcePath + rPaths[k];
+ bool bHasReplaced = ReplaceGraphicItem( aPath );
+
+ if ( !bHasReplaced )
+ {
+ bool result = ImportGraphic( aPath );
+ if ( !result )
+ {
+ rejected[ rejectedCount ] = rPaths[i];
+ ++rejectedCount;
+ }
+ }
+ }
+ break;
+ }
+ }
+ else
+ {
+ bool result = ImportGraphic( aSourcePath + rPaths[i] );
+ if ( !result )
+ {
+ rejected[ rejectedCount ] = rPaths[i];
+ ++rejectedCount;
+ }
+ }
+ }
+ }
+
+ if ( rejectedCount == 0 )
+ return;
+
+ OUStringBuffer message;
+ OUString fPath;
+ if (rejectedCount > 1)
+ fPath = OUString::Concat(rPaths[0].subView(8)) + "/";
+ for ( sal_Int32 i = 0; i < rejectedCount; ++i )
+ {
+ message.append(fPath + rejected[i] + "\n");
+ }
+
+ SvxIconChangeDialog aDialog(m_xDialog.get(), message.makeStringAndClear());
+ aDialog.run();
+}
+
+bool SvxIconSelectorDialog::ImportGraphic( const OUString& aURL )
+{
+ bool result = false;
+
+ uno::Sequence< beans::PropertyValue > aMediaProps{ comphelper::makePropertyValue("URL", aURL) };
+
+ try
+ {
+ uno::Reference< beans::XPropertySet > props =
+ m_xGraphProvider->queryGraphicDescriptor( aMediaProps );
+
+ uno::Any a = props->getPropertyValue("SizePixel");
+
+ uno::Reference< graphic::XGraphic > xGraphic = m_xGraphProvider->queryGraphic( aMediaProps );
+ if ( xGraphic.is() )
+ {
+ bool bOK = true;
+ css::awt::Size aSize;
+
+ a >>= aSize;
+ if ( 0 == aSize.Width || 0 == aSize.Height )
+ bOK = false;
+
+ Image aImage( xGraphic );
+
+ if ( bOK && ((aSize.Width != m_nExpectedSize) || (aSize.Height != m_nExpectedSize)) )
+ {
+ BitmapEx aBitmap = aImage.GetBitmapEx();
+ BitmapEx aBitmapex = BitmapEx::AutoScaleBitmap(aBitmap, m_nExpectedSize);
+ aImage = Image( aBitmapex);
+ }
+ if ( bOK && !!aImage )
+ {
+ m_aGraphics.push_back(Graphic(aImage.GetBitmapEx()).GetXGraphic());
+ m_xTbSymbol->InsertItem(m_aGraphics.size(), aImage, aURL);
+
+ uno::Sequence<OUString> aImportURL { aURL };
+ uno::Sequence< uno::Reference<graphic::XGraphic > > aImportGraph{ xGraphic };
+ m_xImportedImageManager->insertImages( SvxConfigPageHelper::GetImageType(), aImportURL, aImportGraph );
+ if ( m_xImportedImageManager->isModified() )
+ {
+ m_xImportedImageManager->store();
+ }
+
+ result = true;
+ }
+ else
+ {
+ SAL_WARN("cui.customize", "could not create Image from XGraphic");
+ }
+ }
+ else
+ {
+ SAL_WARN("cui.customize", "could not get query XGraphic");
+ }
+ }
+ catch( uno::Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION("cui.customize", "Caught exception importing XGraphic");
+ }
+ return result;
+}
+
+/*******************************************************************************
+*
+* The SvxIconChangeDialog class added for issue83555
+*
+*******************************************************************************/
+SvxIconChangeDialog::SvxIconChangeDialog(weld::Window *pWindow, const OUString& rMessage)
+ : MessageDialogController(pWindow, "cui/ui/iconchangedialog.ui", "IconChange", "grid")
+ , m_xLineEditDescription(m_xBuilder->weld_text_view("addrTextview"))
+{
+ m_xLineEditDescription->set_size_request(m_xLineEditDescription->get_approximate_digit_width() * 48,
+ m_xLineEditDescription->get_text_height() * 8);
+ m_xLineEditDescription->set_text(rMessage);
+}
+
+SvxConfigPageFunctionDropTarget::SvxConfigPageFunctionDropTarget(SvxConfigPage&rPage, weld::TreeView& rTreeView)
+ : weld::ReorderingDropTarget(rTreeView)
+ , m_rPage(rPage)
+{
+}
+
+sal_Int8 SvxConfigPageFunctionDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
+{
+ sal_Int8 nRet = weld::ReorderingDropTarget::ExecuteDrop(rEvt);
+ m_rPage.ListModified();
+ return nRet;;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/customize/cfgutil.cxx b/cui/source/customize/cfgutil.cxx
new file mode 100644
index 000000000..6b39170e1
--- /dev/null
+++ b/cui/source/customize/cfgutil.cxx
@@ -0,0 +1,1270 @@
+/* -*- 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 <cfgutil.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/document/XScriptInvocationContext.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/theUICommandDescription.hpp>
+#include <com/sun/star/frame/XDispatchInformationProvider.hpp>
+#include <com/sun/star/script/browse/XBrowseNode.hpp>
+#include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
+#include <com/sun/star/script/browse/theBrowseNodeFactory.hpp>
+#include <com/sun/star/script/browse/BrowseNodeFactoryViewTypes.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/ui/theUICategoryDescription.hpp>
+
+#include <basic/basmgr.hxx>
+#include <tools/urlobj.hxx>
+#include <strings.hrc>
+#include <bitmaps.hlst>
+#include <sfx2/minfitem.hxx>
+#include <comphelper/SetFlagContextHelper.hxx>
+#include <comphelper/documentinfo.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <svtools/imagemgr.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <dialmgr.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/commandevent.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/help.hxx>
+#include <vcl/svapp.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <sfx2/sidebar/ResourceManager.hxx>
+#include <sfx2/sidebar/Context.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::document;
+
+SfxStylesInfo_Impl::SfxStylesInfo_Impl()
+{}
+
+void SfxStylesInfo_Impl::init(const OUString& rModuleName, const css::uno::Reference< css::frame::XModel >& xModel)
+{
+ m_aModuleName = rModuleName;
+ m_xDoc = xModel;
+}
+
+const char CMDURL_STYLEPROT_ONLY[] = ".uno:StyleApply?";
+const char CMDURL_SPART_ONLY [] = "Style:string=";
+const char CMDURL_FPART_ONLY [] = "FamilyName:string=";
+
+constexpr OUStringLiteral STYLEPROP_UINAME = u"DisplayName";
+
+OUString SfxStylesInfo_Impl::generateCommand(
+ std::u16string_view sFamily, std::u16string_view sStyle)
+{
+ return OUString::Concat(".uno:StyleApply?Style:string=")
+ + sStyle
+ + "&FamilyName:string="
+ + sFamily;
+}
+
+bool SfxStylesInfo_Impl::parseStyleCommand(SfxStyleInfo_Impl& aStyle)
+{
+ static const sal_Int32 LEN_STYLEPROT = strlen(CMDURL_STYLEPROT_ONLY);
+ static const sal_Int32 LEN_SPART = strlen(CMDURL_SPART_ONLY);
+ static const sal_Int32 LEN_FPART = strlen(CMDURL_FPART_ONLY);
+
+ if (!aStyle.sCommand.startsWith(CMDURL_STYLEPROT_ONLY))
+ return false;
+
+ aStyle.sFamily.clear();
+ aStyle.sStyle.clear();
+
+ sal_Int32 nCmdLen = aStyle.sCommand.getLength();
+ OUString sCmdArgs = aStyle.sCommand.copy(LEN_STYLEPROT, nCmdLen-LEN_STYLEPROT);
+ sal_Int32 i = sCmdArgs.indexOf('&');
+ if (i<0)
+ return false;
+
+ OUString sArg = sCmdArgs.copy(0, i);
+ if (sArg.startsWith(CMDURL_SPART_ONLY))
+ aStyle.sStyle = sArg.copy(LEN_SPART);
+ else if (sArg.startsWith(CMDURL_FPART_ONLY))
+ aStyle.sFamily = sArg.copy(LEN_FPART);
+
+ sArg = sCmdArgs.copy(i+1, sCmdArgs.getLength()-i-1);
+ if (sArg.startsWith(CMDURL_SPART_ONLY))
+ aStyle.sStyle = sArg.copy(LEN_SPART);
+ else if (sArg.startsWith(CMDURL_FPART_ONLY))
+ aStyle.sFamily = sArg.copy(LEN_FPART);
+
+ return !(aStyle.sFamily.isEmpty() || aStyle.sStyle.isEmpty());
+}
+
+void SfxStylesInfo_Impl::getLabel4Style(SfxStyleInfo_Impl& aStyle)
+{
+ try
+ {
+ css::uno::Reference< css::style::XStyleFamiliesSupplier > xModel(m_xDoc, css::uno::UNO_QUERY);
+
+ css::uno::Reference< css::container::XNameAccess > xFamilies;
+ if (xModel.is())
+ xFamilies = xModel->getStyleFamilies();
+
+ css::uno::Reference< css::container::XNameAccess > xStyleSet;
+ if (xFamilies.is())
+ xFamilies->getByName(aStyle.sFamily) >>= xStyleSet;
+
+ css::uno::Reference< css::beans::XPropertySet > xStyle;
+ if (xStyleSet.is())
+ xStyleSet->getByName(aStyle.sStyle) >>= xStyle;
+
+ aStyle.sLabel.clear();
+ if (xStyle.is())
+ xStyle->getPropertyValue(STYLEPROP_UINAME) >>= aStyle.sLabel;
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { aStyle.sLabel.clear(); }
+
+ if (aStyle.sLabel.isEmpty())
+ {
+ aStyle.sLabel = aStyle.sCommand;
+ }
+}
+
+std::vector< SfxStyleInfo_Impl > SfxStylesInfo_Impl::getStyleFamilies() const
+{
+ // It's an optional interface!
+ css::uno::Reference< css::style::XStyleFamiliesSupplier > xModel(m_xDoc, css::uno::UNO_QUERY);
+ if (!xModel.is())
+ return std::vector< SfxStyleInfo_Impl >();
+
+ css::uno::Reference< css::container::XNameAccess > xCont = xModel->getStyleFamilies();
+ const css::uno::Sequence< OUString > lFamilyNames = xCont->getElementNames();
+ std::vector< SfxStyleInfo_Impl > lFamilies;
+ for (const auto& aFamily : lFamilyNames)
+ {
+ if ((aFamily == "CellStyles" && m_aModuleName != "com.sun.star.sheet.SpreadsheetDocument") ||
+ aFamily == "cell" || aFamily == "table" || aFamily == "Default")
+ continue;
+
+ SfxStyleInfo_Impl aFamilyInfo;
+ aFamilyInfo.sFamily = aFamily;
+
+ try
+ {
+ css::uno::Reference< css::beans::XPropertySet > xFamilyInfo;
+ xCont->getByName(aFamilyInfo.sFamily) >>= xFamilyInfo;
+ if (!xFamilyInfo.is())
+ {
+ // TODO_AS currently there is no support for an UIName property .. use internal family name instead
+ aFamilyInfo.sLabel = aFamilyInfo.sFamily;
+ }
+ else
+ xFamilyInfo->getPropertyValue(STYLEPROP_UINAME) >>= aFamilyInfo.sLabel;
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { return std::vector< SfxStyleInfo_Impl >(); }
+
+ lFamilies.push_back(aFamilyInfo);
+ }
+
+ return lFamilies;
+}
+
+std::vector< SfxStyleInfo_Impl > SfxStylesInfo_Impl::getStyles(const OUString& sFamily)
+{
+ css::uno::Sequence< OUString > lStyleNames;
+ css::uno::Reference< css::style::XStyleFamiliesSupplier > xModel(m_xDoc, css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::container::XNameAccess > xFamilies = xModel->getStyleFamilies();
+ css::uno::Reference< css::container::XNameAccess > xStyleSet;
+ try
+ {
+ xFamilies->getByName(sFamily) >>= xStyleSet;
+ lStyleNames = xStyleSet->getElementNames();
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { return std::vector< SfxStyleInfo_Impl >(); }
+
+ std::vector< SfxStyleInfo_Impl > lStyles;
+ sal_Int32 c = lStyleNames.getLength();
+ sal_Int32 i = 0;
+ for (i=0; i<c; ++i)
+ {
+ SfxStyleInfo_Impl aStyleInfo;
+ aStyleInfo.sFamily = sFamily;
+ aStyleInfo.sStyle = lStyleNames[i];
+ aStyleInfo.sCommand = SfxStylesInfo_Impl::generateCommand(aStyleInfo.sFamily, aStyleInfo.sStyle);
+
+ try
+ {
+ css::uno::Reference< css::beans::XPropertySet > xStyle;
+ xStyleSet->getByName(aStyleInfo.sStyle) >>= xStyle;
+ if (!xStyle.is())
+ continue;
+ xStyle->getPropertyValue("DisplayName") >>= aStyleInfo.sLabel;
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { continue; }
+
+ lStyles.push_back(aStyleInfo);
+ }
+ return lStyles;
+}
+
+OUString CuiConfigFunctionListBox::GetHelpText( bool bConsiderParent )
+{
+ SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(get_selected_id());
+ if (pData)
+ {
+ if ( pData->nKind == SfxCfgKind::FUNCTION_SLOT )
+ {
+ if (bConsiderParent)
+ return Application::GetHelp()->GetHelpText(pData->sCommand, m_xTreeView.get());
+ else
+ return Application::GetHelp()->GetHelpText(pData->sCommand, static_cast<weld::Widget*>(nullptr));
+ }
+ else if ( pData->nKind == SfxCfgKind::FUNCTION_SCRIPT )
+ {
+ return pData->sHelpText;
+ }
+ }
+ return OUString();
+}
+
+OUString CuiConfigFunctionListBox::GetCurCommand() const
+{
+ SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(get_selected_id());
+ if (!pData)
+ return OUString();
+ return pData->sCommand;
+}
+
+OUString CuiConfigFunctionListBox::GetCurLabel() const
+{
+ SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(get_selected_id());
+ if (!pData)
+ return OUString();
+ if (!pData->sLabel.isEmpty())
+ return pData->sLabel;
+ return pData->sCommand;
+}
+
+CuiConfigFunctionListBox::CuiConfigFunctionListBox(std::unique_ptr<weld::TreeView> xTreeView)
+ : m_xTreeView(std::move(xTreeView))
+ , m_xScratchIter(m_xTreeView->make_iterator())
+{
+ m_xTreeView->make_sorted();
+ m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 35, m_xTreeView->get_height_rows(9));
+ m_xTreeView->connect_query_tooltip(LINK(this, CuiConfigFunctionListBox, QueryTooltip));
+}
+
+CuiConfigFunctionListBox::~CuiConfigFunctionListBox()
+{
+ ClearAll();
+}
+
+IMPL_LINK(CuiConfigFunctionListBox, QueryTooltip, const weld::TreeIter&, rIter, OUString)
+{
+ SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(m_xTreeView->get_id(rIter));
+ if (!pData)
+ return OUString();
+ OUString aLabel = CuiResId(RID_CUISTR_COMMANDLABEL) + ": ";
+ OUString aName = CuiResId(RID_CUISTR_COMMANDNAME) + ": ";
+ OUString aTip = CuiResId(RID_CUISTR_COMMANDTIP) + ": ";
+ return aLabel + pData->sLabel + "\n" + aName + pData->sCommand+ "\n" + aTip + pData->sTooltip;
+}
+
+void CuiConfigFunctionListBox::ClearAll()
+/* Description
+ Deletes all entries in the FunctionListBox, all UserData and all
+ possibly existing MacroInfo.
+*/
+{
+ sal_uInt16 nCount = aArr.size();
+ for ( sal_uInt16 i=0; i<nCount; ++i )
+ {
+ SfxGroupInfo_Impl *pData = aArr[i].get();
+
+ if ( pData->nKind == SfxCfgKind::FUNCTION_SCRIPT )
+ {
+ OUString* pScriptURI = static_cast<OUString*>(pData->pObject);
+ delete pScriptURI;
+ }
+
+ if ( pData->nKind == SfxCfgKind::GROUP_SCRIPTCONTAINER )
+ {
+ XInterface* xi = static_cast<XInterface *>(pData->pObject);
+ if (xi != nullptr)
+ {
+ xi->release();
+ }
+ }
+ }
+
+ aArr.clear();
+ m_xTreeView->clear();
+}
+
+OUString CuiConfigFunctionListBox::GetSelectedScriptURI() const
+{
+ SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(get_selected_id());
+ if (pData && pData->nKind == SfxCfgKind::FUNCTION_SCRIPT)
+ return *static_cast<OUString*>(pData->pObject);
+ return OUString();
+}
+
+struct SvxConfigGroupBoxResource_Impl
+{
+ OUString m_sMyMacros;
+ OUString m_sProdMacros;
+ OUString m_sMacros;
+ OUString m_sDlgMacros;
+ OUString m_aStrGroupStyles;
+ OUString m_aStrGroupSidebarDecks;
+
+ SvxConfigGroupBoxResource_Impl();
+};
+
+SvxConfigGroupBoxResource_Impl::SvxConfigGroupBoxResource_Impl() :
+ m_sMyMacros(CuiResId(RID_CUISTR_MYMACROS)),
+ m_sProdMacros(CuiResId(RID_CUISTR_PRODMACROS)),
+ m_sMacros(CuiResId(RID_CUISTR_BASICMACROS)),
+ m_sDlgMacros(CuiResId(RID_CUISTR_PRODMACROS)),
+ m_aStrGroupStyles(CuiResId(RID_CUISTR_GROUP_STYLES)),
+ m_aStrGroupSidebarDecks(CuiResId(RID_CUISTR_GROUP_SIDEBARDECKS))
+{
+}
+
+void CuiConfigGroupListBox::SetStylesInfo(SfxStylesInfo_Impl* pStyles)
+{
+ m_pStylesInfo = pStyles;
+}
+
+namespace
+{
+
+ /** examines a component whether it supports XEmbeddedScripts, or provides access to such a
+ component by implementing XScriptInvocationContext.
+ @return
+ the model which supports the embedded scripts, or <NULL/> if it cannot find such a
+ model
+ */
+ Reference< XModel > lcl_getDocumentWithScripts_throw( const Reference< XInterface >& _rxComponent )
+ {
+ Reference< XEmbeddedScripts > xScripts( _rxComponent, UNO_QUERY );
+ if ( !xScripts.is() )
+ {
+ Reference< XScriptInvocationContext > xContext( _rxComponent, UNO_QUERY );
+ if ( xContext.is() )
+ xScripts = xContext->getScriptContainer();
+ }
+
+ return Reference< XModel >( xScripts, UNO_QUERY );
+ }
+
+
+ Reference< XModel > lcl_getScriptableDocument_nothrow( const Reference< XFrame >& _rxFrame )
+ {
+ Reference< XModel > xDocument;
+
+ // examine our associated frame
+ try
+ {
+ OSL_ENSURE( _rxFrame.is(), "lcl_getScriptableDocument_nothrow: you need to pass a frame to this dialog/tab page!" );
+ if ( _rxFrame.is() )
+ {
+ // first try the model in the frame
+ Reference< XController > xController( _rxFrame->getController(), UNO_SET_THROW );
+ xDocument = lcl_getDocumentWithScripts_throw( xController->getModel() );
+
+ if ( !xDocument.is() )
+ {
+ // if there is no suitable document in the frame, try the controller
+ xDocument = lcl_getDocumentWithScripts_throw( _rxFrame->getController() );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+
+ return xDocument;
+ }
+}
+
+CuiConfigGroupListBox::CuiConfigGroupListBox(std::unique_ptr<weld::TreeView> xTreeView)
+ : xImp(new SvxConfigGroupBoxResource_Impl())
+ , m_pFunctionListBox(nullptr)
+ , m_pStylesInfo(nullptr)
+ , m_xTreeView(std::move(xTreeView))
+ , m_xScratchIter(m_xTreeView->make_iterator())
+{
+ m_xTreeView->connect_expanding(LINK(this, CuiConfigGroupListBox, ExpandingHdl));
+ m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 35, m_xTreeView->get_height_rows(9));
+}
+
+CuiConfigGroupListBox::~CuiConfigGroupListBox()
+{
+ ClearAll();
+}
+
+void CuiConfigGroupListBox::ClearAll()
+{
+ sal_uInt16 nCount = aArr.size();
+ for ( sal_uInt16 i=0; i<nCount; ++i )
+ {
+ SfxGroupInfo_Impl *pData = aArr[i].get();
+ if (pData->nKind == SfxCfgKind::GROUP_STYLES && pData->pObject)
+ {
+ SfxStyleInfo_Impl* pStyle = static_cast<SfxStyleInfo_Impl*>(pData->pObject);
+ delete pStyle;
+ }
+ else if (pData->nKind == SfxCfgKind::FUNCTION_SCRIPT && pData->pObject )
+ {
+ OUString* pScriptURI = static_cast<OUString*>(pData->pObject);
+ delete pScriptURI;
+ }
+ else if (pData->nKind == SfxCfgKind::GROUP_SCRIPTCONTAINER)
+ {
+ XInterface* xi = static_cast<XInterface *>(pData->pObject);
+ if (xi != nullptr)
+ {
+ xi->release();
+ }
+ }
+ }
+
+ aArr.clear();
+ m_xTreeView->clear();
+}
+
+void CuiConfigGroupListBox::InitModule()
+{
+ try
+ {
+ css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider(m_xFrame, css::uno::UNO_QUERY_THROW);
+ css::uno::Sequence< sal_Int16 > lGroups = xProvider->getSupportedCommandGroups();
+ sal_Int32 c1 = lGroups.getLength();
+ sal_Int32 i1 = 0;
+
+ if ( c1 )
+ {
+ // Add All Commands category
+ aArr.push_back(std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_ALLFUNCTIONS, 0));
+ m_xTreeView->append(weld::toId(aArr.back().get()), CuiResId(RID_CUISTR_ALLFUNCTIONS));
+ }
+
+ for (i1=0; i1<c1; ++i1)
+ {
+ sal_Int16 nGroupID = lGroups[i1];
+ OUString sGroupID = OUString::number(nGroupID);
+ OUString sGroupName ;
+
+ try
+ {
+ m_xModuleCategoryInfo->getByName(sGroupID) >>= sGroupName;
+ if (sGroupName.isEmpty())
+ continue;
+ }
+ catch(const css::container::NoSuchElementException&)
+ { continue; }
+
+ aArr.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::GROUP_FUNCTION, nGroupID ) );
+ m_xTreeView->append(weld::toId(aArr.back().get()), sGroupName);
+ }
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ {}
+}
+
+void CuiConfigGroupListBox::FillScriptList(const css::uno::Reference< css::script::browse::XBrowseNode >& xRootNode,
+ const weld::TreeIter* pParentEntry)
+{
+ try {
+ if ( xRootNode->hasChildNodes() )
+ {
+ // tdf#120362: Don't ask to enable disabled Java when filling script list
+ css::uno::ContextLayer layer(comphelper::NoEnableJavaInteractionContext());
+
+ const Sequence< Reference< browse::XBrowseNode > > children =
+ xRootNode->getChildNodes();
+ bool bIsRootNode = false;
+
+ OUString user("user");
+ OUString share("share");
+ if ( xRootNode->getName() == "Root" )
+ {
+ bIsRootNode = true;
+ }
+
+ //To mimic current starbasic behaviour we
+ //need to make sure that only the current document
+ //is displayed in the config tree. Tests below
+ //set the bDisplay flag to FALSE if the current
+ //node is a first level child of the Root and is NOT
+ //either the current document, user or share
+ OUString currentDocTitle;
+ Reference< XModel > xDocument( lcl_getScriptableDocument_nothrow( m_xFrame ) );
+ if ( xDocument.is() )
+ {
+ currentDocTitle = ::comphelper::DocumentInfo::getDocumentTitle( xDocument );
+ }
+
+ for ( Reference< browse::XBrowseNode > const & theChild : children )
+ {
+ bool bDisplay = true;
+ OUString uiName = theChild->getName();
+ if ( bIsRootNode )
+ {
+ if ( ! (uiName == user || uiName == share ||
+ uiName == currentDocTitle ) )
+ {
+ bDisplay=false;
+ }
+ else
+ {
+ if ( uiName == user )
+ {
+ uiName = xImp->m_sMyMacros;
+ }
+ else if ( uiName == share )
+ {
+ uiName = xImp->m_sProdMacros;
+ }
+ }
+ }
+ if (theChild->getType() != browse::BrowseNodeTypes::SCRIPT && bDisplay )
+ {
+// We call acquire on the XBrowseNode so that it does not
+// get autodestructed and become invalid when accessed later.
+ theChild->acquire();
+
+ bool bChildOnDemand = false;
+
+ if ( theChild->hasChildNodes() )
+ {
+ const Sequence< Reference< browse::XBrowseNode > > grandchildren =
+ theChild->getChildNodes();
+
+ for ( const auto& rxNode : grandchildren )
+ {
+ if ( rxNode->getType() == browse::BrowseNodeTypes::CONTAINER )
+ {
+ bChildOnDemand = true;
+ break;
+ }
+ }
+ }
+
+ OUString aImage = GetImage(theChild, m_xContext, bIsRootNode);
+
+ aArr.push_back( std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_SCRIPTCONTAINER,
+ 0, static_cast<void *>( theChild.get())));
+
+ OUString sId(weld::toId(aArr.back().get()));
+ m_xTreeView->insert(pParentEntry, -1, &uiName, &sId, nullptr, nullptr, bChildOnDemand, m_xScratchIter.get());
+ m_xTreeView->set_image(*m_xScratchIter, aImage);
+ }
+ }
+ }
+ }
+ catch (RuntimeException&) {
+ // do nothing, the entry will not be displayed in the UI
+ }
+}
+
+void CuiConfigGroupListBox::FillFunctionsList(const css::uno::Sequence<DispatchInformation>& xCommands)
+{
+ m_pFunctionListBox->freeze();
+ for (const auto & rInfo : xCommands)
+ {
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rInfo.Command, m_sModuleLongName);
+
+ OUString sUIName = MapCommand2UIName(rInfo.Command);
+ aArr.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::FUNCTION_SLOT, 0 ) );
+ SfxGroupInfo_Impl* pGrpInfo = aArr.back().get();
+ pGrpInfo->sCommand = rInfo.Command;
+ pGrpInfo->sLabel = sUIName;
+ pGrpInfo->sTooltip = vcl::CommandInfoProvider::GetTooltipForCommand(rInfo.Command, aProperties, m_xFrame);
+ m_pFunctionListBox->append(weld::toId(pGrpInfo), sUIName);
+ }
+ m_pFunctionListBox->thaw();
+}
+
+void CuiConfigGroupListBox::Init(const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Reference< css::frame::XFrame >& xFrame,
+ const OUString& sModuleLongName,
+ bool bEventMode)
+{
+ m_xTreeView->freeze();
+ ClearAll(); // Remove all old entries from treelist box
+
+ m_xContext = xContext;
+ m_xFrame = xFrame;
+ if( bEventMode )
+ {
+ m_sModuleLongName = sModuleLongName;
+ m_xGlobalCategoryInfo = css::ui::theUICategoryDescription::get( m_xContext );
+ m_xModuleCategoryInfo.set(m_xGlobalCategoryInfo->getByName(m_sModuleLongName), css::uno::UNO_QUERY_THROW);
+ m_xUICmdDescription = css::frame::theUICommandDescription::get( m_xContext );
+
+ InitModule();
+ }
+
+ SAL_INFO("cui.customize", "** ** About to initialise SF Scripts");
+ // Add Scripting Framework entries
+ Reference< browse::XBrowseNode > rootNode;
+ try
+ {
+ Reference< browse::XBrowseNodeFactory > xFac = browse::theBrowseNodeFactory::get( m_xContext );
+ rootNode.set( xFac->createView( browse::BrowseNodeFactoryViewTypes::MACROSELECTOR ) );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("cui.customize", "Caught some exception whilst retrieving browse nodes from factory");
+ // TODO exception handling
+ }
+
+
+ if ( rootNode.is() )
+ {
+ if ( bEventMode )
+ {
+ //We call acquire on the XBrowseNode so that it does not
+ //get autodestructed and become invalid when accessed later.
+ rootNode->acquire();
+
+ aArr.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::GROUP_SCRIPTCONTAINER, 0,
+ static_cast<void *>(rootNode.get())));
+ OUString aTitle(xImp->m_sDlgMacros);
+ OUString sId(weld::toId(aArr.back().get()));
+ m_xTreeView->insert(nullptr, -1, &aTitle, &sId, nullptr, nullptr, true, nullptr);
+ }
+ else
+ {
+ //We are only showing scripts not slot APIs so skip
+ //Root node and show location nodes
+ FillScriptList(rootNode, nullptr);
+ }
+ }
+
+ // add styles and sidebar decks
+ if ( bEventMode )
+ {
+ aArr.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::GROUP_STYLES, 0, nullptr ) ); // TODO last parameter should contain user data
+ OUString sStyle(xImp->m_aStrGroupStyles);
+ OUString sId(weld::toId(aArr.back().get()));
+ m_xTreeView->insert(nullptr, -1, &sStyle, &sId, nullptr, nullptr, true, nullptr);
+
+ aArr.push_back( std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_SIDEBARDECKS, 0));
+ OUString sSidebarDecks(xImp->m_aStrGroupSidebarDecks);
+ sId = weld::toId(aArr.back().get());
+ m_xTreeView->insert(nullptr, -1, &sSidebarDecks, &sId, nullptr, nullptr, false, nullptr);
+ }
+
+ m_xTreeView->thaw();
+ m_xTreeView->scroll_to_row(0);
+ m_xTreeView->select(0);
+}
+
+OUString CuiConfigGroupListBox::GetImage(
+ const Reference< browse::XBrowseNode >& node,
+ Reference< XComponentContext > const & xCtx,
+ bool bIsRootNode)
+{
+ OUString aImage;
+ if ( bIsRootNode )
+ {
+ if (node->getName() == "user" || node->getName() == "share" )
+ {
+ aImage = RID_CUIBMP_HARDDISK;
+ }
+ else
+ {
+ OUString factoryURL;
+ OUString nodeName = node->getName();
+ Reference<XInterface> xDocumentModel = getDocumentModel(xCtx, nodeName );
+ if ( xDocumentModel.is() )
+ {
+ Reference< frame::XModuleManager2 > xModuleManager( frame::ModuleManager::create(xCtx) );
+ // get the long name of the document:
+ OUString appModule( xModuleManager->identify(
+ xDocumentModel ) );
+ Sequence<beans::PropertyValue> moduleDescr;
+ Any aAny = xModuleManager->getByName(appModule);
+ if( !( aAny >>= moduleDescr ) )
+ {
+ throw RuntimeException("SFTreeListBox::Init: failed to get PropertyValue");
+ }
+ beans::PropertyValue const * pmoduleDescr =
+ moduleDescr.getConstArray();
+ for ( sal_Int32 pos = moduleDescr.getLength(); pos--; )
+ {
+ if ( pmoduleDescr[ pos ].Name == "ooSetupFactoryEmptyDocumentURL" )
+ {
+ pmoduleDescr[ pos ].Value >>= factoryURL;
+ SAL_INFO("cui.customize", "factory url for doc images is " << factoryURL);
+ break;
+ }
+ }
+ }
+ if( !factoryURL.isEmpty() )
+ {
+ aImage = SvFileInformationManager::GetFileImageId(INetURLObject(factoryURL));
+ }
+ else
+ {
+ aImage = RID_CUIBMP_DOC;
+ }
+ }
+ }
+ else
+ {
+ if( node->getType() == browse::BrowseNodeTypes::SCRIPT )
+ aImage = RID_CUIBMP_MACRO;
+ else
+ aImage = RID_CUIBMP_LIB;
+ }
+ return aImage;
+}
+
+Reference< XInterface >
+CuiConfigGroupListBox::getDocumentModel( Reference< XComponentContext > const & xCtx, std::u16string_view docName )
+{
+ Reference< XInterface > xModel;
+ Reference< frame::XDesktop2 > desktop = frame::Desktop::create( xCtx );
+
+ Reference< container::XEnumerationAccess > componentsAccess =
+ desktop->getComponents();
+ Reference< container::XEnumeration > components =
+ componentsAccess->createEnumeration();
+ while (components->hasMoreElements())
+ {
+ Reference< frame::XModel > model(
+ components->nextElement(), UNO_QUERY );
+ if ( model.is() )
+ {
+ OUString sTdocUrl =
+ ::comphelper::DocumentInfo::getDocumentTitle( model );
+ if( sTdocUrl == docName )
+ {
+ xModel = model;
+ break;
+ }
+ }
+ }
+ return xModel;
+}
+
+OUString CuiConfigGroupListBox::MapCommand2UIName(const OUString& sCommand)
+{
+ OUString sUIName;
+ try
+ {
+ css::uno::Reference< css::container::XNameAccess > xModuleConf;
+ m_xUICmdDescription->getByName(m_sModuleLongName) >>= xModuleConf;
+ if (xModuleConf.is())
+ {
+ ::comphelper::SequenceAsHashMap lProps(xModuleConf->getByName(sCommand));
+ sUIName = lProps.getUnpackedValueOrDefault("Name", OUString());
+ }
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(css::uno::Exception&)
+ { sUIName.clear(); }
+
+ // fallback for missing UINames !?
+ if (sUIName.isEmpty())
+ {
+ sUIName = sCommand;
+ }
+
+ return sUIName;
+}
+
+void CuiConfigGroupListBox::GroupSelected()
+/* Description
+ A function group or a basic module has been selected.
+ All functions/macros are displayed in the functionlistbox.
+*/
+{
+ std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_selected(xIter.get()))
+ return;
+
+ SfxGroupInfo_Impl *pInfo = weld::fromId<SfxGroupInfo_Impl*>(m_xTreeView->get_id(*xIter));
+ m_pFunctionListBox->freeze();
+ m_pFunctionListBox->ClearAll();
+
+ switch ( pInfo->nKind )
+ {
+ case SfxCfgKind::GROUP_ALLFUNCTIONS:
+ {
+ css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider( m_xFrame, UNO_QUERY );
+ bool bValidIter = m_xTreeView->get_iter_first(*xIter);
+ while (bValidIter)
+ {
+ SfxGroupInfo_Impl *pCurrentInfo = weld::fromId<SfxGroupInfo_Impl*>(m_xTreeView->get_id(*xIter));
+ if (pCurrentInfo->nKind == SfxCfgKind::GROUP_FUNCTION)
+ {
+ css::uno::Sequence< css::frame::DispatchInformation > lCommands;
+ try
+ {
+ lCommands = xProvider->getConfigurableDispatchInformation( pCurrentInfo->nUniqueID );
+ FillFunctionsList( lCommands );
+ }
+ catch ( container::NoSuchElementException& )
+ {
+ }
+ }
+ bValidIter = m_xTreeView->iter_next(*xIter);
+ }
+ break;
+ }
+
+ case SfxCfgKind::GROUP_FUNCTION :
+ {
+ sal_uInt16 nGroup = pInfo->nUniqueID;
+ css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider (m_xFrame, css::uno::UNO_QUERY_THROW);
+ css::uno::Sequence< css::frame::DispatchInformation > lCommands = xProvider->getConfigurableDispatchInformation(nGroup);
+ FillFunctionsList( lCommands );
+ break;
+ }
+
+ case SfxCfgKind::GROUP_SCRIPTCONTAINER:
+ {
+ if (!m_xTreeView->iter_has_child(*xIter))
+ {
+ Reference< browse::XBrowseNode > rootNode(
+ static_cast< browse::XBrowseNode* >( pInfo->pObject ) ) ;
+
+ try {
+ if ( rootNode->hasChildNodes() )
+ {
+ const Sequence< Reference< browse::XBrowseNode > > children =
+ rootNode->getChildNodes();
+
+ for ( const Reference< browse::XBrowseNode >& childNode : children )
+ {
+ if (childNode->getType() == browse::BrowseNodeTypes::SCRIPT)
+ {
+ OUString uri, description;
+
+ Reference < beans::XPropertySet >xPropSet( childNode, UNO_QUERY );
+ if (!xPropSet.is())
+ {
+ continue;
+ }
+
+ Any value =
+ xPropSet->getPropertyValue("URI");
+ value >>= uri;
+
+ try
+ {
+ value = xPropSet->getPropertyValue("Description");
+ value >>= description;
+ }
+ catch (Exception &) {
+ // do nothing, the description will be empty
+ }
+
+ OUString* pScriptURI = new OUString( uri );
+
+ OUString aImage = GetImage(childNode, Reference< XComponentContext >(), false);
+ m_pFunctionListBox->aArr.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::FUNCTION_SCRIPT, 0, pScriptURI ));
+ m_pFunctionListBox->aArr.back()->sCommand = uri;
+ m_pFunctionListBox->aArr.back()->sLabel = childNode->getName();
+ m_pFunctionListBox->aArr.back()->sHelpText = description;
+
+ OUString sId(weld::toId(m_pFunctionListBox->aArr.back().get()));
+ m_pFunctionListBox->append(sId, childNode->getName(), aImage);
+ }
+ }
+ }
+ }
+ catch (RuntimeException&) {
+ // do nothing, the entry will not be displayed in the UI
+ }
+ }
+ break;
+ }
+
+ case SfxCfgKind::GROUP_STYLES :
+ {
+ SfxStyleInfo_Impl* pFamily = static_cast<SfxStyleInfo_Impl*>(pInfo->pObject);
+ if (pFamily)
+ {
+ const std::vector< SfxStyleInfo_Impl > lStyles = m_pStylesInfo->getStyles(pFamily->sFamily);
+ for (auto const& lStyle : lStyles)
+ {
+ SfxStyleInfo_Impl* pStyle = new SfxStyleInfo_Impl(lStyle);
+ m_pFunctionListBox->aArr.push_back(std::make_unique<SfxGroupInfo_Impl>(SfxCfgKind::GROUP_STYLES, 0, pStyle));
+ m_pFunctionListBox->aArr.back()->sCommand = pStyle->sCommand;
+ m_pFunctionListBox->aArr.back()->sLabel = pStyle->sLabel;
+ OUString sId(weld::toId(m_pFunctionListBox->aArr.back().get()));
+ m_pFunctionListBox->append(sId, pStyle->sLabel);
+ }
+ }
+ break;
+ }
+
+ case SfxCfgKind::GROUP_SIDEBARDECKS:
+ {
+ sfx2::sidebar::ResourceManager aResourceManager;
+ sfx2::sidebar::Context aContext(m_sModuleLongName, OUString());
+ sfx2::sidebar::ResourceManager::DeckContextDescriptorContainer aDecks;
+ aResourceManager.GetMatchingDecks(aDecks, aContext, false, m_xFrame->getController());
+
+ for (auto const& rDeck : aDecks)
+ {
+ const OUString sCommand = ".uno:SidebarDeck." + rDeck.msId;
+ m_pFunctionListBox->aArr.push_back(std::make_unique<SfxGroupInfo_Impl>(
+ SfxCfgKind::GROUP_SIDEBARDECKS, 0,
+ nullptr));
+ m_pFunctionListBox->aArr.back()->sCommand = sCommand;
+ m_pFunctionListBox->aArr.back()->sLabel = rDeck.msId;
+ m_pFunctionListBox->aArr.back()->sTooltip =
+ vcl::CommandInfoProvider::GetCommandShortcut(sCommand, m_xFrame);
+ m_pFunctionListBox->append(weld::toId(m_pFunctionListBox->aArr.back().get()),
+ rDeck.msId);
+ }
+
+ break;
+ }
+
+ default:
+ // Do nothing, the list box will stay empty
+ SAL_INFO( "cui.customize", "Ignoring unexpected SfxCfgKind: " << static_cast<int>(pInfo->nKind) );
+ break;
+ }
+
+ m_pFunctionListBox->thaw();
+
+ if (m_pFunctionListBox->n_children())
+ m_pFunctionListBox->select(0);
+}
+
+/* Description
+ A basic or a library is opened.
+*/
+IMPL_LINK(CuiConfigGroupListBox, ExpandingHdl, const weld::TreeIter&, rIter, bool)
+{
+ SfxGroupInfo_Impl *pInfo = weld::fromId<SfxGroupInfo_Impl*>(m_xTreeView->get_id(rIter));
+ switch ( pInfo->nKind )
+ {
+ case SfxCfgKind::GROUP_SCRIPTCONTAINER:
+ {
+ if (!m_xTreeView->iter_has_child(rIter))
+ {
+ Reference< browse::XBrowseNode > rootNode(
+ static_cast< browse::XBrowseNode* >( pInfo->pObject ) ) ;
+ FillScriptList(rootNode, &rIter);
+ }
+ break;
+ }
+
+ case SfxCfgKind::GROUP_STYLES:
+ {
+ if (!m_xTreeView->iter_has_child(rIter))
+ {
+ const std::vector<SfxStyleInfo_Impl> lStyleFamilies = m_pStylesInfo->getStyleFamilies();
+ for (auto const& lStyleFamily : lStyleFamilies)
+ {
+ SfxStyleInfo_Impl* pFamily = new SfxStyleInfo_Impl(lStyleFamily);
+ aArr.push_back( std::make_unique<SfxGroupInfo_Impl>( SfxCfgKind::GROUP_STYLES, 0, pFamily ));
+ OUString sId(weld::toId(aArr.back().get()));
+ m_xTreeView->insert(&rIter, -1, &pFamily->sLabel, &sId, nullptr, nullptr, false, nullptr);
+ }
+ }
+ break;
+ }
+
+ default:
+ OSL_FAIL( "Wrong group type!" );
+ break;
+ }
+ return true;
+}
+
+#if HAVE_FEATURE_SCRIPTING
+void CuiConfigGroupListBox::SelectMacro( const SfxMacroInfoItem *pItem )
+{
+ SelectMacro( pItem->GetBasicManager()->GetName(),
+ pItem->GetQualifiedName() );
+}
+
+void CuiConfigGroupListBox::SelectMacro( std::u16string_view rBasic,
+ std::u16string_view rMacro )
+{
+ const OUString aBasicName(OUString::Concat(rBasic) + " " + xImp->m_sMacros);
+ size_t nIdx {rMacro.rfind('.')};
+ const std::u16string_view aMethod( rMacro.substr(nIdx == std::u16string_view::npos ? 0 : nIdx + 1) );
+ std::u16string_view aLib;
+ std::u16string_view aModule;
+ if ( nIdx>0 && nIdx != std::u16string_view::npos )
+ {
+ // string contains at least 2 tokens
+ nIdx = rMacro.rfind('.', nIdx);
+ if (nIdx != std::u16string_view::npos)
+ {
+ // string contains at least 3 tokens
+ aLib = o3tl::getToken(rMacro, 0, '.' );
+ sal_Int32 nIdx2 = nIdx + 1;
+ aModule = o3tl::getToken(rMacro, 0, '.', nIdx2 );
+ }
+ }
+
+ std::unique_ptr<weld::TreeIter> xIter = m_xTreeView->make_iterator();
+ if (!m_xTreeView->get_iter_first(*xIter))
+ return;
+
+ do
+ {
+ OUString aEntryBas = m_xTreeView->get_text(*xIter);
+ if (aEntryBas == aBasicName)
+ {
+ m_xTreeView->expand_row(*xIter);
+ std::unique_ptr<weld::TreeIter> xLibIter = m_xTreeView->make_iterator(xIter.get());
+ if (m_xTreeView->get_iter_first(*xLibIter))
+ {
+ do
+ {
+ OUString aEntryLib = m_xTreeView->get_text(*xLibIter);
+ if (aEntryLib == aLib)
+ {
+ m_xTreeView->expand_row(*xLibIter);
+ std::unique_ptr<weld::TreeIter> xModIter = m_xTreeView->make_iterator(xLibIter.get());
+ if (m_xTreeView->get_iter_first(*xModIter))
+ {
+ do
+ {
+ OUString aEntryMod = m_xTreeView->get_text(*xModIter);
+ if ( aEntryMod == aModule )
+ {
+ m_xTreeView->expand_row(*xModIter);
+ m_xTreeView->scroll_to_row(*xModIter);
+ m_xTreeView->select(*xModIter);
+ for (int i = 0, nCount = m_pFunctionListBox->n_children(); i < nCount; ++i)
+ {
+ OUString aEntryMethod = m_pFunctionListBox->get_text(i);
+ if (aEntryMethod == aMethod)
+ {
+ m_pFunctionListBox->select(i);
+ m_pFunctionListBox->scroll_to_row(i);
+ return;
+ }
+ }
+ }
+ } while (m_xTreeView->iter_next_sibling(*xModIter));
+ }
+ }
+ } while (m_xTreeView->iter_next_sibling(*xLibIter));
+ }
+ }
+ } while (m_xTreeView->iter_next_sibling(*xIter));
+}
+#endif
+
+/*
+ * Implementation of SvxScriptSelectorDialog
+ *
+ * This dialog is used for selecting Slot API commands
+ * and Scripting Framework Scripts.
+ */
+
+SvxScriptSelectorDialog::SvxScriptSelectorDialog(
+ weld::Window* pParent, const css::uno::Reference< css::frame::XFrame >& xFrame)
+ : GenericDialogController(pParent, "cui/ui/macroselectordialog.ui", "MacroSelectorDialog")
+ , m_xDialogDescription(m_xBuilder->weld_label("helpmacro"))
+ , m_xCategories(new CuiConfigGroupListBox(m_xBuilder->weld_tree_view("categories")))
+ , m_xCommands(new CuiConfigFunctionListBox(m_xBuilder->weld_tree_view("commands")))
+ , m_xLibraryFT(m_xBuilder->weld_label("libraryft"))
+ , m_xMacronameFT(m_xBuilder->weld_label("macronameft"))
+ , m_xOKButton(m_xBuilder->weld_button("ok"))
+ , m_xCancelButton(m_xBuilder->weld_button("cancel"))
+ , m_xDescriptionText(m_xBuilder->weld_text_view("description"))
+ , m_xDescriptionFrame(m_xBuilder->weld_frame("descriptionframe"))
+{
+ m_xCancelButton->show();
+ m_xDialogDescription->show();
+ m_xOKButton->show();
+
+ m_xLibraryFT->set_visible(true);
+ m_xMacronameFT->set_visible(true);
+
+ const OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame));
+ m_xCategories->SetFunctionListBox(m_xCommands.get());
+ m_xCategories->Init(comphelper::getProcessComponentContext(), xFrame, aModuleName, /*bShowSlots*/false);
+
+ m_xCategories->connect_changed(
+ LINK( this, SvxScriptSelectorDialog, SelectHdl ) );
+ m_xCommands->connect_changed( LINK( this, SvxScriptSelectorDialog, SelectHdl ) );
+ m_xCommands->connect_row_activated( LINK( this, SvxScriptSelectorDialog, FunctionDoubleClickHdl ) );
+ m_xCommands->connect_popup_menu( LINK( this, SvxScriptSelectorDialog, ContextMenuHdl ) );
+
+ m_xOKButton->connect_clicked( LINK( this, SvxScriptSelectorDialog, ClickHdl ) );
+ m_xCancelButton->connect_clicked( LINK( this, SvxScriptSelectorDialog, ClickHdl ) );
+
+ m_sDefaultDesc = m_xDescriptionText->get_text();
+
+ // Support style commands
+ uno::Reference<frame::XController> xController;
+ uno::Reference<frame::XModel> xModel;
+ if (xFrame.is())
+ xController = xFrame->getController();
+ if (xController.is())
+ xModel = xController->getModel();
+
+ m_aStylesInfo.init(aModuleName, xModel);
+ m_xCategories->SetStylesInfo(&m_aStylesInfo);
+
+ UpdateUI();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ m_xDescriptionFrame->hide();
+}
+
+SvxScriptSelectorDialog::~SvxScriptSelectorDialog()
+{
+}
+
+IMPL_LINK(SvxScriptSelectorDialog, SelectHdl, weld::TreeView&, rCtrl, void)
+{
+ if (&rCtrl == &m_xCategories->get_widget())
+ {
+ m_xCategories->GroupSelected();
+ }
+ UpdateUI();
+}
+
+IMPL_LINK_NOARG(SvxScriptSelectorDialog, FunctionDoubleClickHdl, weld::TreeView&, bool)
+{
+ if (m_xOKButton->get_sensitive())
+ ClickHdl(*m_xOKButton);
+ return true;
+}
+
+IMPL_LINK(SvxScriptSelectorDialog, ContextMenuHdl, const CommandEvent&, rCEvt, bool)
+{
+ weld::TreeView& xTreeView = m_xCommands->get_widget();
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu || !xTreeView.n_children())
+ return false;
+
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&xTreeView, "modules/BasicIDE/ui/sortmenu.ui"));
+ std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("sortmenu"));
+ std::unique_ptr<weld::Menu> xDropMenu(xBuilder->weld_menu("sortsubmenu"));
+ xDropMenu->set_active("alphabetically", xTreeView.get_sort_order());
+ xDropMenu->set_active("properorder", !xTreeView.get_sort_order());
+
+ OString sCommand(xPopup->popup_at_rect(&xTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))));
+ if (sCommand == "alphabetically")
+ {
+ xTreeView.make_sorted();
+ }
+ else if (sCommand == "properorder")
+ {
+ xTreeView.make_unsorted();
+ m_xCategories->GroupSelected();
+ }
+ else if (!sCommand.isEmpty())
+ {
+ SAL_WARN("cui.customize", "Unknown context menu action: " << sCommand );
+ }
+
+ return true;
+}
+
+// Check if command is selected and enable the OK button accordingly
+// Grab the help text for this id if available and update the description field
+void
+SvxScriptSelectorDialog::UpdateUI()
+{
+ OUString url = GetScriptURL();
+ if ( !url.isEmpty() )
+ {
+ OUString sMessage = m_xCommands->GetHelpText();
+ m_xDescriptionText->set_text(sMessage.isEmpty() ? m_sDefaultDesc : sMessage);
+
+ m_xOKButton->set_sensitive(true);
+ }
+ else
+ {
+ m_xDescriptionText->set_text(m_sDefaultDesc);
+ m_xOKButton->set_sensitive(false);
+ }
+}
+
+IMPL_LINK(SvxScriptSelectorDialog, ClickHdl, weld::Button&, rButton, void)
+{
+ if (&rButton == m_xCancelButton.get())
+ {
+ m_xDialog->response(RET_CANCEL);
+ }
+ else if (&rButton == m_xOKButton.get())
+ {
+ m_xDialog->response(RET_OK);
+ }
+}
+
+void
+SvxScriptSelectorDialog::SetRunLabel()
+{
+ m_xOKButton->set_label(CuiResId(RID_CUISTR_SELECTOR_RUN));
+}
+
+OUString
+SvxScriptSelectorDialog::GetScriptURL() const
+{
+ OUString result;
+
+ std::unique_ptr<weld::TreeIter> xIter = m_xCommands->make_iterator();
+ if (m_xCommands->get_selected(xIter.get()))
+ {
+ SfxGroupInfo_Impl *pData = weld::fromId<SfxGroupInfo_Impl*>(m_xCommands->get_id(*xIter));
+ if ( ( pData->nKind == SfxCfgKind::FUNCTION_SLOT )
+ || ( pData->nKind == SfxCfgKind::FUNCTION_SCRIPT )
+ || ( pData->nKind == SfxCfgKind::GROUP_STYLES )
+ )
+ {
+ result = pData->sCommand;
+ }
+ }
+
+ return result;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/customize/eventdlg.cxx b/cui/source/customize/eventdlg.cxx
new file mode 100644
index 000000000..54ebcdefa
--- /dev/null
+++ b/cui/source/customize/eventdlg.cxx
@@ -0,0 +1,159 @@
+/* -*- 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 <tools/diagnose_ex.h>
+#include <com/sun/star/document/XEventsSupplier.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/documentinfo.hxx>
+#include <unotools/configmgr.hxx>
+#include <rtl/ustring.hxx>
+
+#include "eventdlg.hxx"
+#include "macropg_impl.hxx"
+
+#include <cfg.hxx>
+
+using namespace ::com::sun::star;
+
+
+SvxEventConfigPage::SvxEventConfigPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet,
+ SvxEventConfigPage::EarlyInit)
+ : SvxMacroTabPage_(pPage, pController, "cui/ui/eventsconfigpage.ui", "EventsConfigPage", rSet)
+ , m_xSaveInListBox(m_xBuilder->weld_combo_box("savein"))
+{
+ mpImpl->xEventLB = m_xBuilder->weld_tree_view("events");
+ mpImpl->xAssignPB = m_xBuilder->weld_button("macro");
+ mpImpl->xDeletePB = m_xBuilder->weld_button("delete");
+ mpImpl->xAssignComponentPB = m_xBuilder->weld_button("component");
+
+ mpImpl->xEventLB->set_size_request(mpImpl->xEventLB->get_approximate_digit_width() * 70,
+ mpImpl->xEventLB->get_height_rows(20));
+
+ InitResources();
+
+ m_xSaveInListBox->connect_changed( LINK( this, SvxEventConfigPage,
+ SelectHdl_Impl ) );
+
+ uno::Reference< frame::XGlobalEventBroadcaster > xSupplier =
+ frame::theGlobalEventBroadcaster::get(::comphelper::getProcessComponentContext());
+
+ m_xAppEvents = xSupplier->getEvents();
+ m_xSaveInListBox->append(OUString::boolean(true), utl::ConfigManager::getProductName());
+ m_xSaveInListBox->set_active(0);
+}
+
+void SvxEventConfigPage::LateInit( const uno::Reference< frame::XFrame >& _rxFrame )
+{
+ SetFrame( _rxFrame );
+ ImplInitDocument();
+
+ InitAndSetHandler( m_xAppEvents, m_xDocumentEvents, m_xDocumentModifiable );
+
+ SelectHdl_Impl( *m_xSaveInListBox );
+}
+
+SvxEventConfigPage::~SvxEventConfigPage()
+{
+}
+
+void SvxEventConfigPage::ImplInitDocument()
+{
+ uno::Reference< frame::XFrame > xFrame( GetFrame() );
+ OUString aModuleId = SvxConfigPage::GetFrameWithDefaultAndIdentify( xFrame );
+ if ( !xFrame.is() )
+ return;
+
+ try
+ {
+ uno::Reference< frame::XModel > xModel;
+ if ( !SvxConfigPage::CanConfig( aModuleId ) )
+ return;
+
+ uno::Reference< frame::XController > xController =
+ xFrame->getController();
+
+ if ( xController.is() )
+ {
+ xModel = xController->getModel();
+ }
+
+ if ( !xModel.is() )
+ return;
+
+ uno::Reference< document::XEventsSupplier > xSupplier( xModel, uno::UNO_QUERY );
+
+ if ( xSupplier.is() )
+ {
+ m_xDocumentEvents = xSupplier->getEvents();
+ m_xDocumentModifiable.set(xModel, css::uno::UNO_QUERY);
+
+ OUString aTitle = ::comphelper::DocumentInfo::getDocumentTitle( xModel );
+
+ m_xSaveInListBox->append(OUString::boolean(false), aTitle);
+ m_xSaveInListBox->set_active(m_xSaveInListBox->get_count() - 1);
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("cui.customize");
+ }
+}
+
+IMPL_LINK_NOARG( SvxEventConfigPage, SelectHdl_Impl, weld::ComboBox&, void )
+{
+ bool bApp = m_xSaveInListBox->get_active_id().toBoolean();
+
+ if (bApp)
+ {
+ SetReadOnly( false );
+ SvxMacroTabPage_::DisplayAppEvents( true );
+ }
+ else
+ {
+ bool isReadonly = false;
+
+ uno::Reference< frame::XDesktop2 > xFramesSupplier = frame::Desktop::create(
+ ::comphelper::getProcessComponentContext() );
+
+ uno::Reference< frame::XFrame > xFrame =
+ xFramesSupplier->getActiveFrame();
+
+ if ( xFrame.is() )
+ {
+ uno::Reference< frame::XController > xController =
+ xFrame->getController();
+
+ if ( xController.is() )
+ {
+ uno::Reference< frame::XStorable > xStorable(
+ xController->getModel(), uno::UNO_QUERY );
+ isReadonly = xStorable->isReadonly();
+ }
+ }
+
+ SetReadOnly( isReadonly );
+ SvxMacroTabPage_::DisplayAppEvents( false );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/customize/eventdlg.hxx b/cui/source/customize/eventdlg.hxx
new file mode 100644
index 000000000..2d368e982
--- /dev/null
+++ b/cui/source/customize/eventdlg.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <macropg.hxx>
+
+#include <com/sun/star/frame/XFrame.hpp>
+
+class SvxEventConfigPage : public SvxMacroTabPage_
+{
+ css::uno::Reference< css::container::XNameReplace > m_xAppEvents;
+ css::uno::Reference< css::container::XNameReplace > m_xDocumentEvents;
+ css::uno::Reference< css::util::XModifiable > m_xDocumentModifiable;
+
+ std::unique_ptr<weld::ComboBox> m_xSaveInListBox;
+
+ DECL_LINK( SelectHdl_Impl, weld::ComboBox&, void );
+
+ SvxEventConfigPage (const SvxEventConfigPage &) = delete;
+ SvxEventConfigPage & operator= (const SvxEventConfigPage &) = delete;
+
+public:
+
+ /// this is only to let callers know that there is a LateInit which *must* be called
+ struct EarlyInit { };
+ SvxEventConfigPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet, EarlyInit);
+ virtual ~SvxEventConfigPage() override;
+
+ void LateInit( const css::uno::Reference< css::frame::XFrame >& _rxFrame );
+
+private:
+ void ImplInitDocument();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/customize/macropg.cxx b/cui/source/customize/macropg.cxx
new file mode 100644
index 000000000..7e3ff8bc3
--- /dev/null
+++ b/cui/source/customize/macropg.cxx
@@ -0,0 +1,663 @@
+/* -*- 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 <macropg.hxx>
+#include <svl/eitem.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <bitmaps.hlst>
+#include <cfgutil.hxx>
+#include <dialmgr.hxx>
+#include <helpids.h>
+#include <headertablistbox.hxx>
+#include "macropg_impl.hxx"
+#include <o3tl/safeint.hxx>
+#include <svl/macitem.hxx>
+#include <svx/svxids.hrc>
+#include <strings.hrc>
+#include <comphelper/namedvaluecollection.hxx>
+#include <o3tl/string_view.hxx>
+#include <utility>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral aVndSunStarUNO = u"vnd.sun.star.UNO:";
+
+SvxMacroTabPage_Impl::SvxMacroTabPage_Impl( const SfxItemSet& rAttrSet )
+ : bReadOnly(false)
+ , bIDEDialogMode(false)
+{
+ const SfxPoolItem* pItem;
+ if ( SfxItemState::SET == rAttrSet.GetItemState( SID_ATTR_MACROITEM, false, &pItem ) )
+ bIDEDialogMode = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+}
+
+MacroEventListBox::MacroEventListBox(std::unique_ptr<weld::TreeView> xTreeView)
+ : m_xTreeView(std::move(xTreeView))
+{
+ m_xTreeView->set_help_id(HID_MACRO_HEADERTABLISTBOX);
+ m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 70, m_xTreeView->get_height_rows(9));
+}
+
+// assign button ("Add Command") is enabled only if it is not read only
+// delete button ("Remove Command") is enabled if a current binding exists
+// and it is not read only
+void SvxMacroTabPage_::EnableButtons()
+{
+ int nEvent = mpImpl->xEventLB->get_selected_index();
+ if (nEvent != -1)
+ {
+ mpImpl->xDeletePB->set_sensitive( !mpImpl->bReadOnly );
+ mpImpl->xAssignPB->set_sensitive( !mpImpl->bReadOnly );
+ if( mpImpl->xAssignComponentPB )
+ mpImpl->xAssignComponentPB->set_sensitive( !mpImpl->bReadOnly );
+ }
+}
+
+SvxMacroTabPage_::SvxMacroTabPage_(weld::Container* pPage, weld::DialogController* pController, const OUString& rUIXMLDescription,
+ const OString& rID, const SfxItemSet& rAttrSet)
+ : SfxTabPage(pPage, pController, rUIXMLDescription, rID, &rAttrSet)
+ , bDocModified(false)
+ , bAppEvents(false)
+ , bInitialized(false)
+{
+ mpImpl.reset( new SvxMacroTabPage_Impl( rAttrSet ) );
+}
+
+SvxMacroTabPage_::~SvxMacroTabPage_()
+{
+ mpImpl.reset();
+}
+
+void SvxMacroTabPage_::InitResources()
+{
+ // Note: the order here controls the order in which the events are displayed in the UI!
+
+ // the event name to UI string mappings for App Events
+ aDisplayNames.emplace_back( "OnStartApp", RID_CUISTR_EVENT_STARTAPP );
+ aDisplayNames.emplace_back( "OnCloseApp", RID_CUISTR_EVENT_CLOSEAPP );
+ aDisplayNames.emplace_back( "OnCreate", RID_CUISTR_EVENT_CREATEDOC );
+ aDisplayNames.emplace_back( "OnNew", RID_CUISTR_EVENT_NEWDOC );
+ aDisplayNames.emplace_back( "OnLoadFinished", RID_CUISTR_EVENT_LOADDOCFINISHED );
+ aDisplayNames.emplace_back( "OnLoad", RID_CUISTR_EVENT_OPENDOC );
+ aDisplayNames.emplace_back( "OnPrepareUnload", RID_CUISTR_EVENT_PREPARECLOSEDOC );
+ aDisplayNames.emplace_back( "OnUnload", RID_CUISTR_EVENT_CLOSEDOC ) ;
+ aDisplayNames.emplace_back( "OnViewCreated", RID_CUISTR_EVENT_VIEWCREATED );
+ aDisplayNames.emplace_back( "OnPrepareViewClosing", RID_CUISTR_EVENT_PREPARECLOSEVIEW );
+ aDisplayNames.emplace_back( "OnViewClosed", RID_CUISTR_EVENT_CLOSEVIEW ) ;
+ aDisplayNames.emplace_back( "OnFocus", RID_CUISTR_EVENT_ACTIVATEDOC );
+ aDisplayNames.emplace_back( "OnUnfocus", RID_CUISTR_EVENT_DEACTIVATEDOC );
+ aDisplayNames.emplace_back( "OnSave", RID_CUISTR_EVENT_SAVEDOC );
+ aDisplayNames.emplace_back( "OnSaveDone", RID_CUISTR_EVENT_SAVEDOCDONE );
+ aDisplayNames.emplace_back( "OnSaveFailed", RID_CUISTR_EVENT_SAVEDOCFAILED );
+ aDisplayNames.emplace_back( "OnSaveAs", RID_CUISTR_EVENT_SAVEASDOC );
+ aDisplayNames.emplace_back( "OnSaveAsDone", RID_CUISTR_EVENT_SAVEASDOCDONE );
+ aDisplayNames.emplace_back( "OnSaveAsFailed", RID_CUISTR_EVENT_SAVEASDOCFAILED );
+ aDisplayNames.emplace_back( "OnCopyTo", RID_CUISTR_EVENT_COPYTODOC );
+ aDisplayNames.emplace_back( "OnCopyToDone", RID_CUISTR_EVENT_COPYTODOCDONE );
+ aDisplayNames.emplace_back( "OnCopyToFailed", RID_CUISTR_EVENT_COPYTODOCFAILED );
+ aDisplayNames.emplace_back( "OnPrint", RID_CUISTR_EVENT_PRINTDOC );
+ aDisplayNames.emplace_back( "OnModifyChanged", RID_CUISTR_EVENT_MODIFYCHANGED );
+ aDisplayNames.emplace_back( "OnTitleChanged", RID_CUISTR_EVENT_TITLECHANGED );
+
+ // application specific events
+ aDisplayNames.emplace_back( "OnMailMerge", RID_CUISTR_EVENT_MAILMERGE );
+ aDisplayNames.emplace_back( "OnMailMergeFinished", RID_CUISTR_EVENT_MAILMERGE_END );
+ aDisplayNames.emplace_back( "OnFieldMerge", RID_CUISTR_EVENT_FIELDMERGE );
+ aDisplayNames.emplace_back( "OnFieldMergeFinished", RID_CUISTR_EVENT_FIELDMERGE_FINISHED );
+ aDisplayNames.emplace_back( "OnPageCountChange", RID_CUISTR_EVENT_PAGECOUNTCHANGE );
+ aDisplayNames.emplace_back( "OnSubComponentOpened", RID_CUISTR_EVENT_SUBCOMPONENT_OPENED );
+ aDisplayNames.emplace_back( "OnSubComponentClosed", RID_CUISTR_EVENT_SUBCOMPONENT_CLOSED );
+ aDisplayNames.emplace_back( "OnSelect", RID_CUISTR_EVENT_SELECTIONCHANGED );
+ aDisplayNames.emplace_back( "OnDoubleClick", RID_CUISTR_EVENT_DOUBLECLICK );
+ aDisplayNames.emplace_back( "OnRightClick", RID_CUISTR_EVENT_RIGHTCLICK );
+ aDisplayNames.emplace_back( "OnCalculate", RID_CUISTR_EVENT_CALCULATE );
+ aDisplayNames.emplace_back( "OnChange", RID_CUISTR_EVENT_CONTENTCHANGED );
+
+ // the event name to UI string mappings for forms & dialogs
+
+ aDisplayNames.emplace_back( "approveAction", RID_CUISTR_EVENT_APPROVEACTIONPERFORMED );
+ aDisplayNames.emplace_back( "actionPerformed", RID_CUISTR_EVENT_ACTIONPERFORMED );
+ aDisplayNames.emplace_back( "changed", RID_CUISTR_EVENT_CHANGED );
+ aDisplayNames.emplace_back( "textChanged", RID_CUISTR_EVENT_TEXTCHANGED );
+ aDisplayNames.emplace_back( "itemStateChanged", RID_CUISTR_EVENT_ITEMSTATECHANGED );
+ aDisplayNames.emplace_back( "focusGained", RID_CUISTR_EVENT_FOCUSGAINED );
+ aDisplayNames.emplace_back( "focusLost", RID_CUISTR_EVENT_FOCUSLOST );
+ aDisplayNames.emplace_back( "keyPressed", RID_CUISTR_EVENT_KEYTYPED );
+ aDisplayNames.emplace_back( "keyReleased", RID_CUISTR_EVENT_KEYUP );
+ aDisplayNames.emplace_back( "mouseEntered", RID_CUISTR_EVENT_MOUSEENTERED );
+ aDisplayNames.emplace_back( "mouseDragged", RID_CUISTR_EVENT_MOUSEDRAGGED );
+ aDisplayNames.emplace_back( "mouseMoved", RID_CUISTR_EVENT_MOUSEMOVED );
+ aDisplayNames.emplace_back( "mousePressed", RID_CUISTR_EVENT_MOUSEPRESSED );
+ aDisplayNames.emplace_back( "mouseReleased", RID_CUISTR_EVENT_MOUSERELEASED );
+ aDisplayNames.emplace_back( "mouseExited", RID_CUISTR_EVENT_MOUSEEXITED );
+ aDisplayNames.emplace_back( "approveReset", RID_CUISTR_EVENT_APPROVERESETTED );
+ aDisplayNames.emplace_back( "resetted", RID_CUISTR_EVENT_RESETTED );
+ aDisplayNames.emplace_back( "approveSubmit", RID_CUISTR_EVENT_SUBMITTED );
+ aDisplayNames.emplace_back( "approveUpdate", RID_CUISTR_EVENT_BEFOREUPDATE );
+ aDisplayNames.emplace_back( "updated", RID_CUISTR_EVENT_AFTERUPDATE );
+ aDisplayNames.emplace_back( "loaded", RID_CUISTR_EVENT_LOADED );
+ aDisplayNames.emplace_back( "reloading", RID_CUISTR_EVENT_RELOADING );
+ aDisplayNames.emplace_back( "reloaded", RID_CUISTR_EVENT_RELOADED );
+ aDisplayNames.emplace_back( "unloading", RID_CUISTR_EVENT_UNLOADING );
+ aDisplayNames.emplace_back( "unloaded", RID_CUISTR_EVENT_UNLOADED );
+ aDisplayNames.emplace_back( "confirmDelete", RID_CUISTR_EVENT_CONFIRMDELETE );
+ aDisplayNames.emplace_back( "approveRowChange", RID_CUISTR_EVENT_APPROVEROWCHANGE );
+ aDisplayNames.emplace_back( "rowChanged", RID_CUISTR_EVENT_ROWCHANGE );
+ aDisplayNames.emplace_back( "approveCursorMove", RID_CUISTR_EVENT_POSITIONING );
+ aDisplayNames.emplace_back( "cursorMoved", RID_CUISTR_EVENT_POSITIONED );
+ aDisplayNames.emplace_back( "approveParameter", RID_CUISTR_EVENT_APPROVEPARAMETER );
+ aDisplayNames.emplace_back( "errorOccured", RID_CUISTR_EVENT_ERROROCCURRED );
+ aDisplayNames.emplace_back( "adjustmentValueChanged", RID_CUISTR_EVENT_ADJUSTMENTVALUECHANGED );
+}
+
+// the following method is called when the user clicks OK
+// We use the contents of the hashes to replace the settings
+bool SvxMacroTabPage_::FillItemSet( SfxItemSet* /*rSet*/ )
+{
+ try
+ {
+ OUString eventName;
+ if( m_xAppEvents.is() )
+ {
+ for (auto const& appEvent : m_appEventsHash)
+ {
+ eventName = appEvent.first;
+ try
+ {
+ m_xAppEvents->replaceByName( eventName, GetPropsByName( eventName, m_appEventsHash ) );
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("cui.customize");
+ }
+ }
+ }
+ if( m_xDocEvents.is() && bDocModified )
+ {
+ for (auto const& docEvent : m_docEventsHash)
+ {
+ eventName = docEvent.first;
+ try
+ {
+ m_xDocEvents->replaceByName( eventName, GetPropsByName( eventName, m_docEventsHash ) );
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("cui.customize");
+ }
+ }
+ // if we have a valid XModifiable (in the case of doc events)
+ // call setModified(true)
+ // in principle this should not be necessary (see issue ??)
+ if(m_xModifiable.is())
+ {
+ m_xModifiable->setModified( true );
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+ // what is the return value about??
+ return false;
+}
+
+// the following method clears the bindings in the hashes for both doc & app
+void SvxMacroTabPage_::Reset( const SfxItemSet* )
+{
+ // called once in creation - don't reset the data this time
+ if(!bInitialized)
+ {
+ bInitialized = true;
+ return;
+ }
+
+ try
+ {
+ if( m_xAppEvents.is() )
+ {
+ for (auto & appEvent : m_appEventsHash)
+ {
+ appEvent.second.second.clear();
+ }
+ }
+ if( m_xDocEvents.is() && bDocModified )
+ {
+ for (auto & docEvent : m_docEventsHash)
+ {
+ docEvent.second.second.clear();
+ }
+ // if we have a valid XModifiable (in the case of doc events)
+ // call setModified(true)
+ if(m_xModifiable.is())
+ {
+ m_xModifiable->setModified( true );
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+ DisplayAppEvents(bAppEvents);
+}
+
+void SvxMacroTabPage_::SetReadOnly( bool bSet )
+{
+ mpImpl->bReadOnly = bSet;
+}
+
+bool SvxMacroTabPage_::IsReadOnly() const
+{
+ return mpImpl->bReadOnly;
+}
+
+namespace
+{
+ std::u16string_view GetEventDisplayText(std::u16string_view rURL)
+ {
+ if (rURL.empty())
+ return std::u16string_view();
+ bool bUNO = o3tl::starts_with(rURL, aVndSunStarUNO);
+ std::u16string_view aPureMethod;
+ if (bUNO)
+ {
+ aPureMethod = rURL.substr(aVndSunStarUNO.getLength());
+ }
+ else
+ {
+ aPureMethod = rURL.substr(strlen("vnd.sun.star.script:"));
+ aPureMethod = aPureMethod.substr( 0, aPureMethod.find( '?' ) );
+ }
+ return aPureMethod;
+ }
+
+ OUString GetEventDisplayImage(std::u16string_view rURL)
+ {
+ if (rURL.empty())
+ return OUString();
+ size_t nIndex = rURL.find(aVndSunStarUNO);
+ bool bUNO = nIndex == 0;
+ return bUNO ? OUString(RID_SVXBMP_COMPONENT) : OUString(RID_SVXBMP_MACRO);
+ }
+}
+
+// displays the app events if appEvents=true, otherwise displays the doc events
+void SvxMacroTabPage_::DisplayAppEvents( bool appEvents)
+{
+ bAppEvents = appEvents;
+
+ mpImpl->xEventLB->freeze();
+ mpImpl->xEventLB->clear();
+ EventsHash* eventsHash;
+ Reference< container::XNameReplace> nameReplace;
+ if(bAppEvents)
+ {
+ eventsHash = &m_appEventsHash;
+ nameReplace = m_xAppEvents;
+ }
+ else
+ {
+ eventsHash = &m_docEventsHash;
+ nameReplace = m_xDocEvents;
+ }
+ // have to use the original XNameReplace since the hash iterators do
+ // not guarantee the order in which the elements are returned
+ if(!nameReplace.is())
+ {
+ mpImpl->xEventLB->thaw();
+ return;
+ }
+
+ for (auto const& displayableEvent : aDisplayNames)
+ {
+ OUString sEventName( OUString::createFromAscii( displayableEvent.pAsciiEventName ) );
+ if ( !nameReplace->hasByName( sEventName ) )
+ continue;
+
+ EventsHash::iterator h_it = eventsHash->find( sEventName );
+ if( h_it == eventsHash->end() )
+ {
+ OSL_FAIL( "SvxMacroTabPage_::DisplayAppEvents: something's suspicious here!" );
+ continue;
+ }
+
+ OUString eventURL = h_it->second.second;
+ OUString displayName(CuiResId(displayableEvent.pEventResourceID));
+
+ int nRow = mpImpl->xEventLB->n_children();
+ mpImpl->xEventLB->append(sEventName, displayName);
+ mpImpl->xEventLB->set_image(nRow, GetEventDisplayImage(eventURL), 1);
+ mpImpl->xEventLB->set_text(nRow, OUString(GetEventDisplayText(eventURL)), 2);
+ }
+
+ mpImpl->xEventLB->thaw();
+
+ if (mpImpl->xEventLB->n_children())
+ {
+ mpImpl->xEventLB->select(0);
+ mpImpl->xEventLB->scroll_to_row(0);
+ }
+
+ EnableButtons();
+}
+
+// select event handler on the listbox
+IMPL_LINK_NOARG( SvxMacroTabPage_, SelectEvent_Impl, weld::TreeView&, void)
+{
+ int nEntry = mpImpl->xEventLB->get_selected_index();
+
+ if (nEntry == -1)
+ {
+ DBG_ASSERT(false, "Where does the empty entry come from?" );
+ return;
+ }
+
+ EnableButtons();
+}
+
+IMPL_LINK( SvxMacroTabPage_, AssignDeleteHdl_Impl, weld::Button&, rBtn, void )
+{
+ GenericHandler_Impl(this, &rBtn);
+}
+
+IMPL_LINK_NOARG( SvxMacroTabPage_, DoubleClickHdl_Impl, weld::TreeView&, bool)
+{
+ GenericHandler_Impl(this, nullptr);
+ return true;
+}
+
+// handler for double click on the listbox, and for the assign/delete buttons
+void SvxMacroTabPage_::GenericHandler_Impl(SvxMacroTabPage_* pThis, const weld::Button* pBtn)
+{
+ SvxMacroTabPage_Impl* pImpl = pThis->mpImpl.get();
+ weld::TreeView& rListBox = *pImpl->xEventLB;
+ int nEntry = rListBox.get_selected_index();
+ if (nEntry == -1)
+ {
+ DBG_ASSERT(false, "Where does the empty entry come from?");
+ return;
+ }
+
+ const bool bAssEnabled = pBtn != pImpl->xDeletePB.get() && pImpl->xAssignPB->get_sensitive();
+
+ OUString sEventName = rListBox.get_id(nEntry);
+
+ OUString sEventURL;
+ OUString sEventType;
+ if(pThis->bAppEvents)
+ {
+ EventsHash::iterator h_it = pThis->m_appEventsHash.find(sEventName);
+ if(h_it != pThis->m_appEventsHash.end() )
+ {
+ sEventType = h_it->second.first;
+ sEventURL = h_it->second.second;
+ }
+ }
+ else
+ {
+ EventsHash::iterator h_it = pThis->m_docEventsHash.find(sEventName);
+ if(h_it != pThis->m_docEventsHash.end() )
+ {
+ sEventType = h_it->second.first;
+ sEventURL = h_it->second.second;
+ }
+ }
+
+ bool bDoubleClick = (pBtn == nullptr);
+ bool bUNOAssigned = sEventURL.startsWith( aVndSunStarUNO );
+ if( pBtn == pImpl->xDeletePB.get() )
+ {
+ // delete pressed
+ sEventType = "Script" ;
+ sEventURL.clear();
+ if(!pThis->bAppEvents)
+ pThis->bDocModified = true;
+ }
+ else if ( ( ( pBtn != nullptr )
+ && ( pBtn == pImpl->xAssignComponentPB.get() )
+ )
+ || ( bDoubleClick
+ && bUNOAssigned
+ )
+ )
+ {
+ AssignComponentDialog aAssignDlg(pThis->GetFrameWeld(), sEventURL);
+
+ short ret = aAssignDlg.run();
+ if( ret )
+ {
+ sEventType = "UNO";
+ sEventURL = aAssignDlg.getURL();
+ if(!pThis->bAppEvents)
+ pThis->bDocModified = true;
+ }
+ }
+ else if( bAssEnabled )
+ {
+ // assign pressed
+ SvxScriptSelectorDialog aDlg(pThis->GetFrameWeld(), pThis->GetFrame());
+ short ret = aDlg.run();
+ if ( ret )
+ {
+ sEventType = "Script";
+ sEventURL = aDlg.GetScriptURL();
+ if(!pThis->bAppEvents)
+ pThis->bDocModified = true;
+ }
+ }
+
+ // update the hashes
+ if(pThis->bAppEvents)
+ {
+ EventsHash::iterator h_it = pThis->m_appEventsHash.find(sEventName);
+ h_it->second.first = sEventType;
+ h_it->second.second = sEventURL;
+ }
+ else
+ {
+ EventsHash::iterator h_it = pThis->m_docEventsHash.find(sEventName);
+ h_it->second.first = sEventType;
+ h_it->second.second = sEventURL;
+ }
+
+ rListBox.set_image(nEntry, GetEventDisplayImage(sEventURL), 1);
+ rListBox.set_text(nEntry, OUString(GetEventDisplayText(sEventURL)), 2);
+
+ rListBox.select(nEntry );
+ rListBox.scroll_to_row(nEntry);
+
+ pThis->EnableButtons();
+}
+
+// pass in the XNameReplace.
+// can remove the 3rd arg once issue ?? is fixed
+void SvxMacroTabPage_::InitAndSetHandler( const Reference< container::XNameReplace>& xAppEvents, const Reference< container::XNameReplace>& xDocEvents, const Reference< util::XModifiable >& xModifiable )
+{
+ m_xAppEvents = xAppEvents;
+ m_xDocEvents = xDocEvents;
+ m_xModifiable = xModifiable;
+ Link<weld::Button&,void> aLnk(LINK(this, SvxMacroTabPage_, AssignDeleteHdl_Impl ));
+ mpImpl->xDeletePB->connect_clicked(aLnk);
+ mpImpl->xAssignPB->connect_clicked(aLnk);
+ if( mpImpl->xAssignComponentPB )
+ mpImpl->xAssignComponentPB->connect_clicked( aLnk );
+ mpImpl->xEventLB->connect_row_activated( LINK(this, SvxMacroTabPage_, DoubleClickHdl_Impl ) );
+ mpImpl->xEventLB->connect_changed( LINK( this, SvxMacroTabPage_, SelectEvent_Impl ));
+
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(mpImpl->xEventLB->get_approximate_digit_width() * 32),
+ mpImpl->xEventLB->get_checkbox_column_width()
+ };
+ mpImpl->xEventLB->set_column_fixed_widths(aWidths);
+
+ mpImpl->xEventLB->show();
+ mpImpl->xEventLB->set_sensitive(true);
+
+ if(!m_xAppEvents.is())
+ {
+ return;
+ }
+ Sequence< OUString > eventNames = m_xAppEvents->getElementNames();
+ sal_Int32 nEventCount = eventNames.getLength();
+ for(sal_Int32 nEvent = 0; nEvent < nEventCount; ++nEvent )
+ {
+ //need exception handling here
+ try
+ {
+ m_appEventsHash[ eventNames[nEvent] ] = GetPairFromAny( m_xAppEvents->getByName( eventNames[nEvent] ) );
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+ if(!m_xDocEvents.is())
+ return;
+
+ eventNames = m_xDocEvents->getElementNames();
+ nEventCount = eventNames.getLength();
+ for(sal_Int32 nEvent = 0; nEvent < nEventCount; ++nEvent )
+ {
+ try
+ {
+ m_docEventsHash[ eventNames[nEvent] ] = GetPairFromAny( m_xDocEvents->getByName( eventNames[nEvent] ) );
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+}
+
+// returns the two props EventType & Script for a given event name
+Any SvxMacroTabPage_::GetPropsByName( const OUString& eventName, EventsHash& eventsHash )
+{
+ const std::pair< OUString, OUString >& rAssignedEvent( eventsHash[ eventName ] );
+
+ Any aReturn;
+ ::comphelper::NamedValueCollection aProps;
+ if ( !(rAssignedEvent.first.isEmpty() || rAssignedEvent.second.isEmpty()) )
+ {
+ aProps.put( "EventType", rAssignedEvent.first );
+ aProps.put( "Script", rAssignedEvent.second );
+ }
+ aReturn <<= aProps.getPropertyValues();
+
+ return aReturn;
+}
+
+// converts the Any returned by GetByName into a pair which can be stored in
+// the EventHash
+std::pair< OUString, OUString > SvxMacroTabPage_::GetPairFromAny( const Any& aAny )
+{
+ Sequence< beans::PropertyValue > props;
+ OUString type, url;
+ if( aAny >>= props )
+ {
+ ::comphelper::NamedValueCollection aProps( props );
+ type = aProps.getOrDefault( "EventType", type );
+ url = aProps.getOrDefault( "Script", url );
+ }
+ return std::make_pair( type, url );
+}
+
+SvxMacroTabPage::SvxMacroTabPage(weld::Container* pPage, weld::DialogController* pController,
+ const Reference< frame::XFrame >& _rxDocumentFrame,
+ const SfxItemSet& rSet,
+ Reference< container::XNameReplace > const & xNameReplace,
+ sal_uInt16 nSelectedIndex)
+ : SvxMacroTabPage_(pPage, pController, "cui/ui/macroassignpage.ui", "MacroAssignPage", rSet)
+{
+ mpImpl->xEventLB = m_xBuilder->weld_tree_view("assignments");
+ mpImpl->xEventLB->set_size_request(mpImpl->xEventLB->get_approximate_digit_width() * 70,
+ mpImpl->xEventLB->get_height_rows(9));
+ mpImpl->xAssignPB = m_xBuilder->weld_button("assign");
+ mpImpl->xDeletePB = m_xBuilder->weld_button("delete");
+ mpImpl->xAssignComponentPB = m_xBuilder->weld_button("component");
+
+ SetFrame( _rxDocumentFrame );
+
+ if( !mpImpl->bIDEDialogMode )
+ {
+ mpImpl->xAssignComponentPB->hide();
+ mpImpl->xAssignComponentPB->set_sensitive(false);
+ }
+
+ InitResources();
+
+ InitAndSetHandler( xNameReplace, Reference< container::XNameReplace>(nullptr), Reference< util::XModifiable >(nullptr));
+ DisplayAppEvents(true);
+ mpImpl->xEventLB->select(nSelectedIndex);
+}
+
+SvxMacroAssignDlg::SvxMacroAssignDlg(weld::Window* pParent, const Reference< frame::XFrame >& _rxDocumentFrame, const SfxItemSet& rSet,
+ const Reference< container::XNameReplace >& xNameReplace, sal_uInt16 nSelectedIndex)
+ : SvxMacroAssignSingleTabDialog(pParent, rSet)
+{
+ SetTabPage(std::make_unique<SvxMacroTabPage>(get_content_area(), this, _rxDocumentFrame, rSet, xNameReplace, nSelectedIndex));
+}
+
+IMPL_LINK_NOARG(AssignComponentDialog, ButtonHandler, weld::Button&, void)
+{
+ OUString aMethodName = mxMethodEdit->get_text();
+ maURL.clear();
+ if( !aMethodName.isEmpty() )
+ {
+ maURL = aVndSunStarUNO;
+ maURL += aMethodName;
+ }
+ m_xDialog->response(RET_OK);
+}
+
+AssignComponentDialog::AssignComponentDialog(weld::Window* pParent, OUString aURL)
+ : GenericDialogController(pParent, "cui/ui/assigncomponentdialog.ui", "AssignComponent")
+ , maURL(std::move( aURL ))
+ , mxMethodEdit(m_xBuilder->weld_entry("methodEntry"))
+ , mxOKButton(m_xBuilder->weld_button("ok"))
+{
+ mxOKButton->connect_clicked(LINK(this, AssignComponentDialog, ButtonHandler));
+
+ OUString aMethodName;
+ if( maURL.startsWith( aVndSunStarUNO ) )
+ {
+ aMethodName = maURL.copy( aVndSunStarUNO.getLength() );
+ }
+ mxMethodEdit->set_text(aMethodName);
+ mxMethodEdit->select_region(0, -1);
+}
+
+AssignComponentDialog::~AssignComponentDialog()
+{
+}
+
+IMPL_LINK_NOARG(SvxMacroAssignSingleTabDialog, OKHdl_Impl, weld::Button&, void)
+{
+ m_xSfxPage->FillItemSet(nullptr);
+ m_xDialog->response(RET_OK);
+}
+
+SvxMacroAssignSingleTabDialog::SvxMacroAssignSingleTabDialog(weld::Window *pParent,
+ const SfxItemSet& rSet)
+ : SfxSingleTabDialogController(pParent, &rSet, "cui/ui/macroassigndialog.ui", "MacroAssignDialog")
+{
+ GetOKButton().connect_clicked(LINK(this, SvxMacroAssignSingleTabDialog, OKHdl_Impl));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/customize/macropg_impl.hxx b/cui/source/customize/macropg_impl.hxx
new file mode 100644
index 000000000..63d2f2ae2
--- /dev/null
+++ b/cui/source/customize/macropg_impl.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svl/itemset.hxx>
+#include <vcl/weld.hxx>
+
+class SvxMacroTabPage_Impl
+{
+public:
+ explicit SvxMacroTabPage_Impl( const SfxItemSet& rAttrSet );
+
+ std::unique_ptr<weld::Button> xAssignPB;
+ std::unique_ptr<weld::Button> xAssignComponentPB;
+ std::unique_ptr<weld::Button> xDeletePB;
+ std::unique_ptr<weld::TreeView> xEventLB;
+ bool bReadOnly;
+ bool bIDEDialogMode;
+};
+
+class AssignComponentDialog : public weld::GenericDialogController
+{
+private:
+ OUString maURL;
+
+ std::unique_ptr<weld::Entry> mxMethodEdit;
+ std::unique_ptr<weld::Button> mxOKButton;
+
+ DECL_LINK(ButtonHandler, weld::Button&, void);
+
+public:
+ AssignComponentDialog(weld::Window* pParent, OUString aURL);
+ virtual ~AssignComponentDialog() override;
+
+ const OUString& getURL() const { return maURL; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */