cui/qa/uitest/dialogs/accelerators.py | 44 +++++ cui/source/customize/acccfg.cxx | 253 +++++++++++++++++++++------------- cui/source/inc/acccfg.hxx | 38 ++--- 3 files changed, 219 insertions(+), 116 deletions(-)
New commits: commit a1c00d4e3db74c6661266cf57eedae873dcd5238 Author: Neil Roberts <[email protected]> AuthorDate: Sat Feb 28 18:40:19 2026 +0100 Commit: Neil Roberts <[email protected]> CommitDate: Sun Mar 8 08:43:49 2026 +0100 tdf#171077: Allow customizing shortcuts for multiple scopes Previously whenever the scope is changed in the accelerator configuration dialog it would silently forget any pending assignments. This patch makes it instead store an array of assignments for each possible scope and then apply them all when FillItemSet is called. The arrays are lazily created as each scope is accessed. Change-Id: I0cbff7945f7c11c0498c63314e288f45495529ef Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200697 Reviewed-by: Neil Roberts <[email protected]> Tested-by: Jenkins diff --git a/cui/qa/uitest/dialogs/accelerators.py b/cui/qa/uitest/dialogs/accelerators.py index 34722ffc02a5..60391edc3f9a 100644 --- a/cui/qa/uitest/dialogs/accelerators.py +++ b/cui/qa/uitest/dialogs/accelerators.py @@ -181,4 +181,48 @@ class Test(UITestCase): xAcceleratorPage.getChild("office").executeAction("CLICK", tuple()) self.assertEqual(get_state_as_dict(xScope)["Enabled"], "false") + def test_multiple_scopes(self): + # tdf#171077 Test setting accelerators in multiple scopes at the same time without closing + # the dialog in between changing the scope + with self.ui_test.create_doc_in_start_center("writer") as xDoc1, \ + self.ui_test.load_empty_file("writer") as xDoc2: + with self.ui_test.execute_dialog_through_command(".uno:ConfigureDialog") as xDialog: + # Set a shortcut on the global scope + self.assign_key(xDialog, "office", "F7", ".uno:EditBookmark") + # Set a shortcut on the module scope + self.assign_key(xDialog, "module", "F7", ".uno:Credits") + # Set a shortcut in the first document + self.assign_key(xDialog, "Untitled 1", "F7", "Spelling") + # Set a shortcut in the second document + self.assign_key(xDialog, "Untitled 2", "F7", ".uno:OptionsSecurityDialog") + + # Switch back to the first document and make sure the binding is still in the list + xAcceleratorPage = xDialog.getChild("AccelConfigPage") + xScope = xAcceleratorPage.getChild("savein") + select_by_text(xScope, "Untitled 1") + xShortcuts = xAcceleratorPage.getChild("shortcuts") + select_key(xShortcuts, "F7") + self.assertEqual(get_state_as_dict(xShortcuts)["SelectEntryText"].split(" ")[1], + "Spelling") + + # Check that all the accelerators made it into the configs + xGlobalAccelCfg = self.xContext.ServiceManager.createInstance( + 'com.sun.star.ui.GlobalAcceleratorConfiguration') + xKeyEvent = KeyEvent() + xKeyEvent.KeyCode = Key.F7 + self.assertEqual(xGlobalAccelCfg.getCommandByKeyEvent(xKeyEvent), ".uno:EditBookmark") + + xModuleAccelCfg = self.xContext.ServiceManager.createInstanceWithArguments( + 'com.sun.star.ui.ModuleAcceleratorConfiguration', + ('com.sun.star.text.TextDocument',)) + self.assertEqual(xModuleAccelCfg.getCommandByKeyEvent(xKeyEvent), ".uno:Credits") + + xDocAccelCfg = xDoc1.getUIConfigurationManager().getShortCutManager() + self.assertEqual(xDocAccelCfg.getCommandByKeyEvent(xKeyEvent), + ".uno:SpellingAndGrammarDialog") + + xDocAccelCfg = xDoc2.getUIConfigurationManager().getShortCutManager() + self.assertEqual(xDocAccelCfg.getCommandByKeyEvent(xKeyEvent), + ".uno:OptionsSecurityDialog") + # vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/cui/source/customize/acccfg.cxx b/cui/source/customize/acccfg.cxx index 68bb32250be8..32b3f751188b 100644 --- a/cui/source/customize/acccfg.cxx +++ b/cui/source/customize/acccfg.cxx @@ -843,6 +843,21 @@ struct AcceleratorSaveInData }; } +// Pairs a reference to an accelerator configuration with a list of assignments +struct AssignmentData +{ + AssignmentData(const uno::Reference<ui::XAcceleratorConfiguration>& xAccMgr) + : m_xAccMgr(xAccMgr) + // Create an array with an empty string for each key + , m_aAssignments(std::size(KEYCODE_ARRAY)) + { + } + + uno::Reference<ui::XAcceleratorConfiguration> m_xAccMgr; + // A list of assignments using the same indices as KEYCODE_ARRAY + std::vector<OUString> m_aAssignments; +}; + // Helper class to listen for components being disposed so we can // remove them from the SaveIn combobox class ComponentDisposedListener : public ::cppu::WeakImplHelper<lang::XEventListener> @@ -893,6 +908,12 @@ void ComponentDisposedListener::disposing(const lang::EventObject& rEvent) m_pAccelCfgPage->m_xDocumentButton->hide(); } + // Abandon any assignment data for this document + std::erase_if(m_pAccelCfgPage->m_aAssignmentData, + [pData](const AssignmentData& rAssignmentData) { + return rAssignmentData.m_xAccMgr == pData->m_xAccMgr; + }); + pData->m_xModel->removeEventListener(this); delete pData; @@ -1108,15 +1129,35 @@ void SfxAcceleratorConfigPage::InitAccCfg() } } -void SfxAcceleratorConfigPage::Init(const uno::Reference<ui::XAcceleratorConfiguration>& xAccMgr) +void SfxAcceleratorConfigPage::LoadAcceleratorConfig( + const css::uno::Reference<css::ui::XAcceleratorConfiguration>& xAccMgr) { - if (!xAccMgr.is()) - return; + std::vector<OUString>& rAssignments = GetAssignments(); + + // Reset all of the assignments + for (auto& rAssignment : rAssignments) + rAssignment.clear(); + + // Assign all commands to its shortcuts - reading the accelerator config. + uno::Sequence<awt::KeyEvent> lKeys = xAccMgr->getAllKeyEvents(); + + for (sal_Int32 i = 0, nKeys = lKeys.getLength(); i < nKeys; ++i) + { + const awt::KeyEvent& aAWTKey = lKeys[i]; + vcl::KeyCode aKeyCode = svt::AcceleratorExecute::st_AWTKey2VCLKey(aAWTKey); - // Initially set all of the assignments to an empty string - m_aAssignments.clear(); - m_aAssignments.resize(KEYCODE_ARRAY_SIZE); + const sal_uInt16* pKeycodePos = std::find(KEYCODE_ARRAY, KEYCODE_ARRAY + KEYCODE_ARRAY_SIZE, + aKeyCode.GetCode() | aKeyCode.GetModifier()); + if (pKeycodePos >= KEYCODE_ARRAY + KEYCODE_ARRAY_SIZE) + continue; + + rAssignments[pKeycodePos - KEYCODE_ARRAY] = xAccMgr->getCommandByKeyEvent(aAWTKey); + } +} + +void SfxAcceleratorConfigPage::Init() +{ if (!m_bStylesInfoInitialized) { uno::Reference<frame::XController> xController; @@ -1131,6 +1172,8 @@ void SfxAcceleratorConfigPage::Init(const uno::Reference<ui::XAcceleratorConfigu m_bStylesInfoInitialized = true; } + const std::vector<OUString>& rAssignments = GetAssignments(); + // Insert all editable accelerators into list box. It is possible // that some accelerators are not mapped on the current system/keyboard // but we don't want to lose these mappings. @@ -1142,39 +1185,14 @@ void SfxAcceleratorConfigPage::Init(const uno::Reference<ui::XAcceleratorConfigu continue; m_xEntriesBox->append(OUString::number(i1), sKey); int nPos = m_xEntriesBox->n_children() - 1; - m_xEntriesBox->set_text(nPos, OUString(), 1); - m_xEntriesBox->set_sensitive(nPos, !IsReservedKeyCode(aKey)); - } - - // Assign all commands to its shortcuts - reading the accelerator config. - uno::Sequence<awt::KeyEvent> lKeys = xAccMgr->getAllKeyEvents(); - sal_Int32 c2 = lKeys.getLength(); - sal_Int32 i2 = 0; - - for (i2 = 0; i2 < c2; ++i2) - { - const awt::KeyEvent& aAWTKey = lKeys[i2]; - vcl::KeyCode aKeyCode = svt::AcceleratorExecute::st_AWTKey2VCLKey(aAWTKey); - OUString sCommand = xAccMgr->getCommandByKeyEvent(aAWTKey); - - const sal_uInt16* pKeyCode - = std::find(KEYCODE_ARRAY, KEYCODE_ARRAY + KEYCODE_ARRAY_SIZE, aKeyCode.GetFullCode()); - - if (pKeyCode < KEYCODE_ARRAY + KEYCODE_ARRAY_SIZE) - m_aAssignments[pKeyCode - KEYCODE_ARRAY] = sCommand; - - sal_Int32 nPos = MapKeyCodeToPos(aKeyCode); - - if (nPos == -1) - continue; - - OUString sLabel = GetLabel4Command(sCommand); - + OUString sLabel = GetLabel4Command(rAssignments[i1]); m_xEntriesBox->set_text(nPos, sLabel, 1); + m_xEntriesBox->set_sensitive(nPos, !IsReservedKeyCode(aKey)); } } -void SfxAcceleratorConfigPage::Apply(const uno::Reference<ui::XAcceleratorConfiguration>& xAccMgr) +void SfxAcceleratorConfigPage::Apply(const uno::Reference<ui::XAcceleratorConfiguration>& xAccMgr, + const std::vector<OUString>& rAssignments) { if (!xAccMgr.is()) return; @@ -1182,11 +1200,11 @@ void SfxAcceleratorConfigPage::Apply(const uno::Reference<ui::XAcceleratorConfig // Go through the list from the bottom to the top ... // because logical accelerator must be preferred instead of // physical ones! - for (int i = 0, nCount = m_aAssignments.size(); i < nCount; ++i) + for (int i = 0, nCount = rAssignments.size(); i < nCount; ++i) { vcl::KeyCode aKey = KEYCODE_ARRAY[i]; awt::KeyEvent aAWTKey = svt::AcceleratorExecute::st_VCLKey2AWTKey(aKey); - OUString sCommand = m_aAssignments[i]; + OUString sCommand = rAssignments[i]; try { @@ -1207,6 +1225,26 @@ void SfxAcceleratorConfigPage::Apply(const uno::Reference<ui::XAcceleratorConfig void SfxAcceleratorConfigPage::ResetConfig() { m_xEntriesBox->clear(); } +std::vector<OUString>* SfxAcceleratorConfigPage::FindAssignments() +{ + for (auto& rAssignmentData : m_aAssignmentData) + { + if (rAssignmentData.m_xAccMgr == m_xAct) + return &rAssignmentData.m_aAssignments; + } + + return nullptr; +} + +std::vector<OUString>& SfxAcceleratorConfigPage::GetAssignments() +{ + if (std::vector<OUString>* pAssignments = FindAssignments()) + return *pAssignments; + + // Lazily create the data + return m_aAssignmentData.emplace_back(m_xAct).m_aAssignments; +} + IMPL_LINK_NOARG(SfxAcceleratorConfigPage, ImplUpdateDataHdl, Timer*, void) { SelectHdl(m_xGroupLBox->get_widget()); @@ -1249,7 +1287,8 @@ IMPL_LINK_NOARG(SfxAcceleratorConfigPage, Default, weld::Button&, void) m_xEntriesBox->freeze(); ResetConfig(); - Init(m_xAct); + LoadAcceleratorConfig(m_xAct); + Init(); m_xEntriesBox->thaw(); m_xEntriesBox->select(0); SelectHdl(*m_xEntriesBox); @@ -1267,7 +1306,7 @@ IMPL_LINK_NOARG(SfxAcceleratorConfigPage, ChangeHdl, weld::Button&, void) if (sLabel.isEmpty()) sLabel = GetLabel4Command(sNewCommand); - m_aAssignments[nKeyPos] = sNewCommand; + GetAssignments()[nKeyPos] = sNewCommand; m_xEntriesBox->set_text(nPos, sLabel, 1); SelectHdl(m_xFunctionBox->get_widget()); @@ -1284,13 +1323,15 @@ IMPL_LINK_NOARG(SfxAcceleratorConfigPage, RemoveHdl, weld::Button&, void) // remove function name from selected entry m_xEntriesBox->set_text(nPos, OUString(), 1); - m_aAssignments[nKeyPos].clear(); + GetAssignments()[nKeyPos].clear(); SelectHdl(m_xFunctionBox->get_widget()); } IMPL_LINK(SfxAcceleratorConfigPage, SelectHdl, weld::TreeView&, rListBox, void) { + const std::vector<OUString>& rAssignments = GetAssignments(); + if (&rListBox == m_xEntriesBox.get()) { OUString nSelectedId = m_xEntriesBox->get_selected_id(); @@ -1306,7 +1347,7 @@ IMPL_LINK(SfxAcceleratorConfigPage, SelectHdl, weld::TreeView&, rListBox, void) if (!IsReservedKeyCode(KEYCODE_ARRAY[nKeyPos])) { - OUString sCommand = m_aAssignments[nKeyPos]; + OUString sCommand = rAssignments[nKeyPos]; if (!sCommand.isEmpty()) m_xRemoveButton->set_sensitive(true); m_xChangeButton->set_sensitive(sCommand != sPossibleNewCommand); @@ -1349,7 +1390,7 @@ IMPL_LINK(SfxAcceleratorConfigPage, SelectHdl, weld::TreeView&, rListBox, void) if (!IsReservedKeyCode(KEYCODE_ARRAY[nKeyPos])) { - OUString sCommand = m_aAssignments[nKeyPos]; + OUString sCommand = rAssignments[nKeyPos]; if (!sCommand.isEmpty()) m_xRemoveButton->set_sensitive(true); m_xChangeButton->set_sensitive(sCommand != sPossibleNewCommand @@ -1363,7 +1404,7 @@ IMPL_LINK(SfxAcceleratorConfigPage, SelectHdl, weld::TreeView&, rListBox, void) for (int i = 0, nCount = m_xEntriesBox->n_children(); i < nCount; ++i) { sal_uInt32 nEntryKeyPos = m_xEntriesBox->get_id(i).toUInt32(); - if (m_aAssignments[nEntryKeyPos] == sPossibleNewCommand) + if (rAssignments[nEntryKeyPos] == sPossibleNewCommand) { vcl::KeyCode aKey(KEYCODE_ARRAY[nEntryKeyPos]); m_xKeyBox->append(OUString::number(nEntryKeyPos), aKey.GetName()); @@ -1427,7 +1468,11 @@ void SfxAcceleratorConfigPage::HandleScopeChanged() m_xEntriesBox->freeze(); ResetConfig(); - Init(m_xAct); + // If we don’t have any saved assignments for this scope then load from the accelerator + // configuration + if (FindAssignments() == nullptr && m_xAct.is()) + LoadAcceleratorConfig(m_xAct); + Init(); m_xEntriesBox->thaw(); m_xGroupLBox->Init(m_xContext, m_xFrame, m_sModuleLongName, true); @@ -1487,7 +1532,8 @@ IMPL_LINK_NOARG(SfxAcceleratorConfigPage, LoadHdl, sfx2::FileDialogHelper*, void m_xEntriesBox->freeze(); ResetConfig(); - Init(xTempAccMgr); + LoadAcceleratorConfig(xTempAccMgr); + Init(); m_xEntriesBox->thaw(); if (m_xEntriesBox->n_children()) { @@ -1615,10 +1661,26 @@ void SfxAcceleratorConfigPage::StartFileDialog(StartFileDialogType nType, const bool SfxAcceleratorConfigPage::FillItemSet(SfxItemSet*) { - Apply(m_xAct); + for (const AssignmentData& rAssignmentData : m_aAssignmentData) + { + Apply(rAssignmentData.m_xAccMgr, rAssignmentData.m_aAssignments); + + try + { + rAssignmentData.m_xAccMgr->store(); + } + catch (const uno::RuntimeException&) + { + throw; + } + catch (const uno::Exception&) + { + return false; + } + } + try { - m_xAct->store(); css::uno::Reference<css::beans::XPropertySet> xFrameProps(m_xFrame, css::uno::UNO_QUERY_THROW); css::uno::Reference<css::frame::XLayoutManager> xLayoutManager; diff --git a/cui/source/inc/acccfg.hxx b/cui/source/inc/acccfg.hxx index 960fbbe7efc1..abc29984cb3d 100644 --- a/cui/source/inc/acccfg.hxx +++ b/cui/source/inc/acccfg.hxx @@ -51,6 +51,7 @@ enum class StartFileDialogType }; class ComponentDisposedListener; +struct AssignmentData; class SfxAcceleratorConfigPage : public SfxTabPage { @@ -69,8 +70,8 @@ private: // Array of reserved key codes in sorted order std::vector<sal_uInt16> m_aReservedKeyCodes; - // Array mapping key code index to a command. The indices are the same as for KEYCODE_ARRAY. - std::vector<OUString> m_aAssignments; + // Lazily created assignment data for each accelerator configuration + std::vector<AssignmentData> m_aAssignmentData; css::uno::Reference<css::uno::XComponentContext> m_xContext; css::uno::Reference<css::ui::XAcceleratorConfiguration> m_xGlobal; @@ -129,7 +130,9 @@ private: sal_Int32 MapKeyCodeToPos(const vcl::KeyCode& rCode) const; void StartFileDialog(StartFileDialogType nType, const OUString& rTitle); - void Init(const css::uno::Reference<css::ui::XAcceleratorConfiguration>& pAccMgr); + void + LoadAcceleratorConfig(const css::uno::Reference<css::ui::XAcceleratorConfiguration>& pAccMgr); + void Init(); void ResetConfig(); void ClearSaveInComboBox(); void AddFrameToSaveInComboBox(const css::uno::Reference<css::frame::XFrame>& xFrame); @@ -137,6 +140,17 @@ private: void HandleScopeChanged(); bool IsReservedKeyCode(const vcl::KeyCode& rCode) const; static std::vector<sal_uInt16> GetReservedKeyCodes(); + // Find the assignments array for the current configuration or return nullptr if there isn’t one + // yet + std::vector<OUString>* FindAssignments(); + // Get the assignments for the current configuration or lazily create it if there isn’t one yet + std::vector<OUString>& GetAssignments(); + static void Apply(const css::uno::Reference<css::ui::XAcceleratorConfiguration>& pAccMgr, + const std::vector<OUString>& rAssignments); + void Apply(const css::uno::Reference<css::ui::XAcceleratorConfiguration>& pAccMgr) + { + Apply(pAccMgr, GetAssignments()); + }; public: SfxAcceleratorConfigPage(weld::Container* pPage, weld::DialogController* pController, @@ -145,8 +159,6 @@ public: virtual bool FillItemSet(SfxItemSet*) override; virtual void Reset(const SfxItemSet*) override; - - void Apply(const css::uno::Reference<css::ui::XAcceleratorConfiguration>& pAccMgr); }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit 5d46f3e5513d13371b32f45a1cce11fa98a9ed10 Author: Neil Roberts <[email protected]> AuthorDate: Sun Mar 1 02:54:44 2026 +0100 Commit: Neil Roberts <[email protected]> CommitDate: Sun Mar 8 08:43:36 2026 +0100 acccfg: Store key assignments in a separate array Previously the assignments for each key were stored as user data for each entry in the key list box. Instead they are now stored in a vector which maps key indices to a command string. The key indices are the indices of KEYCODE_ARRAY. The user data in TAccInfo which was attached to each entry in the list box no longer has any useful information so the struct has been removed entirely. The ID of an entry in the list box is now directly a key index instead of storing a pointer to the user data. The goal is to make it easier to store the assignments for multiple scopes in a later patch. Change-Id: I267da16210e7354834833b7f5277569e1208a46c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200696 Tested-by: Jenkins Reviewed-by: Neil Roberts <[email protected]> diff --git a/cui/source/customize/acccfg.cxx b/cui/source/customize/acccfg.cxx index 1f1d1fa6159f..68bb32250be8 100644 --- a/cui/source/customize/acccfg.cxx +++ b/cui/source/customize/acccfg.cxx @@ -919,18 +919,16 @@ IMPL_LINK(SfxAcceleratorConfigPage, KeyInputHdl, const KeyEvent&, rKey, bool) for (int i = 0, nCount = m_xEntriesBox->n_children(); i < nCount; ++i) { - TAccInfo* pUserData = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(i)); - if (pUserData) - { - sal_uInt16 nCode2 = pUserData->m_aKey.GetCode(); - sal_uInt16 nMod2 = pUserData->m_aKey.GetModifier(); + sal_uInt32 nKeyPos = m_xEntriesBox->get_id(i).toUInt32(); + vcl::KeyCode aCode2 = KEYCODE_ARRAY[nKeyPos]; + sal_uInt16 nCode2 = aCode2.GetCode(); + sal_uInt16 nMod2 = aCode2.GetModifier(); - if (nCode1 == nCode2 && nMod1 == nMod2) - { - m_xEntriesBox->select(i); - m_xEntriesBox->scroll_to_row(i); - return true; - } + if (nCode1 == nCode2 && nMod1 == nMod2) + { + m_xEntriesBox->select(i); + m_xEntriesBox->scroll_to_row(i); + return true; } } @@ -1053,13 +1051,6 @@ bool SfxAcceleratorConfigPage::IsReservedKeyCode(const vcl::KeyCode& rKeyCode) c SfxAcceleratorConfigPage::~SfxAcceleratorConfigPage() { - // free memory - remove all dynamic user data - for (int i = 0, nCount = m_xEntriesBox->n_children(); i < nCount; ++i) - { - TAccInfo* pUserData = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(i)); - delete pUserData; - } - // Free the dynamic user data for the SaveIn combobox ClearSaveInComboBox(); @@ -1122,6 +1113,10 @@ void SfxAcceleratorConfigPage::Init(const uno::Reference<ui::XAcceleratorConfigu if (!xAccMgr.is()) return; + // Initially set all of the assignments to an empty string + m_aAssignments.clear(); + m_aAssignments.resize(KEYCODE_ARRAY_SIZE); + if (!m_bStylesInfoInitialized) { uno::Reference<frame::XController> xController; @@ -1145,8 +1140,7 @@ void SfxAcceleratorConfigPage::Init(const uno::Reference<ui::XAcceleratorConfigu OUString sKey = aKey.GetName(); if (sKey.isEmpty()) continue; - TAccInfo* pEntry = new TAccInfo(i1, aKey); - m_xEntriesBox->append(weld::toId(pEntry), sKey); + m_xEntriesBox->append(OUString::number(i1), sKey); int nPos = m_xEntriesBox->n_children() - 1; m_xEntriesBox->set_text(nPos, OUString(), 1); m_xEntriesBox->set_sensitive(nPos, !IsReservedKeyCode(aKey)); @@ -1160,19 +1154,23 @@ void SfxAcceleratorConfigPage::Init(const uno::Reference<ui::XAcceleratorConfigu for (i2 = 0; i2 < c2; ++i2) { const awt::KeyEvent& aAWTKey = lKeys[i2]; - OUString sCommand = xAccMgr->getCommandByKeyEvent(aAWTKey); - OUString sLabel = GetLabel4Command(sCommand); vcl::KeyCode aKeyCode = svt::AcceleratorExecute::st_AWTKey2VCLKey(aAWTKey); + OUString sCommand = xAccMgr->getCommandByKeyEvent(aAWTKey); + + const sal_uInt16* pKeyCode + = std::find(KEYCODE_ARRAY, KEYCODE_ARRAY + KEYCODE_ARRAY_SIZE, aKeyCode.GetFullCode()); + + if (pKeyCode < KEYCODE_ARRAY + KEYCODE_ARRAY_SIZE) + m_aAssignments[pKeyCode - KEYCODE_ARRAY] = sCommand; + sal_Int32 nPos = MapKeyCodeToPos(aKeyCode); if (nPos == -1) continue; - m_xEntriesBox->set_text(nPos, sLabel, 1); - - TAccInfo* pEntry = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(nPos)); + OUString sLabel = GetLabel4Command(sCommand); - pEntry->m_sCommand = sCommand; + m_xEntriesBox->set_text(nPos, sLabel, 1); } } @@ -1184,17 +1182,11 @@ void SfxAcceleratorConfigPage::Apply(const uno::Reference<ui::XAcceleratorConfig // Go through the list from the bottom to the top ... // because logical accelerator must be preferred instead of // physical ones! - for (int i = 0, nCount = m_xEntriesBox->n_children(); i < nCount; ++i) + for (int i = 0, nCount = m_aAssignments.size(); i < nCount; ++i) { - TAccInfo* pUserData = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(i)); - OUString sCommand; - awt::KeyEvent aAWTKey; - - if (pUserData) - { - sCommand = pUserData->m_sCommand; - aAWTKey = svt::AcceleratorExecute::st_VCLKey2AWTKey(pUserData->m_aKey); - } + vcl::KeyCode aKey = KEYCODE_ARRAY[i]; + awt::KeyEvent aAWTKey = svt::AcceleratorExecute::st_VCLKey2AWTKey(aKey); + OUString sCommand = m_aAssignments[i]; try { @@ -1269,13 +1261,13 @@ IMPL_LINK_NOARG(SfxAcceleratorConfigPage, ChangeHdl, weld::Button&, void) if (nPos == -1) return; - TAccInfo* pEntry = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(nPos)); + sal_uInt32 nKeyPos = m_xEntriesBox->get_id(nPos).toUInt32(); OUString sNewCommand = m_xFunctionBox->GetCurCommand(); OUString sLabel = m_xFunctionBox->GetCurLabel(); if (sLabel.isEmpty()) sLabel = GetLabel4Command(sNewCommand); - pEntry->m_sCommand = sNewCommand; + m_aAssignments[nKeyPos] = sNewCommand; m_xEntriesBox->set_text(nPos, sLabel, 1); SelectHdl(m_xFunctionBox->get_widget()); @@ -1288,11 +1280,11 @@ IMPL_LINK_NOARG(SfxAcceleratorConfigPage, RemoveHdl, weld::Button&, void) if (nPos == -1) return; - TAccInfo* pEntry = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(nPos)); + sal_uInt32 nKeyPos = m_xEntriesBox->get_id(nPos).toUInt32(); // remove function name from selected entry m_xEntriesBox->set_text(nPos, OUString(), 1); - pEntry->m_sCommand.clear(); + m_aAssignments[nKeyPos].clear(); SelectHdl(m_xFunctionBox->get_widget()); } @@ -1301,18 +1293,24 @@ IMPL_LINK(SfxAcceleratorConfigPage, SelectHdl, weld::TreeView&, rListBox, void) { if (&rListBox == m_xEntriesBox.get()) { - TAccInfo* pEntry = weld::fromId<TAccInfo*>(m_xEntriesBox->get_selected_id()); + OUString nSelectedId = m_xEntriesBox->get_selected_id(); OUString sPossibleNewCommand = m_xFunctionBox->GetCurCommand(); m_xRemoveButton->set_sensitive(false); m_xChangeButton->set_sensitive(false); - if (pEntry && !IsReservedKeyCode(pEntry->m_aKey)) + if (!nSelectedId.isEmpty()) { - if (pEntry->isConfigured()) - m_xRemoveButton->set_sensitive(true); - m_xChangeButton->set_sensitive(pEntry->m_sCommand != sPossibleNewCommand); + sal_uInt32 nKeyPos = nSelectedId.toUInt32(); + + if (!IsReservedKeyCode(KEYCODE_ARRAY[nKeyPos])) + { + OUString sCommand = m_aAssignments[nKeyPos]; + if (!sCommand.isEmpty()) + m_xRemoveButton->set_sensitive(true); + m_xChangeButton->set_sensitive(sCommand != sPossibleNewCommand); + } } } else if (&rListBox == &m_xGroupLBox->get_widget()) @@ -1343,16 +1341,18 @@ IMPL_LINK(SfxAcceleratorConfigPage, SelectHdl, weld::TreeView&, rListBox, void) m_xChangeButton->set_sensitive(false); // #i36994 First selected can return null! - TAccInfo* pEntry = weld::fromId<TAccInfo*>(m_xEntriesBox->get_selected_id()); - if (pEntry) + OUString sSelectedId = m_xEntriesBox->get_selected_id(); + if (!sSelectedId.isEmpty()) { + sal_uInt32 nKeyPos = sSelectedId.toUInt32(); OUString sPossibleNewCommand = m_xFunctionBox->GetCurCommand(); - if (!IsReservedKeyCode(pEntry->m_aKey)) + if (!IsReservedKeyCode(KEYCODE_ARRAY[nKeyPos])) { - if (pEntry->isConfigured()) + OUString sCommand = m_aAssignments[nKeyPos]; + if (!sCommand.isEmpty()) m_xRemoveButton->set_sensitive(true); - m_xChangeButton->set_sensitive(pEntry->m_sCommand != sPossibleNewCommand + m_xChangeButton->set_sensitive(sCommand != sPossibleNewCommand && !sPossibleNewCommand.isEmpty()); } @@ -1362,10 +1362,11 @@ IMPL_LINK(SfxAcceleratorConfigPage, SelectHdl, weld::TreeView&, rListBox, void) { for (int i = 0, nCount = m_xEntriesBox->n_children(); i < nCount; ++i) { - TAccInfo* pUserData = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(i)); - if (pUserData && pUserData->m_sCommand == sPossibleNewCommand) + sal_uInt32 nEntryKeyPos = m_xEntriesBox->get_id(i).toUInt32(); + if (m_aAssignments[nEntryKeyPos] == sPossibleNewCommand) { - m_xKeyBox->append(weld::toId(pUserData), pUserData->m_aKey.GetName()); + vcl::KeyCode aKey(KEYCODE_ARRAY[nEntryKeyPos]); + m_xKeyBox->append(OUString::number(nEntryKeyPos), aKey.GetName()); } } } @@ -1374,15 +1375,17 @@ IMPL_LINK(SfxAcceleratorConfigPage, SelectHdl, weld::TreeView&, rListBox, void) else { // goto selected "key" entry of the key box - int nP2 = -1; - TAccInfo* pU2 = weld::fromId<TAccInfo*>(m_xKeyBox->get_selected_id()); - if (pU2) - nP2 = MapKeyCodeToPos(pU2->m_aKey); - if (nP2 != -1) + OUString sSelectedId = m_xKeyBox->get_selected_id(); + if (!sSelectedId.isEmpty()) { - m_xEntriesBox->select(nP2); - m_xEntriesBox->scroll_to_row(nP2); - SelectHdl(*m_xEntriesBox); + sal_uInt32 nKeyPos = sSelectedId.toUInt32(); + int nPos = MapKeyCodeToPos(KEYCODE_ARRAY[nKeyPos]); + if (nPos != -1) + { + m_xEntriesBox->select(nPos); + m_xEntriesBox->scroll_to_row(nPos); + SelectHdl(*m_xEntriesBox); + } } } } @@ -1784,16 +1787,12 @@ void SfxAcceleratorConfigPage::Reset(const SfxItemSet* rSet) sal_Int32 SfxAcceleratorConfigPage::MapKeyCodeToPos(const vcl::KeyCode& aKey) const { - sal_uInt16 nCode1 = aKey.GetCode() + aKey.GetModifier(); + sal_uInt16 nCode = aKey.GetCode() + aKey.GetModifier(); for (int i = 0, nCount = m_xEntriesBox->n_children(); i < nCount; ++i) { - TAccInfo* pUserData = weld::fromId<TAccInfo*>(m_xEntriesBox->get_id(i)); - if (pUserData) - { - sal_uInt16 nCode2 = pUserData->m_aKey.GetCode() + pUserData->m_aKey.GetModifier(); - if (nCode1 == nCode2) - return i; - } + sal_uInt32 nKeyPos = m_xEntriesBox->get_id(i).toUInt32(); + if (nCode == KEYCODE_ARRAY[nKeyPos]) + return i; } return -1; diff --git a/cui/source/inc/acccfg.hxx b/cui/source/inc/acccfg.hxx index 11051f350bb2..960fbbe7efc1 100644 --- a/cui/source/inc/acccfg.hxx +++ b/cui/source/inc/acccfg.hxx @@ -39,23 +39,6 @@ class SfxMacroInfoItem; // class SfxAcceleratorConfigPage ---------------------------------------- -struct TAccInfo -{ -public: - TAccInfo(sal_Int32 nKeyPos, const vcl::KeyCode& aKey) - : m_nKeyPos(nKeyPos) - , m_sCommand() - , m_aKey(aKey) - { - } - - bool isConfigured() const { return (m_nKeyPos > -1 && !m_sCommand.isEmpty()); } - - sal_Int32 m_nKeyPos; - OUString m_sCommand; - vcl::KeyCode m_aKey; -}; - namespace sfx2 { class FileDialogHelper; @@ -86,6 +69,9 @@ private: // Array of reserved key codes in sorted order std::vector<sal_uInt16> m_aReservedKeyCodes; + // Array mapping key code index to a command. The indices are the same as for KEYCODE_ARRAY. + std::vector<OUString> m_aAssignments; + css::uno::Reference<css::uno::XComponentContext> m_xContext; css::uno::Reference<css::ui::XAcceleratorConfiguration> m_xGlobal; css::uno::Reference<css::ui::XAcceleratorConfiguration> m_xModule;
