File indexing completed on 2024-04-14 03:57:11

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 #include "kshortcutsdialog_p.h"
0008 
0009 #include <QComboBox>
0010 #include <QDir>
0011 #include <QDomDocument>
0012 #include <QFile>
0013 #include <QFileDialog>
0014 #include <QInputDialog>
0015 #include <QLabel>
0016 #include <QMenu>
0017 #include <QPushButton>
0018 #include <QStandardPaths>
0019 #include <QTextStream>
0020 
0021 #include <KConfigGroup>
0022 #include <KMessageBox>
0023 #include <KSharedConfig>
0024 
0025 #include "kactioncollection.h"
0026 #include "kshortcutschemeshelper_p.h"
0027 #include "kshortcutsdialog.h"
0028 #include "kxmlguiclient.h"
0029 #include <debug.h>
0030 
0031 KShortcutSchemesEditor::KShortcutSchemesEditor(KShortcutsDialog *parent)
0032     : QGroupBox(i18nc("@title:group", "Shortcut Schemes"), parent)
0033     , m_dialog(parent)
0034 {
0035     QHBoxLayout *l = new QHBoxLayout(this);
0036 
0037     QLabel *schemesLabel = new QLabel(i18n("Current scheme:"), this);
0038     l->addWidget(schemesLabel);
0039 
0040     m_schemesList = new QComboBox(this);
0041     m_schemesList->setEditable(false);
0042     refreshSchemes();
0043     m_schemesList->setSizeAdjustPolicy(QComboBox::AdjustToContents);
0044     schemesLabel->setBuddy(m_schemesList);
0045     l->addWidget(m_schemesList);
0046 
0047     m_newScheme = new QPushButton(QIcon::fromTheme(QStringLiteral("document-new")), i18nc("@action:button", "New..."));
0048     l->addWidget(m_newScheme);
0049 
0050     m_deleteScheme = new QPushButton(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action:button", "Delete"));
0051     l->addWidget(m_deleteScheme);
0052 
0053     QPushButton *moreActions = new QPushButton(QIcon::fromTheme(QStringLiteral("view-more-symbolic")), i18nc("@action:button", "More Actions"));
0054     l->addWidget(moreActions);
0055 
0056     m_moreActionsMenu = new QMenu(this);
0057     m_moreActionsMenu->addAction(QIcon::fromTheme(QStringLiteral("document-save")),
0058                                  i18nc("@action:inmenu", "Save shortcuts to scheme"),
0059                                  this,
0060                                  &KShortcutSchemesEditor::saveAsDefaultsForScheme);
0061     m_moreActionsMenu->addAction(QIcon::fromTheme(QStringLiteral("document-export")),
0062                                  i18nc("@action:inmenu", "Export Scheme..."),
0063                                  this,
0064                                  &KShortcutSchemesEditor::exportShortcutsScheme);
0065     m_moreActionsMenu->addAction(QIcon::fromTheme(QStringLiteral("document-import")),
0066                                  i18nc("@action:inmenu", "Import Scheme..."),
0067                                  this,
0068                                  &KShortcutSchemesEditor::importShortcutsScheme);
0069     moreActions->setMenu(m_moreActionsMenu);
0070 
0071     l->addStretch(1);
0072 
0073     connect(m_schemesList, &QComboBox::textActivated, this, &KShortcutSchemesEditor::shortcutsSchemeChanged);
0074     connect(m_newScheme, &QPushButton::clicked, this, &KShortcutSchemesEditor::newScheme);
0075     connect(m_deleteScheme, &QPushButton::clicked, this, &KShortcutSchemesEditor::deleteScheme);
0076     updateDeleteButton();
0077 }
0078 
0079 void KShortcutSchemesEditor::refreshSchemes()
0080 {
0081     QStringList schemes;
0082     schemes << QStringLiteral("Default");
0083     // List files in the shortcuts subdir, each one is a scheme. See KShortcutSchemesHelper::{shortcutSchemeFileName,exportActionCollection}
0084     const QStringList shortcutsDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
0085                                                                 QCoreApplication::applicationName() + QLatin1String("/shortcuts"),
0086                                                                 QStandardPaths::LocateDirectory);
0087     qCDebug(DEBUG_KXMLGUI) << "shortcut scheme dirs:" << shortcutsDirs;
0088     for (const QString &dir : shortcutsDirs) {
0089         const auto files = QDir(dir).entryList(QDir::Files | QDir::NoDotAndDotDot);
0090         for (const QString &file : files) {
0091             qCDebug(DEBUG_KXMLGUI) << "shortcut scheme file:" << file;
0092             schemes << file;
0093         }
0094     }
0095 
0096     m_schemesList->clear();
0097     m_schemesList->addItems(schemes);
0098 
0099     KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("Shortcut Schemes"));
0100     const QString currentScheme = group.readEntry("Current Scheme", "Default");
0101     qCDebug(DEBUG_KXMLGUI) << "Current Scheme" << currentScheme;
0102 
0103     const int schemeIdx = m_schemesList->findText(currentScheme);
0104     if (schemeIdx > -1) {
0105         m_schemesList->setCurrentIndex(schemeIdx);
0106     } else {
0107         qCWarning(DEBUG_KXMLGUI) << "Current scheme" << currentScheme << "not found in" << shortcutsDirs;
0108     }
0109 }
0110 
0111 void KShortcutSchemesEditor::newScheme()
0112 {
0113     bool ok;
0114     const QString newName =
0115         QInputDialog::getText(this, i18nc("@title:window", "Name for New Scheme"), i18n("Name for new scheme:"), QLineEdit::Normal, i18n("New Scheme"), &ok);
0116     if (!ok) {
0117         return;
0118     }
0119 
0120     if (m_schemesList->findText(newName) != -1) {
0121         KMessageBox::error(this, i18n("A scheme with this name already exists."));
0122         return;
0123     }
0124 
0125     const QString newSchemeFileName = KShortcutSchemesHelper::writableApplicationShortcutSchemeFileName(newName);
0126     QDir().mkpath(QFileInfo(newSchemeFileName).absolutePath());
0127     QFile schemeFile(newSchemeFileName);
0128     if (!schemeFile.open(QFile::WriteOnly | QFile::Truncate)) {
0129         qCWarning(DEBUG_KXMLGUI) << "Couldn't write to" << newSchemeFileName;
0130         return;
0131     }
0132 
0133     QDomDocument doc;
0134     QDomElement docElem = doc.createElement(QStringLiteral("gui"));
0135     doc.appendChild(docElem);
0136     QDomElement elem = doc.createElement(QStringLiteral("ActionProperties"));
0137     docElem.appendChild(elem);
0138 
0139     QTextStream out(&schemeFile);
0140     out << doc.toString(4);
0141 
0142     m_schemesList->addItem(newName);
0143     m_schemesList->setCurrentIndex(m_schemesList->findText(newName));
0144     updateDeleteButton();
0145     Q_EMIT shortcutsSchemeChanged(newName);
0146 }
0147 
0148 void KShortcutSchemesEditor::deleteScheme()
0149 {
0150     if (KMessageBox::questionTwoActions(this,
0151                                         i18n("Do you really want to delete the scheme %1?\n\
0152 Note that this will not remove any system wide shortcut schemes.",
0153                                              currentScheme()),
0154                                         QString(),
0155                                         KStandardGuiItem::del(),
0156                                         KStandardGuiItem::cancel())
0157         == KMessageBox::SecondaryAction) {
0158         return;
0159     }
0160 
0161     // delete the scheme for the app itself
0162     QFile::remove(KShortcutSchemesHelper::writableApplicationShortcutSchemeFileName(currentScheme()));
0163 
0164     // delete all scheme files we can find for xmlguiclients in the user directories
0165     const auto dialogCollections = m_dialog->actionCollections();
0166     for (KActionCollection *collection : dialogCollections) {
0167         const KXMLGUIClient *client = collection->parentGUIClient();
0168         if (!client) {
0169             continue;
0170         }
0171         QFile::remove(KShortcutSchemesHelper::writableShortcutSchemeFileName(client->componentName(), currentScheme()));
0172     }
0173 
0174     m_schemesList->removeItem(m_schemesList->findText(currentScheme()));
0175     updateDeleteButton();
0176     Q_EMIT shortcutsSchemeChanged(currentScheme());
0177 }
0178 
0179 QString KShortcutSchemesEditor::currentScheme()
0180 {
0181     return m_schemesList->currentText();
0182 }
0183 
0184 void KShortcutSchemesEditor::exportShortcutsScheme()
0185 {
0186     // ask user about dir
0187     QString path = QFileDialog::getSaveFileName(this, i18nc("@title:window", "Export Shortcuts"), QDir::currentPath(), i18n("Shortcuts (*.shortcuts)"));
0188     if (path.isEmpty()) {
0189         return;
0190     }
0191 
0192     m_dialog->exportConfiguration(path);
0193 }
0194 
0195 void KShortcutSchemesEditor::importShortcutsScheme()
0196 {
0197     // ask user about dir
0198     QString path = QFileDialog::getOpenFileName(this, i18nc("@title:window", "Import Shortcuts"), QDir::currentPath(), i18n("Shortcuts (*.shortcuts)"));
0199     if (path.isEmpty()) {
0200         return;
0201     }
0202 
0203     m_dialog->importConfiguration(path);
0204 }
0205 
0206 void KShortcutSchemesEditor::saveAsDefaultsForScheme()
0207 {
0208     if (KShortcutSchemesHelper::saveShortcutScheme(m_dialog->actionCollections(), currentScheme())) {
0209         KMessageBox::information(this, i18n("Shortcut scheme successfully saved."));
0210     } else {
0211         // We'd need to return to return more than a bool, to show more details here.
0212         KMessageBox::error(this, i18n("Error saving the shortcut scheme."));
0213     }
0214 }
0215 
0216 void KShortcutSchemesEditor::updateDeleteButton()
0217 {
0218     m_deleteScheme->setEnabled(m_schemesList->count() >= 1);
0219 }
0220 
0221 void KShortcutSchemesEditor::addMoreMenuAction(QAction *action)
0222 {
0223     m_moreActionsMenu->addAction(action);
0224 }