1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2008 Alexander Dymo <adymo@kdevelop.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kshortcutschemeshelper_p.h"
9
10#include <QAction>
11#include <QCoreApplication>
12#include <QDomDocument>
13#include <QFile>
14#include <QStandardPaths>
15#include <QTextStream>
16
17#include <KConfigGroup>
18#include <KSharedConfig>
19#include <QDir>
20
21#include "debug.h"
22#include "kactioncollection.h"
23#include "kxmlguiclient.h"
24
25bool KShortcutSchemesHelper::saveShortcutScheme(const QList<KActionCollection *> &collections, const QString &schemeName)
26{
27 // Every action collection is associated with a KXMLGUIClient
28 // (at least if it was added by KXMLGUIFactory::configureShortcuts()
29 // or KXMLGUIFactory::showConfigureShortcutsDialog())
30
31 // Some GUI clients have the same name (e.g. the child client for a mainwindow
32 // holding the actions for hiding/showing toolbars), so we need to save them
33 // together, otherwise they will overwrite each other's files on disk.
34
35 // For cases like kdevelop (many guiclients not reused in other apps) it's simpler
36 // to even save all shortcuts to a single shortcuts file -> set the boolean below to true
37 // to create an all-in-one scheme.
38 // Maybe we need a checkbox for this? Or an env var for contributors to set, rather? End users don't care.
39 const bool saveToApplicationFile = false;
40
41 QMultiMap<QString, KActionCollection *> collectionsByClientName;
42 for (KActionCollection *coll : collections) {
43 const KXMLGUIClient *client = coll->parentGUIClient();
44 if (client) {
45 const QString key = saveToApplicationFile ? QCoreApplication::applicationName() : client->componentName();
46 collectionsByClientName.insert(key, value: coll);
47 }
48 }
49 const auto componentNames = collectionsByClientName.uniqueKeys();
50 for (const QString &componentName : componentNames) {
51 qCDebug(DEBUG_KXMLGUI) << "Considering component" << componentName;
52 QDomDocument doc;
53 QDomElement docElem = doc.createElement(QStringLiteral("gui"));
54 docElem.setAttribute(QStringLiteral("version"), QStringLiteral("1"));
55 docElem.setAttribute(QStringLiteral("name"), value: componentName);
56 doc.appendChild(newChild: docElem);
57 QDomElement elem = doc.createElement(QStringLiteral("ActionProperties"));
58 docElem.appendChild(newChild: elem);
59
60 const auto componentCollections = collectionsByClientName.values(key: componentName);
61 for (KActionCollection *collection : componentCollections) {
62 qCDebug(DEBUG_KXMLGUI) << "Saving shortcut scheme for action collection with" << collection->actions().count() << "actions";
63
64 const auto collectionActions = collection->actions();
65 for (QAction *action : collectionActions) {
66 if (!action) {
67 continue;
68 }
69
70 const QString actionName = action->objectName();
71 const QString shortcut = QKeySequence::listToString(list: action->shortcuts());
72 // qCDebug(DEBUG_KXMLGUI) << "action" << actionName << "has shortcut" << shortcut;
73 if (!shortcut.isEmpty()) {
74 QDomElement act_elem = doc.createElement(QStringLiteral("Action"));
75 act_elem.setAttribute(QStringLiteral("name"), value: actionName);
76 act_elem.setAttribute(QStringLiteral("shortcut"), value: shortcut);
77 elem.appendChild(newChild: act_elem);
78 }
79 }
80 }
81
82 const QString schemeFileName = writableShortcutSchemeFileName(componentName, schemeName);
83 if (elem.childNodes().isEmpty()) {
84 QFile::remove(fileName: schemeFileName);
85 } else {
86 qCDebug(DEBUG_KXMLGUI) << "saving to" << schemeFileName;
87 QDir().mkpath(dirPath: QFileInfo(schemeFileName).absolutePath());
88 QFile schemeFile(schemeFileName);
89 if (!schemeFile.open(flags: QFile::WriteOnly | QFile::Truncate)) {
90 qCDebug(DEBUG_KXMLGUI) << "COULD NOT WRITE" << schemeFileName;
91 return false;
92 }
93
94 QTextStream out(&schemeFile);
95 out << doc.toString(2);
96 }
97 }
98 return true;
99}
100
101QString KShortcutSchemesHelper::currentShortcutSchemeName()
102{
103 return KSharedConfig::openConfig()->group(QStringLiteral("Shortcut Schemes")).readEntry(key: "Current Scheme", aDefault: "Default");
104}
105
106QString KShortcutSchemesHelper::writableShortcutSchemeFileName(const QString &componentName, const QString &schemeName)
107{
108 return QStandardPaths::writableLocation(type: QStandardPaths::GenericDataLocation) + QLatin1String("/%1/shortcuts/%2").arg(args: componentName, args: schemeName);
109}
110
111QString KShortcutSchemesHelper::writableApplicationShortcutSchemeFileName(const QString &schemeName)
112{
113 return QStandardPaths::writableLocation(type: QStandardPaths::GenericDataLocation)
114 + QLatin1String("/%1/shortcuts/%2").arg(args: QCoreApplication::applicationName(), args: schemeName);
115}
116
117QString KShortcutSchemesHelper::shortcutSchemeFileName(const QString &componentName, const QString &schemeName)
118{
119 return QStandardPaths::locate(type: QStandardPaths::GenericDataLocation, fileName: QLatin1String("%1/shortcuts/%2").arg(args: componentName, args: schemeName));
120}
121
122QString KShortcutSchemesHelper::applicationShortcutSchemeFileName(const QString &schemeName)
123{
124 return QStandardPaths::locate(type: QStandardPaths::GenericDataLocation, fileName: QLatin1String("%1/shortcuts/%2").arg(args: QCoreApplication::applicationName(), args: schemeName));
125}
126

source code of kxmlgui/src/kshortcutschemeshelper.cpp