cui/qa/uitest/dialogs/accelerators.py | 184 ++++++++++++++++++ cui/source/customize/acccfg.cxx | 237 ++++++++++++++++++++++- cui/source/inc/acccfg.hxx | 15 + cui/uiconfig/ui/accelconfigpage.ui | 345 +++++++++++++++++++--------------- 4 files changed, 630 insertions(+), 151 deletions(-)
New commits: commit e4e601b1eb8ec8d5b5783743606e4bc79ba859de Author: Neil Roberts <[email protected]> AuthorDate: Tue Jan 20 11:47:41 2026 +0100 Commit: Neil Roberts <[email protected]> CommitDate: Sat Feb 28 10:49:02 2026 +0100 tdf#94522: accelconfigpage: Allow defining shortcuts in document Previously, when assigning a shortcut key, there was only a choice between assigning it in the current module or globally for all of LibreOffice. This patch adds a third radio button with a dropdown box to be able to pick a document to define the key in instead. This functionality is already available in the toolbar and menu pages. Change-Id: I465dbb84428268f621174d8e52662175a72f44e5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197709 Reviewed-by: Heiko Tietze <[email protected]> Tested-by: Jenkins diff --git a/cui/qa/uitest/dialogs/accelerators.py b/cui/qa/uitest/dialogs/accelerators.py new file mode 100644 index 000000000000..34722ffc02a5 --- /dev/null +++ b/cui/qa/uitest/dialogs/accelerators.py @@ -0,0 +1,184 @@ +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# 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/. +# + +from uitest.framework import UITestCase +from uitest.uihelper.common import select_pos, select_by_text +from libreoffice.uno.propertyvalue import mkPropertyValues +from uitest.uihelper.common import get_state_as_dict +from com.sun.star.awt import KeyEvent +from com.sun.star.awt import Key +from com.sun.star.container import NoSuchElementException + +def select_key(xShortcuts, key_name): + for child in xShortcuts.getChildren(): + entry = xShortcuts.getChild(child) + name = get_state_as_dict(entry)["Text"] + + if name.startswith(key_name): + entry.executeAction("SELECT", tuple()) + break + else: + raise Exception(f"Couldn’t find key {key_name}") + +def select_keyboard_tab(xDialog): + xTabs = xDialog.getChild("tabcontrol") + + n_tabs = int(get_state_as_dict(xTabs)["PageCount"]) + + for i in range(n_tabs): + select_pos(xTabs, str(i)) + + if get_state_as_dict(xTabs)["CurrPageTitle"] == "Keyboard": + break + else: + raise Exception("Couldn’t find keyboard tab") + +class Test(UITestCase): + def assign_key(self, xDialog, scope, key_name, action): + xAcceleratorPage = xDialog.getChild("AccelConfigPage") + + select_keyboard_tab(xDialog) + + if scope == "office": + xAcceleratorPage.getChild("office").executeAction("CLICK", tuple()) + elif scope == "module": + xAcceleratorPage.getChild("module").executeAction("CLICK", tuple()) + else: + xAcceleratorPage.getChild("document").executeAction("CLICK", tuple()) + xScope = xAcceleratorPage.getChild("savein") + select_by_text(xScope, scope) + + xShortcuts = xAcceleratorPage.getChild("shortcuts") + select_key(xShortcuts, key_name) + + xSearchEntry = xAcceleratorPage.getChild("searchEntry") + xSearchEntry.executeAction("FOCUS", tuple()) + xSearchEntry.executeAction("SET", mkPropertyValues({"TEXT": action})) + + # Focus something else so that the search will be applied + # immediately without having to wait for the timer + xShortcuts.executeAction("FOCUS", tuple()) + + xChange = xAcceleratorPage.getChild("change") + xChange.executeAction("CLICK", tuple()) + + def test_scope(self): + with self.ui_test.create_doc_in_start_center("writer") as xComponent: + # Set a shortcut on the global scope + with self.ui_test.execute_dialog_through_command(".uno:ConfigureDialog") as xDialog: + self.assign_key(xDialog, "office", "F7", ".uno:Credits") + + # Check that the key made it into the global config + xGlobalAccelCfg = self.xContext.ServiceManager.createInstance( + 'com.sun.star.ui.GlobalAcceleratorConfiguration') + xKeyEvent = KeyEvent() + xKeyEvent.KeyCode = Key.F7 + self.assertEqual(xGlobalAccelCfg.getCommandByKeyEvent(xKeyEvent), ".uno:Credits") + + # Set a shortcut on the module scope + with self.ui_test.execute_dialog_through_command(".uno:ConfigureDialog") as xDialog: + self.assign_key(xDialog, "module", "F8", ".uno:EditBookmark") + + # Check that the key made it into the module config + xModuleAccelCfg = self.xContext.ServiceManager.createInstanceWithArguments( + 'com.sun.star.ui.ModuleAcceleratorConfiguration', + ('com.sun.star.text.TextDocument',)) + xKeyEvent = KeyEvent() + xKeyEvent.KeyCode = Key.F8 + self.assertEqual(xModuleAccelCfg.getCommandByKeyEvent(xKeyEvent), ".uno:EditBookmark") + + # Set a shortcut on the document scope + with self.ui_test.execute_dialog_through_command(".uno:ConfigureDialog") as xDialog: + self.assign_key(xDialog, "Untitled 1", "F9", ".uno:OptionsSecurityDialog") + + # Check that the key made it into the document config + xDocAccelCfg = xComponent.getUIConfigurationManager().getShortCutManager() + xKeyEvent = KeyEvent() + xKeyEvent.KeyCode = Key.F9 + self.assertEqual(xDocAccelCfg.getCommandByKeyEvent(xKeyEvent), + ".uno:OptionsSecurityDialog") + + def test_scope_multi_doc(self): + # Create two writer documents and a calc document. Only the + # writer documents should appear in the scope combobox + with self.ui_test.create_doc_in_start_center("writer"), \ + self.ui_test.load_empty_file("calc"), \ + self.ui_test.load_empty_file("writer"), \ + self.ui_test.execute_dialog_through_command(".uno:ConfigureDialog") as xDialog: + xAcceleratorPage = xDialog.getChild("AccelConfigPage") + + select_keyboard_tab(xDialog) + xAcceleratorPage.getChild("document").executeAction("CLICK", tuple()) + + xScope = xAcceleratorPage.getChild("savein") + + self.assertEqual(get_state_as_dict(xScope)["EntryCount"], "2") + + # The current document should be listed first + select_pos(xScope, "0") + self.assertEqual(get_state_as_dict(xScope)["SelectEntryText"], "Untitled 3") + + select_pos(xScope, "1") + self.assertEqual(get_state_as_dict(xScope)["SelectEntryText"], "Untitled 1") + + def test_deleted_doc(self): + # Create two writer docs then try to assign a key in the first + # one but close the document before pressing ok. + with self.ui_test.create_doc_in_start_center("writer") as xDoc1, \ + self.ui_test.load_empty_file("writer"), \ + self.ui_test.execute_dialog_through_command(".uno:ConfigureDialog") as xDialog: + self.assign_key(xDialog, "Untitled 1", "F7", ".uno:Credits") + xFrame1 = xDoc1.getCurrentController().getFrame() + self.xUITest.executeCommandForProvider(".uno:CloseDoc", xFrame1) + + xAcceleratorPage = xDialog.getChild("AccelConfigPage") + xScope = xAcceleratorPage.getChild("savein") + self.assertEqual(get_state_as_dict(xScope)["SelectEntryText"], "Untitled 2") + + def test_reset_doc(self): + with self.ui_test.create_doc_in_start_center("writer") as xComponent: + with self.ui_test.execute_dialog_through_command(".uno:ConfigureDialog") as xDialog: + # Assign a key + self.assign_key(xDialog, "Untitled 1", "F7", ".uno:Credits") + # … but then press reset before pressing OK + xReset = xDialog.getChild("AccelConfigPage").getChild("reset") + xReset.executeAction("CLICK", tuple()) + + # Make sure the key isn’t assigned + xDocAccelCfg = xComponent.getUIConfigurationManager().getShortCutManager() + xKeyEvent = KeyEvent() + xKeyEvent.KeyCode = Key.F7 + with self.assertRaises(NoSuchElementException): + xDocAccelCfg.getCommandByKeyEvent(xKeyEvent) + + def test_savein_enabled(self): + with self.ui_test.create_doc_in_start_center("writer") as xComponent: + with self.ui_test.execute_dialog_through_command(".uno:ConfigureDialog") as xDialog: + select_keyboard_tab(xDialog) + + xAcceleratorPage = xDialog.getChild("AccelConfigPage") + xScope = xAcceleratorPage.getChild("savein") + + # The combobox should be initially disabled + self.assertEqual(get_state_as_dict(xScope)["Enabled"], "false") + + # Check that selecting any of the other radio buttons makes the save-in combobox be + # disabled + xAcceleratorPage.getChild("office").executeAction("CLICK", tuple()) + self.assertEqual(get_state_as_dict(xScope)["Enabled"], "false") + xAcceleratorPage.getChild("module").executeAction("CLICK", tuple()) + self.assertEqual(get_state_as_dict(xScope)["Enabled"], "false") + + # It should be enabled when the document radio button is selected + xAcceleratorPage.getChild("document").executeAction("CLICK", tuple()) + self.assertEqual(get_state_as_dict(xScope)["Enabled"], "true") + + # … and disabled again if something else is selected + xAcceleratorPage.getChild("office").executeAction("CLICK", tuple()) + self.assertEqual(get_state_as_dict(xScope)["Enabled"], "false") + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/cui/source/customize/acccfg.cxx b/cui/source/customize/acccfg.cxx index 8d758b09a1cb..e29b6e9fb253 100644 --- a/cui/source/customize/acccfg.cxx +++ b/cui/source/customize/acccfg.cxx @@ -22,6 +22,7 @@ #include <acccfg.hxx> #include <cfgutil.hxx> #include <dialmgr.hxx> +#include <cfg.hxx> #include <sfx2/filedlghelper.hxx> #include <sfx2/minfitem.hxx> @@ -44,10 +45,13 @@ #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/frame/FrameSearchFlag.hpp> +#include <com/sun/star/frame/UnknownModuleException.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/XUIConfigurationManagerSupplier.hpp> #include <com/sun/star/ui/dialogs/TemplateDescription.hpp> // include search util @@ -56,12 +60,15 @@ #include <unotools/textsearch.hxx> // include other projects +#include <comphelper/documentinfo.hxx> #include <comphelper/processfactory.hxx> #include <svtools/acceleratorexecute.hxx> #include <vcl/svapp.hxx> #include <vcl/weld/Builder.hxx> #include <comphelper/sequenceashashmap.hxx> #include <config_features.h> +#include <unotools/configmgr.hxx> +#include <cppuhelper/implbase.hxx> #include <com/sun/star/frame/XLayoutManager.hpp> @@ -820,6 +827,81 @@ const sal_uInt16 KEYCODE_ARRAY[] = { KEY_F1, const sal_uInt16 KEYCODE_ARRAY_SIZE = std::size(KEYCODE_ARRAY); +namespace +{ +struct AcceleratorSaveInData +{ + AcceleratorSaveInData(const uno::Reference<frame::XModel>& xModel, + const uno::Reference<ui::XAcceleratorConfiguration>& xAccMgr) + : m_xModel(xModel) + , m_xAccMgr(xAccMgr) + { + } + + uno::Reference<frame::XModel> m_xModel; + uno::Reference<ui::XAcceleratorConfiguration> m_xAccMgr; +}; +} + +// Helper class to listen for components being disposed so we can +// remove them from the SaveIn combobox +class ComponentDisposedListener : public ::cppu::WeakImplHelper<lang::XEventListener> +{ +public: + ComponentDisposedListener(SfxAcceleratorConfigPage* pAccelCfgPage) + : m_pAccelCfgPage(pAccelCfgPage) + { + } + + void SAL_CALL disposing(const lang::EventObject& rEvent) override; + +private: + SfxAcceleratorConfigPage* m_pAccelCfgPage; + friend class SfxAcceleratorConfigPage; +}; + +void ComponentDisposedListener::disposing(const lang::EventObject& rEvent) +{ + SolarMutexGuard aGuard; + + if (!m_pAccelCfgPage) + return; + + int cnt = m_pAccelCfgPage->m_xSaveInListBox->get_count(); + + for (int i = 0; i < cnt; ++i) + { + AcceleratorSaveInData* pData + = weld::fromId<AcceleratorSaveInData*>(m_pAccelCfgPage->m_xSaveInListBox->get_id(i)); + + if (pData->m_xModel == rEvent.Source) + { + int nOldActive = m_pAccelCfgPage->m_xSaveInListBox->get_active(); + + m_pAccelCfgPage->m_xSaveInListBox->remove(i); + + if (m_pAccelCfgPage->m_xDocumentButton->get_active() && nOldActive == i) + { + m_pAccelCfgPage->m_xModuleButton->set_active(true); + m_pAccelCfgPage->m_xSaveInListBox->set_active(0); + m_pAccelCfgPage->HandleScopeChanged(); + } + + if (m_pAccelCfgPage->m_xSaveInListBox->get_count() <= 0) + { + m_pAccelCfgPage->m_xSaveInListBox->hide(); + m_pAccelCfgPage->m_xDocumentButton->hide(); + } + + pData->m_xModel->removeEventListener(this); + + delete pData; + + break; + } + } +} + /** select the entry, which match the current key input ... excepting keys, which are used for the dialog itself. */ @@ -873,6 +955,7 @@ SfxAcceleratorConfigPage::SfxAcceleratorConfigPage(weld::Container* pPage, , m_xEntriesBox(m_xBuilder->weld_tree_view(u"shortcuts"_ustr)) , m_xOfficeButton(m_xBuilder->weld_radio_button(u"office"_ustr)) , m_xModuleButton(m_xBuilder->weld_radio_button(u"module"_ustr)) + , m_xDocumentButton(m_xBuilder->weld_radio_button(u"document"_ustr)) , m_xChangeButton(m_xBuilder->weld_button(u"change"_ustr)) , m_xRemoveButton(m_xBuilder->weld_button(u"delete"_ustr)) , m_xGroupLBox(new CuiConfigGroupListBox(m_xBuilder->weld_tree_view(u"category"_ustr))) @@ -882,6 +965,8 @@ SfxAcceleratorConfigPage::SfxAcceleratorConfigPage(weld::Container* pPage, , m_xLoadButton(m_xBuilder->weld_button(u"load"_ustr)) , m_xSaveButton(m_xBuilder->weld_button(u"save"_ustr)) , m_xResetButton(m_xBuilder->weld_button(u"reset"_ustr)) + , m_xSaveInListBox(m_xBuilder->weld_combo_box(u"savein"_ustr)) + , m_xComponentDisposedListener(new ComponentDisposedListener(this)) { Size aSize(m_xEntriesBox->get_approximate_digit_width() * 40, m_xEntriesBox->get_height_rows(10)); @@ -908,6 +993,8 @@ SfxAcceleratorConfigPage::SfxAcceleratorConfigPage(weld::Container* pPage, 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_xDocumentButton->connect_toggled(LINK(this, SfxAcceleratorConfigPage, DocumentRadioHdl)); + m_xSaveInListBox->connect_changed(LINK(this, SfxAcceleratorConfigPage, SelectSaveInLocation)); m_xSearchEdit->connect_changed(LINK(this, SfxAcceleratorConfigPage, SearchUpdateHdl)); m_xSearchEdit->connect_focus_out(LINK(this, SfxAcceleratorConfigPage, FocusOut_Impl)); @@ -949,6 +1036,11 @@ SfxAcceleratorConfigPage::~SfxAcceleratorConfigPage() TAccInfo* pUserData = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(i)); delete pUserData; } + + // Free the dynamic user data for the SaveIn combobox + ClearSaveInComboBox(); + + m_xComponentDisposedListener->m_pAccelCfgPage = nullptr; } void SfxAcceleratorConfigPage::InitAccCfg() @@ -1151,9 +1243,13 @@ IMPL_LINK_NOARG(SfxAcceleratorConfigPage, Save, weld::Button&, void) IMPL_LINK_NOARG(SfxAcceleratorConfigPage, Default, weld::Button&, void) { - uno::Reference<form::XReset> xReset(m_xAct, uno::UNO_QUERY); - if (xReset.is()) - xReset->reset(); + // XReset doesn’t work on document-based configs + if (!m_xDocumentButton->get_active()) + { + uno::Reference<form::XReset> xReset(m_xAct, uno::UNO_QUERY); + if (xReset.is()) + xReset->reset(); + } m_xEntriesBox->freeze(); ResetConfig(); @@ -1288,6 +1384,23 @@ IMPL_LINK(SfxAcceleratorConfigPage, SelectHdl, weld::TreeView&, rListBox, void) } IMPL_LINK_NOARG(SfxAcceleratorConfigPage, RadioHdl, weld::Toggleable&, void) +{ + HandleScopeChanged(); +} + +IMPL_LINK_NOARG(SfxAcceleratorConfigPage, DocumentRadioHdl, weld::Toggleable&, void) +{ + m_xSaveInListBox->set_sensitive(m_xDocumentButton->get_active()); + HandleScopeChanged(); +} + +IMPL_LINK_NOARG(SfxAcceleratorConfigPage, SelectSaveInLocation, weld::ComboBox&, void) +{ + m_xDocumentButton->set_active(true); + HandleScopeChanged(); +} + +void SfxAcceleratorConfigPage::HandleScopeChanged() { uno::Reference<ui::XAcceleratorConfiguration> xOld = m_xAct; @@ -1295,6 +1408,11 @@ IMPL_LINK_NOARG(SfxAcceleratorConfigPage, RadioHdl, weld::Toggleable&, void) m_xAct = m_xGlobal; else if (m_xModuleButton->get_active()) m_xAct = m_xModule; + else if (m_xDocumentButton->get_active()) + { + OUString sId = m_xSaveInListBox->get_active_id(); + m_xAct = weld::fromId<AcceleratorSaveInData*>(sId)->m_xAccMgr; + } // nothing changed? => do nothing! if (m_xAct.is() && (xOld == m_xAct)) @@ -1514,6 +1632,107 @@ bool SfxAcceleratorConfigPage::FillItemSet(SfxItemSet*) return true; } +void SfxAcceleratorConfigPage::ClearSaveInComboBox() +{ + int cnt = m_xSaveInListBox->get_count(); + + for (int i = 0; i < cnt; ++i) + { + AcceleratorSaveInData* pData + = weld::fromId<AcceleratorSaveInData*>(m_xSaveInListBox->get_id(i)); + + pData->m_xModel->removeEventListener(m_xComponentDisposedListener); + + delete pData; + } + + m_xSaveInListBox->clear(); +} + +void SfxAcceleratorConfigPage::AddFrameToSaveInComboBox(const uno::Reference<frame::XFrame>& xFrame) +{ + uno::Reference<frame::XController> xController = xFrame->getController(); + + if (!xController.is()) + return; + + uno::Reference<frame::XModel> xModel = xController->getModel(); + + if (!xModel.is()) + return; + + uno::Reference<css::ui::XUIConfigurationManagerSupplier> xCfgSupplier(xModel, uno::UNO_QUERY); + + if (!xCfgSupplier.is()) + return; + + uno::Reference<css::ui::XUIConfigurationManager> xDocCfgMgr + = xCfgSupplier->getUIConfigurationManager(); + + if (!xCfgSupplier) + return; + + uno::Reference<css::ui::XAcceleratorConfiguration> xAccMgr = xDocCfgMgr->getShortCutManager(); + + if (!xAccMgr) + return; + + AcceleratorSaveInData* pDocData = new AcceleratorSaveInData(xModel, xAccMgr); + OUString aTitle = ::comphelper::DocumentInfo::getDocumentTitle(xModel); + m_xSaveInListBox->append(weld::toId(pDocData), aTitle); + + xModel->addEventListener(m_xComponentDisposedListener); +} + +void SfxAcceleratorConfigPage::FillSaveInComboBox() +{ + ClearSaveInComboBox(); + + if (!m_xModule.is() || !SvxConfigPage::CanConfig(m_sModuleLongName)) + return; + + // Add the current frame first + AddFrameToSaveInComboBox(m_xFrame); + + // Add any other frames with the same module + uno::Reference<frame::XModuleManager2> xModuleManager + = frame::ModuleManager::create(m_xContext); + + uno::Reference<frame::XDesktop2> xFramesSupplier = frame::Desktop::create(m_xContext); + + uno::Reference<frame::XFrames> xFrames = xFramesSupplier->getFrames(); + + uno::Sequence<uno::Reference<frame::XFrame>> aFrameList + = xFrames->queryFrames(frame::FrameSearchFlag::ALL); + + for (uno::Reference<frame::XFrame> const& xFrame : aFrameList) + { + if (!xFrame.is() || xFrame == m_xFrame) + continue; + + OUString sFrameModule; + + try + { + sFrameModule = xModuleManager->identify(xFrame); + } + catch (css::lang::IllegalArgumentException&) + { + continue; + } + catch (css::frame::UnknownModuleException&) + { + continue; + } + + if (m_sModuleLongName == sFrameModule) + AddFrameToSaveInComboBox(xFrame); + } + + if (m_xSaveInListBox->get_count() >= 1) + m_xSaveInListBox->set_active(0); +} + void SfxAcceleratorConfigPage::Reset(const SfxItemSet* rSet) { // open accelerator configs @@ -1536,7 +1755,17 @@ void SfxAcceleratorConfigPage::Reset(const SfxItemSet* rSet) m_xOfficeButton->set_active(true); } - RadioHdl(*m_xOfficeButton); + FillSaveInComboBox(); + + if (m_xSaveInListBox->get_count() <= 0) + { + m_xDocumentButton->hide(); + m_xSaveInListBox->hide(); + } + else + m_xSaveInListBox->set_sensitive(false); + + HandleScopeChanged(); #if HAVE_FEATURE_SCRIPTING if (const SfxMacroInfoItem* pMacroItem = rSet->GetItemIfSet(SID_MACROINFO)) diff --git a/cui/source/inc/acccfg.hxx b/cui/source/inc/acccfg.hxx index ed44d8c16d5c..9e444c2615e0 100644 --- a/cui/source/inc/acccfg.hxx +++ b/cui/source/inc/acccfg.hxx @@ -27,6 +27,7 @@ #include <sfx2/tabdlg.hxx> #include <vcl/timer.hxx> #include <vcl/keycod.hxx> +#include <vcl/weld/ComboBox.hxx> #include <vcl/weld/Entry.hxx> #include <i18nutil/searchopt.hxx> #include <config_features.h> @@ -74,6 +75,8 @@ enum class StartFileDialogType SaveAs }; +class ComponentDisposedListener; + class SfxAcceleratorConfigPage : public SfxTabPage { private: @@ -92,6 +95,7 @@ private: css::uno::Reference<css::uno::XComponentContext> m_xContext; css::uno::Reference<css::ui::XAcceleratorConfiguration> m_xGlobal; css::uno::Reference<css::ui::XAcceleratorConfiguration> m_xModule; + css::uno::Reference<css::ui::XAcceleratorConfiguration> m_xDocument; css::uno::Reference<css::ui::XAcceleratorConfiguration> m_xAct; css::uno::Reference<css::container::XNameAccess> m_xUICmdDescription; css::uno::Reference<css::frame::XFrame> m_xFrame; @@ -106,6 +110,7 @@ private: std::unique_ptr<weld::TreeView> m_xEntriesBox; std::unique_ptr<weld::RadioButton> m_xOfficeButton; std::unique_ptr<weld::RadioButton> m_xModuleButton; + std::unique_ptr<weld::RadioButton> m_xDocumentButton; std::unique_ptr<weld::Button> m_xChangeButton; std::unique_ptr<weld::Button> m_xRemoveButton; std::unique_ptr<CuiConfigGroupListBox> m_xGroupLBox; @@ -115,6 +120,10 @@ private: std::unique_ptr<weld::Button> m_xLoadButton; std::unique_ptr<weld::Button> m_xSaveButton; std::unique_ptr<weld::Button> m_xResetButton; + std::unique_ptr<weld::ComboBox> m_xSaveInListBox; + + rtl::Reference<ComponentDisposedListener> m_xComponentDisposedListener; + friend class ComponentDisposedListener; DECL_LINK(ChangeHdl, weld::Button&, void); DECL_LINK(RemoveHdl, weld::Button&, void); @@ -124,6 +133,8 @@ private: DECL_LINK(Load, weld::Button&, void); DECL_LINK(Default, weld::Button&, void); DECL_LINK(RadioHdl, weld::Toggleable&, void); + DECL_LINK(DocumentRadioHdl, weld::Toggleable&, void); + DECL_LINK(SelectSaveInLocation, weld::ComboBox&, void); DECL_LINK(ImplUpdateDataHdl, Timer*, void); DECL_LINK(FocusOut_Impl, weld::Widget&, void); @@ -140,6 +151,10 @@ private: void Init(const css::uno::Reference<css::ui::XAcceleratorConfiguration>& pAccMgr); void ResetConfig(); + void ClearSaveInComboBox(); + void AddFrameToSaveInComboBox(const css::uno::Reference<css::frame::XFrame>& xFrame); + void FillSaveInComboBox(); + void HandleScopeChanged(); public: SfxAcceleratorConfigPage(weld::Container* pPage, weld::DialogController* pController, diff --git a/cui/uiconfig/ui/accelconfigpage.ui b/cui/uiconfig/ui/accelconfigpage.ui index 4e4945a0012d..98acf73a9696 100644 --- a/cui/uiconfig/ui/accelconfigpage.ui +++ b/cui/uiconfig/ui/accelconfigpage.ui @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.38.2 --> +<!-- Generated with glade 3.40.0 --> <interface domain="cui"> <requires lib="gtk+" version="3.24"/> <object class="GtkTreeStore" id="liststore1"> @@ -44,7 +44,7 @@ <column type="gchararray"/> </columns> </object> - <!-- n-columns=1 n-rows=2 --> + <!-- n-columns=1 n-rows=3 --> <object class="GtkGrid" id="AccelConfigPage"> <property name="visible">True</property> <property name="can-focus">False</property> @@ -142,59 +142,24 @@ To quickly find a shortcut in this list, simply press the key combination.</prop </packing> </child> <child> - <object class="GtkBox" id="box17"> + <object class="GtkButtonBox" id="buttonbox1"> <property name="visible">True</property> <property name="can-focus">False</property> <property name="vexpand">True</property> <property name="orientation">vertical</property> <property name="spacing">6</property> + <property name="layout-style">start</property> <child> - <object class="GtkBox" id="box18"> + <object class="GtkButton" id="change"> + <property name="label" translatable="yes" context="accelconfigpage|change">_Assign</property> <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="orientation">vertical</property> - <property name="spacing">6</property> - <child> - <object class="GtkRadioButton" id="office"> - <property name="label" translatable="yes" context="accelconfigpage|office">%PRODUCTNAME</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="use-underline">True</property> - <property name="active">True</property> - <property name="draw-indicator">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="office-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|office">Displays shortcut keys that are common to all the office suite applications.</property> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="module"> - <property name="label" translatable="yes" context="accelconfigpage|module">$(MODULE)</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="use-underline">True</property> - <property name="draw-indicator">True</property> - <property name="group">office</property> - <child internal-child="accessible"> - <object class="AtkObject" id="module-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|module">Displays shortcut keys for the current office suite application.</property> - </object> - </child> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="use-underline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="change-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|change">Assigns the key combination selected in the Shortcut keys list to the command selected in the Function list.</property> </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> </child> </object> <packing> @@ -204,117 +169,83 @@ To quickly find a shortcut in this list, simply press the key combination.</prop </packing> </child> <child> - <object class="GtkButtonBox" id="buttonbox1"> + <object class="GtkButton" id="delete"> + <property name="label" translatable="yes" context="stock">_Delete</property> <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="vexpand">True</property> - <property name="orientation">vertical</property> - <property name="spacing">6</property> - <property name="layout-style">start</property> - <child> - <object class="GtkButton" id="change"> - <property name="label" translatable="yes" context="accelconfigpage|change">_Assign</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">True</property> - <property name="use-underline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="change-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|change">Assigns the key combination selected in the Shortcut keys list to the command selected in the Function list.</property> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="delete"> - <property name="label" translatable="yes" context="stock">_Delete</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">True</property> - <property name="use-underline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="delete-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|delete">Deletes the selected element or elements without requiring confirmation.</property> - </object> - </child> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="use-underline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="delete-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|delete">Deletes the selected element or elements without requiring confirmation.</property> </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> </child> - <child> - <object class="GtkButton" id="load"> - <property name="label" translatable="yes" context="accelconfigpage|load">_Load...</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">True</property> - <property name="use-underline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="load-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|load">Replaces the shortcut key configuration with one that was previously saved.</property> - </object> - </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="load"> + <property name="label" translatable="yes" context="accelconfigpage|load">_Load...</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="use-underline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="load-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|load">Replaces the shortcut key configuration with one that was previously saved.</property> </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - <property name="secondary">True</property> - </packing> </child> - <child> - <object class="GtkButton" id="save"> - <property name="label" translatable="yes" context="accelconfigpage|save">_Save...</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">True</property> - <property name="use-underline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="save-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|save">Saves the current shortcut key configuration, so that you can load it later.</property> - </object> - </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + <property name="secondary">True</property> + </packing> + </child> + <child> + <object class="GtkButton" id="save"> + <property name="label" translatable="yes" context="accelconfigpage|save">_Save...</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="use-underline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="save-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|save">Saves the current shortcut key configuration, so that you can load it later.</property> </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">3</property> - <property name="secondary">True</property> - </packing> </child> - <child> - <object class="GtkButton" id="reset"> - <property name="label" translatable="yes" context="stock">_Reset</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">True</property> - <property name="tooltip-text" translatable="yes" context="accelconfigpage|tooltip|reset">Unsaved modifications to shortcut keys are reverted.</property> - <property name="use-underline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="reset-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|reset">Revert any changes made to keyboard shortcuts to the assignments that were present when this dialog was opened.</property> - </object> - </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + <property name="secondary">True</property> + </packing> + </child> + <child> + <object class="GtkButton" id="reset"> + <property name="label" translatable="yes" context="stock">_Reset</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="tooltip-text" translatable="yes" context="accelconfigpage|tooltip|reset">Unsaved modifications to shortcut keys are reverted.</property> + <property name="use-underline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="reset-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|reset">Revert any changes made to keyboard shortcuts to the assignments that were present when this dialog was opened.</property> </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">4</property> - <property name="secondary">True</property> - </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">1</property> + <property name="position">4</property> + <property name="secondary">True</property> </packing> </child> </object> @@ -326,7 +257,7 @@ To quickly find a shortcut in this list, simply press the key combination.</prop </object> <packing> <property name="left-attach">0</property> - <property name="top-attach">0</property> + <property name="top-attach">1</property> </packing> </child> <child> @@ -574,7 +505,127 @@ To quickly find a shortcut in this list, simply press the key combination.</prop </object> <packing> <property name="left-attach">0</property> - <property name="top-attach">1</property> + <property name="top-attach">2</property> + </packing> + </child> + <child> + <object class="GtkFrame"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label-xalign">0</property> + <property name="shadow-type">none</property> + <child> + <object class="GtkBox" id="box18"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="halign">start</property> + <property name="hexpand">False</property> + <property name="spacing">6</property> + <child> + <object class="GtkRadioButton" id="office"> + <property name="label" translatable="yes" context="accelconfigpage|office">%PRODUCTNAME</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="active">True</property> + <property name="draw-indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="office-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|office">Displays shortcut keys that are common to all the office suite applications.</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="module"> + <property name="label" translatable="yes" context="accelconfigpage|module">$(MODULE)</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> + <property name="group">office</property> + <child internal-child="accessible"> + <object class="AtkObject" id="module-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|module">Displays shortcut keys for the current office suite application.</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="document"> + <property name="label" translatable="yes" context="accelconfigpage|document">_Document</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> + <property name="group">office</property> + <child internal-child="accessible"> + <object class="AtkObject" id="document-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|document">Displays shortcut keys for the chosen document.</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkComboBoxText" id="savein"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child internal-child="accessible"> + <object class="AtkObject" id="savein-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="accelconfigpage|extended_tip|savein">Select the document to make the shortcut be available only in that specific document.</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes" context="accelconfigpage|scopelabel">S_cope</property> + <property name="use-underline">True</property> + </object> + </child> + </object> + <packing> + <property name="left-attach">0</property> + <property name="top-attach">0</property> </packing> </child> <child internal-child="accessible">
