From db49ac4e1916f87143574b064b36ec86f8407145 Mon Sep 17 00:00:00 2001 From: Albert Mkhitaryan Date: Fri, 20 Feb 2026 08:48:54 +0000 Subject: Add keyboard shortcut support for service menu actions Introduce ServiceMenuShortcutManager, which registers all service menu actions with KActionCollection at startup allowing users to assign keyboard shortcuts in Configure Keyboard Shorcuts. Save/Load of configs happens via KXMLGUI in dolphinui.rc. Notes: - Manager initializes before setupGUI() for shortcut restoration - Execution and validation handled entirely by KFileItemAction in KIO. BUG: 260266 --- src/CMakeLists.txt | 7 ++++ src/dolphincontextmenu.cpp | 2 - src/dolphinmainwindow.cpp | 22 ++++++++++- src/dolphinmainwindow.h | 2 + src/servicemenushortcutmanager.cpp | 81 ++++++++++++++++++++++++++++++++++++++ src/servicemenushortcutmanager.h | 42 ++++++++++++++++++++ 6 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 src/servicemenushortcutmanager.cpp create mode 100644 src/servicemenushortcutmanager.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3cf10f486..4faabe9d5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -457,6 +457,13 @@ if(HAVE_KUSERFEEDBACK) ) endif() +if(KF6KIO_VERSION VERSION_GREATER_EQUAL "6.24.0") + target_sources(dolphinstatic PRIVATE + servicemenushortcutmanager.cpp + servicemenushortcutmanager.h + ) +endif() + kconfig_add_kcfg_files(dolphinstatic panels/folders/dolphin_folderspanelsettings.kcfgc panels/places/dolphin_placespanelsettings.kcfgc diff --git a/src/dolphincontextmenu.cpp b/src/dolphincontextmenu.cpp index 577990e19..6fb362039 100644 --- a/src/dolphincontextmenu.cpp +++ b/src/dolphincontextmenu.cpp @@ -268,8 +268,6 @@ void DolphinContextMenu::addItemContextMenu() const KFileItemListProperties &selectedItemsProps = selectedItemsProperties(); - m_fileItemActions->setItemListProperties(selectedItemsProps); - if (m_selectedItems.count() == 1) { // single files if (m_fileInfo.isDir()) { diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 62ff2fa45..110bef2f3 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -28,6 +28,9 @@ #include "panels/terminal/terminalpanel.h" #include "search/dolphinquery.h" #include "selectionmode/actiontexthelper.h" +#if KIO_VERSION >= QT_VERSION_CHECK(6, 24, 0) +#include "servicemenushortcutmanager.h" +#endif #include "settings/dolphinsettingsdialog.h" #include "statusbar/diskspaceusagemenu.h" #include "statusbar/dolphinstatusbar.h" @@ -206,6 +209,11 @@ DolphinMainWindow::DolphinMainWindow() setupDockWidgets(); +#if KIO_VERSION >= QT_VERSION_CHECK(6, 24, 0) + m_serviceMenuShortcutManager = new ServiceMenuShortcutManager(actionCollection(), this); +#endif + setupFileItemActions(); + const bool usePhoneUi{KRuntimePlatform::runtimePlatform().contains(QLatin1String("phone"))}; setupGUI(Save | Create | ToolBar, usePhoneUi ? QStringLiteral("dolphinuiforphones.rc") : QString() /* load the default dolphinui.rc file */); stateChanged(QStringLiteral("new_file")); @@ -256,8 +264,6 @@ DolphinMainWindow::DolphinMainWindow() QTimer::singleShot(0, this, &DolphinMainWindow::updateOpenPreferredSearchToolAction); - setupFileItemActions(); - m_serviceMenuConfigWatcher = KConfigWatcher::create(KSharedConfig::openConfig(QStringLiteral("kservicemenurc"))); connect(m_serviceMenuConfigWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup & /*group*/, const QByteArrayList & /*names*/) { setupFileItemActions(); @@ -431,6 +437,10 @@ void DolphinMainWindow::slotSelectionChanged(const KFileItemList &selection) { updateFileAndEditActions(); + if (m_fileItemActions) { + m_fileItemActions->setItemListProperties(KFileItemListProperties(selection)); + } + const int selectedUrlsCount = m_tabWidget->currentTabPage()->selectedItemsCount(); QAction *compareFilesAction = actionCollection()->action(QStringLiteral("compare_files")); @@ -1783,6 +1793,10 @@ void DolphinMainWindow::slotStorageTearDownExternallyRequested(const QString &mo void DolphinMainWindow::slotKeyBindings() { +#if KIO_VERSION >= QT_VERSION_CHECK(6, 24, 0) + m_serviceMenuShortcutManager->cleanupStaleShortcuts(this); +#endif + KShortcutsDialog dialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this); dialog.addCollection(actionCollection()); if (m_terminalPanel) { @@ -2624,6 +2638,10 @@ void DolphinMainWindow::setupFileItemActions() connect(m_fileItemActions, &KFileItemActions::error, this, [this](const QString &errorMessage) { showErrorMessage(errorMessage); }); + +#if KIO_VERSION >= QT_VERSION_CHECK(6, 24, 0) + m_serviceMenuShortcutManager->refresh(m_fileItemActions); +#endif } void DolphinMainWindow::updateFileAndEditActions() diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h index a03d566d0..cd9f238aa 100644 --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -49,6 +49,7 @@ class KToolBarPopupAction; class QToolButton; class PlacesPanel; class TerminalPanel; +class ServiceMenuShortcutManager; /** Used to identify that a custom command should be triggered on a view background double-click.*/ constexpr QLatin1String customCommand{"CUSTOM_COMMAND"}; @@ -801,6 +802,7 @@ private: QMenu m_searchTools; KConfigWatcher::Ptr m_serviceMenuConfigWatcher; KFileItemActions *m_fileItemActions = nullptr; + ServiceMenuShortcutManager *m_serviceMenuShortcutManager = nullptr; QTimer *m_sessionSaveTimer; QFutureWatcher *m_sessionSaveWatcher; diff --git a/src/servicemenushortcutmanager.cpp b/src/servicemenushortcutmanager.cpp new file mode 100644 index 000000000..ea13fc5f8 --- /dev/null +++ b/src/servicemenushortcutmanager.cpp @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2026 Albert Mkhitaryan + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static constexpr QLatin1String serviceMenuNamePrefix("servicemenu_"); + +ServiceMenuShortcutManager::ServiceMenuShortcutManager(KActionCollection *actionCollection, QObject *parent) + : QObject(parent) + , m_actionCollection(actionCollection) + , m_category(new KActionCategory(i18nc("@title:group", "Context Menu Actions"), actionCollection)) +{ +} + +void ServiceMenuShortcutManager::refresh(KFileItemActions *fileItemActions) +{ + // Uses takeAction() to remove without deleting. Actions are owned by KFileItemActions + // and are deleted when called createServiceMenuActions() or destroyed + for (const QString &name : std::as_const(m_actionNames)) { + m_actionCollection->takeAction(m_actionCollection->action(name)); + } + m_actionNames.clear(); + + // Get action->execution mapping from KIO + const QList actions = fileItemActions->createServiceMenuActions(); + + for (QAction *action : actions) { + const auto desktopAction = action->data().value(); + const QString fileName = QFileInfo(desktopAction.desktopFilePath()).fileName(); + const QString name = serviceMenuNamePrefix + fileName + QLatin1String("::") + desktopAction.actionsKey(); + m_category->addAction(name, action); + m_actionNames.append(name); + } +} + +void ServiceMenuShortcutManager::cleanupStaleShortcuts(KXMLGUIClient *client) +{ + const QString file = client->localXMLFile(); + if (file.isEmpty()) { + return; + } + + const QString xml = KXMLGUIFactory::readConfigFile(file, client->componentName()); + if (xml.isEmpty()) { + return; + } + + QDomDocument doc; + doc.setContent(xml); + QDomElement actionPropElement = KXMLGUIFactory::actionPropertiesElement(doc); + + bool modified = false; + for (QDomElement e = actionPropElement.firstChildElement(); !e.isNull();) { + QDomElement next = e.nextSiblingElement(); + const QString name = e.attribute(QStringLiteral("name")); + if (name.startsWith(serviceMenuNamePrefix) && !m_actionNames.contains(name)) { + actionPropElement.removeChild(e); + modified = true; + } + e = next; + } + + if (modified) { + KXMLGUIFactory::saveConfigFile(doc, file, client->componentName()); + } +} \ No newline at end of file diff --git a/src/servicemenushortcutmanager.h b/src/servicemenushortcutmanager.h new file mode 100644 index 000000000..10502c341 --- /dev/null +++ b/src/servicemenushortcutmanager.h @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2026 Albert Mkhitaryan + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SERVICEMENUSHORTCUTMANAGER_H +#define SERVICEMENUSHORTCUTMANAGER_H + +#include +#include + +class KActionCategory; +class KActionCollection; +class KFileItemActions; +class KXMLGUIClient; + +/** + * @brief Registers service menu actions with KActionCollection for keyboard shortcuts. + * + * This lightweight manager bridges KIO's service menu actions with Dolphin's + * shortcut system. Actions are obtained from KFileItemActions and registered + * with the action collection. Execution is handled entirely by KFileItemActions. + * + */ +class ServiceMenuShortcutManager : public QObject +{ + Q_OBJECT + +public: + explicit ServiceMenuShortcutManager(KActionCollection *actionCollection, QObject *parent = nullptr); + + void refresh(KFileItemActions *fileItemaActions); + void cleanupStaleShortcuts(KXMLGUIClient *client); + +private: + KActionCollection *m_actionCollection; + KActionCategory *m_category; + QStringList m_actionNames; +}; + +#endif // SERVICEMENUSHORTCUTMANAGER_H -- cgit v1.3