diff options
Diffstat (limited to 'cui/source/customize/cfg.cxx')
-rw-r--r-- | cui/source/customize/cfg.cxx | 3249 |
1 files changed, 3249 insertions, 0 deletions
diff --git a/cui/source/customize/cfg.cxx b/cui/source/customize/cfg.cxx new file mode 100644 index 0000000000..d41012850b --- /dev/null +++ b/cui/source/customize/cfg.cxx @@ -0,0 +1,3249 @@ +/* -*- 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/minfitem.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 <comphelper/diagnose_ex.hxx> +#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> +#include <config_features.h> + +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); + + if (const SfxPoolItem* pItem = pInSet->GetItem(SID_CONFIG)) + { + OUString text = static_cast<const SfxStringItem*>(pItem)->GetValue(); + if (text.startsWith( ITEM_TOOLBAR_URL ) ) + SetCurPageId("toolbars"); + else if (text.startsWith( ITEM_EVENT_URL) ) + SetCurPageId("events"); + } +#if HAVE_FEATURE_SCRIPTING + else if (pInSet->GetItemIfSet(SID_MACROINFO)) + { + // for the "assign" button in the Basic Macros chooser automatically switch + // to the keyboard tab in which this macro will be pre-selected for assigning + // to a keystroke + SetCurPageId("keyboard"); + } +#endif +} + +void SvxConfigDialog::ActivatePage(const OUString& rPage) +{ + SfxTabDialogController::ActivatePage(rPage); + 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 OUString &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)); + m_xControl->connect_query_tooltip(LINK(this, SvxMenuEntriesListBox, QueryTooltip)); +} + +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; +} + +IMPL_LINK(SvxMenuEntriesListBox, QueryTooltip, const weld::TreeIter&, rIter, OUString) +{ + SvxConfigEntry *pEntry = weld::fromId<SvxConfigEntry*>(m_xControl->get_id(rIter)); + if (!pEntry || pEntry->GetCommand().isEmpty()) + return OUString(); + const OUString sCommand(pEntry->GetCommand()); + OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(m_pPage->GetFrame())); + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(sCommand, aModuleName); + OUString sTooltipLabel = vcl::CommandInfoProvider::GetTooltipForCommand(sCommand, aProperties, + m_pPage->GetFrame()); + return CuiResId(RID_CUISTR_COMMANDLABEL) + ": " + pEntry->GetName().replaceFirst("~", "") + "\n" + + CuiResId(RID_CUISTR_COMMANDNAME) + ": " + sCommand + "\n" + + CuiResId(RID_CUISTR_COMMANDTIP) + ": " + sTooltipLabel.replaceFirst("~", ""); +} + +/****************************************************************************** + * + * 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_xCustomizeBox(m_xBuilder->weld_widget("customizebox")) + , m_xInsertBtn(m_xBuilder->weld_menu_button("insert")) + , m_xModifyBtn(m_xBuilder->weld_menu_button("modify")) + , m_xResetBtn(m_xBuilder->weld_button("defaultsbtn")) + , m_xCommandButtons(m_xBuilder->weld_widget("arrowgrid")) + , 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()) + { + if (SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + _inout_rxFrame = pViewFrame->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_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: */ |