File indexing completed on 2024-03-24 15:40:47

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2008 Alexander Dymo <adymo@kdevelop.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kshortcutschemeshelper_p.h"
0009 
0010 #include <QAction>
0011 #include <QCoreApplication>
0012 #include <QDomDocument>
0013 #include <QFile>
0014 #include <QStandardPaths>
0015 #include <QTextStream>
0016 
0017 #include <KConfigGroup>
0018 #include <KSharedConfig>
0019 #include <QDir>
0020 
0021 #include "debug.h"
0022 #include "kactioncollection.h"
0023 #include "kxmlguiclient.h"
0024 
0025 bool KShortcutSchemesHelper::saveShortcutScheme(const QList<KActionCollection *> &collections, const QString &schemeName)
0026 {
0027     // Every action collection is associated with a KXMLGUIClient
0028     // (at least if it was added by KXMLGUIFactory::configureShortcuts()
0029     // or KXMLGUIFactory::showConfigureShortcutsDialog())
0030 
0031     // Some GUI clients have the same name (e.g. the child client for a mainwindow
0032     // holding the actions for hiding/showing toolbars), so we need to save them
0033     // together, otherwise they will overwrite each other's files on disk.
0034 
0035     // For cases like kdevelop (many guiclients not reused in other apps) it's simpler
0036     // to even save all shortcuts to a single shortcuts file -> set the boolean below to true
0037     // to create an all-in-one scheme.
0038     // Maybe we need a checkbox for this? Or an env var for contributors to set, rather? End users don't care.
0039     const bool saveToApplicationFile = false;
0040 
0041     QMultiMap<QString, KActionCollection *> collectionsByClientName;
0042     for (KActionCollection *coll : collections) {
0043         const KXMLGUIClient *client = coll->parentGUIClient();
0044         if (client) {
0045             const QString key = saveToApplicationFile ? QCoreApplication::applicationName() : client->componentName();
0046             collectionsByClientName.insert(key, coll);
0047         }
0048     }
0049     const auto componentNames = collectionsByClientName.uniqueKeys();
0050     for (const QString &componentName : componentNames) {
0051         qCDebug(DEBUG_KXMLGUI) << "Considering component" << componentName;
0052         QDomDocument doc;
0053         QDomElement docElem = doc.createElement(QStringLiteral("gui"));
0054         docElem.setAttribute(QStringLiteral("version"), QStringLiteral("1"));
0055         docElem.setAttribute(QStringLiteral("name"), componentName);
0056         doc.appendChild(docElem);
0057         QDomElement elem = doc.createElement(QStringLiteral("ActionProperties"));
0058         docElem.appendChild(elem);
0059 
0060         const auto componentCollections = collectionsByClientName.values(componentName);
0061         for (KActionCollection *collection : componentCollections) {
0062             qCDebug(DEBUG_KXMLGUI) << "Saving shortcut scheme for action collection with" << collection->actions().count() << "actions";
0063 
0064             const auto collectionActions = collection->actions();
0065             for (QAction *action : collectionActions) {
0066                 if (!action) {
0067                     continue;
0068                 }
0069 
0070                 const QString actionName = action->objectName();
0071                 const QString shortcut = QKeySequence::listToString(action->shortcuts());
0072                 // qCDebug(DEBUG_KXMLGUI) << "action" << actionName << "has shortcut" << shortcut;
0073                 if (!shortcut.isEmpty()) {
0074                     QDomElement act_elem = doc.createElement(QStringLiteral("Action"));
0075                     act_elem.setAttribute(QStringLiteral("name"), actionName);
0076                     act_elem.setAttribute(QStringLiteral("shortcut"), shortcut);
0077                     elem.appendChild(act_elem);
0078                 }
0079             }
0080         }
0081 
0082         const QString schemeFileName = writableShortcutSchemeFileName(componentName, schemeName);
0083         if (elem.childNodes().isEmpty()) {
0084             QFile::remove(schemeFileName);
0085         } else {
0086             qCDebug(DEBUG_KXMLGUI) << "saving to" << schemeFileName;
0087             QDir().mkpath(QFileInfo(schemeFileName).absolutePath());
0088             QFile schemeFile(schemeFileName);
0089             if (!schemeFile.open(QFile::WriteOnly | QFile::Truncate)) {
0090                 qCDebug(DEBUG_KXMLGUI) << "COULD NOT WRITE" << schemeFileName;
0091                 return false;
0092             }
0093 
0094             QTextStream out(&schemeFile);
0095             out << doc.toString(2);
0096         }
0097     }
0098     return true;
0099 }
0100 
0101 QString KShortcutSchemesHelper::currentShortcutSchemeName()
0102 {
0103     return KSharedConfig::openConfig()->group("Shortcut Schemes").readEntry("Current Scheme", "Default");
0104 }
0105 
0106 QString KShortcutSchemesHelper::writableShortcutSchemeFileName(const QString &componentName, const QString &schemeName)
0107 {
0108     return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/%1/shortcuts/%2").arg(componentName, schemeName);
0109 }
0110 
0111 QString KShortcutSchemesHelper::writableApplicationShortcutSchemeFileName(const QString &schemeName)
0112 {
0113     return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
0114         + QLatin1String("/%1/shortcuts/%2").arg(QCoreApplication::applicationName(), schemeName);
0115 }
0116 
0117 QString KShortcutSchemesHelper::shortcutSchemeFileName(const QString &componentName, const QString &schemeName)
0118 {
0119     return QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("%1/shortcuts/%2").arg(componentName, schemeName));
0120 }
0121 
0122 QString KShortcutSchemesHelper::applicationShortcutSchemeFileName(const QString &schemeName)
0123 {
0124     return QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("%1/shortcuts/%2").arg(QCoreApplication::applicationName(), schemeName));
0125 }