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#include "kshortcutsdialog_p.h"
8
9#include <QComboBox>
10#include <QDir>
11#include <QDomDocument>
12#include <QFile>
13#include <QFileDialog>
14#include <QInputDialog>
15#include <QLabel>
16#include <QMenu>
17#include <QPushButton>
18#include <QStandardPaths>
19#include <QTextStream>
20
21#include <KConfigGroup>
22#include <KMessageBox>
23#include <KSharedConfig>
24
25#include "kactioncollection.h"
26#include "kshortcutschemeshelper_p.h"
27#include "kshortcutsdialog.h"
28#include "kxmlguiclient.h"
29#include <debug.h>
30
31KShortcutSchemesEditor::KShortcutSchemesEditor(KShortcutsDialog *parent)
32 : QGroupBox(i18nc("@title:group", "Shortcut Schemes"), parent)
33 , m_dialog(parent)
34{
35 QHBoxLayout *l = new QHBoxLayout(this);
36
37 QLabel *schemesLabel = new QLabel(i18n("Current scheme:"), this);
38 l->addWidget(schemesLabel);
39
40 m_schemesList = new QComboBox(this);
41 m_schemesList->setEditable(false);
42 refreshSchemes();
43 m_schemesList->setSizeAdjustPolicy(QComboBox::AdjustToContents);
44 schemesLabel->setBuddy(m_schemesList);
45 l->addWidget(m_schemesList);
46
47 m_newScheme = new QPushButton(QIcon::fromTheme(QStringLiteral("document-new")), i18nc("@action:button", "New..."));
48 l->addWidget(m_newScheme);
49
50 m_deleteScheme = new QPushButton(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action:button", "Delete"));
51 l->addWidget(m_deleteScheme);
52
53 QPushButton *moreActions = new QPushButton(QIcon::fromTheme(QStringLiteral("view-more-symbolic")), i18nc("@action:button", "More Actions"));
54 l->addWidget(moreActions);
55
56 m_moreActionsMenu = new QMenu(this);
57 m_moreActionsMenu->addAction(icon: QIcon::fromTheme(QStringLiteral("document-save")),
58 i18nc("@action:inmenu", "Save shortcuts to scheme"),
59 args: this,
60 args: &KShortcutSchemesEditor::saveAsDefaultsForScheme);
61 m_moreActionsMenu->addAction(icon: QIcon::fromTheme(QStringLiteral("document-export")),
62 i18nc("@action:inmenu", "Export Scheme..."),
63 args: this,
64 args: &KShortcutSchemesEditor::exportShortcutsScheme);
65 m_moreActionsMenu->addAction(icon: QIcon::fromTheme(QStringLiteral("document-import")),
66 i18nc("@action:inmenu", "Import Scheme..."),
67 args: this,
68 args: &KShortcutSchemesEditor::importShortcutsScheme);
69 moreActions->setMenu(m_moreActionsMenu);
70
71 l->addStretch(stretch: 1);
72
73 connect(sender: m_schemesList, signal: &QComboBox::textActivated, context: this, slot: &KShortcutSchemesEditor::shortcutsSchemeChanged);
74 connect(sender: m_newScheme, signal: &QPushButton::clicked, context: this, slot: &KShortcutSchemesEditor::newScheme);
75 connect(sender: m_deleteScheme, signal: &QPushButton::clicked, context: this, slot: &KShortcutSchemesEditor::deleteScheme);
76 updateDeleteButton();
77}
78
79void KShortcutSchemesEditor::refreshSchemes()
80{
81 QStringList schemes;
82 schemes << QStringLiteral("Default");
83 // List files in the shortcuts subdir, each one is a scheme. See KShortcutSchemesHelper::{shortcutSchemeFileName,exportActionCollection}
84 const QStringList shortcutsDirs = QStandardPaths::locateAll(type: QStandardPaths::GenericDataLocation,
85 fileName: QCoreApplication::applicationName() + QLatin1String("/shortcuts"),
86 options: QStandardPaths::LocateDirectory);
87 qCDebug(DEBUG_KXMLGUI) << "shortcut scheme dirs:" << shortcutsDirs;
88 for (const QString &dir : shortcutsDirs) {
89 const auto files = QDir(dir).entryList(filters: QDir::Files | QDir::NoDotAndDotDot);
90 for (const QString &file : files) {
91 qCDebug(DEBUG_KXMLGUI) << "shortcut scheme file:" << file;
92 schemes << file;
93 }
94 }
95
96 m_schemesList->clear();
97 m_schemesList->addItems(texts: schemes);
98
99 KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("Shortcut Schemes"));
100 const QString currentScheme = group.readEntry(key: "Current Scheme", aDefault: "Default");
101 qCDebug(DEBUG_KXMLGUI) << "Current Scheme" << currentScheme;
102
103 const int schemeIdx = m_schemesList->findText(text: currentScheme);
104 if (schemeIdx > -1) {
105 m_schemesList->setCurrentIndex(schemeIdx);
106 } else {
107 qCWarning(DEBUG_KXMLGUI) << "Current scheme" << currentScheme << "not found in" << shortcutsDirs;
108 }
109}
110
111void KShortcutSchemesEditor::newScheme()
112{
113 bool ok;
114 const QString newName =
115 QInputDialog::getText(parent: this, i18nc("@title:window", "Name for New Scheme"), i18n("Name for new scheme:"), echo: QLineEdit::Normal, i18n("New Scheme"), ok: &ok);
116 if (!ok) {
117 return;
118 }
119
120 if (m_schemesList->findText(text: newName) != -1) {
121 KMessageBox::error(parent: this, i18n("A scheme with this name already exists."));
122 return;
123 }
124
125 const QString newSchemeFileName = KShortcutSchemesHelper::writableApplicationShortcutSchemeFileName(schemeName: newName);
126 QDir().mkpath(dirPath: QFileInfo(newSchemeFileName).absolutePath());
127 QFile schemeFile(newSchemeFileName);
128 if (!schemeFile.open(flags: QFile::WriteOnly | QFile::Truncate)) {
129 qCWarning(DEBUG_KXMLGUI) << "Couldn't write to" << newSchemeFileName;
130 return;
131 }
132
133 QDomDocument doc;
134 QDomElement docElem = doc.createElement(QStringLiteral("gui"));
135 doc.appendChild(newChild: docElem);
136 QDomElement elem = doc.createElement(QStringLiteral("ActionProperties"));
137 docElem.appendChild(newChild: elem);
138
139 QTextStream out(&schemeFile);
140 out << doc.toString(4);
141
142 m_schemesList->addItem(atext: newName);
143 m_schemesList->setCurrentIndex(m_schemesList->findText(text: newName));
144 updateDeleteButton();
145 Q_EMIT shortcutsSchemeChanged(newName);
146}
147
148void KShortcutSchemesEditor::deleteScheme()
149{
150 if (KMessageBox::questionTwoActions(parent: this,
151 i18n("Do you really want to delete the scheme %1?\n\
152Note that this will not remove any system wide shortcut schemes.",
153 currentScheme()),
154 title: QString(),
155 primaryAction: KStandardGuiItem::del(),
156 secondaryAction: KStandardGuiItem::cancel())
157 == KMessageBox::SecondaryAction) {
158 return;
159 }
160
161 // delete the scheme for the app itself
162 QFile::remove(fileName: KShortcutSchemesHelper::writableApplicationShortcutSchemeFileName(schemeName: currentScheme()));
163
164 // delete all scheme files we can find for xmlguiclients in the user directories
165 const auto dialogCollections = m_dialog->actionCollections();
166 for (KActionCollection *collection : dialogCollections) {
167 const KXMLGUIClient *client = collection->parentGUIClient();
168 if (!client) {
169 continue;
170 }
171 QFile::remove(fileName: KShortcutSchemesHelper::writableShortcutSchemeFileName(componentName: client->componentName(), schemeName: currentScheme()));
172 }
173
174 m_schemesList->removeItem(index: m_schemesList->findText(text: currentScheme()));
175 updateDeleteButton();
176 Q_EMIT shortcutsSchemeChanged(currentScheme());
177}
178
179QString KShortcutSchemesEditor::currentScheme()
180{
181 return m_schemesList->currentText();
182}
183
184void KShortcutSchemesEditor::exportShortcutsScheme()
185{
186 // ask user about dir
187 QString path = QFileDialog::getSaveFileName(parent: this, i18nc("@title:window", "Export Shortcuts"), dir: QDir::currentPath(), i18n("Shortcuts (*.shortcuts)"));
188 if (path.isEmpty()) {
189 return;
190 }
191
192 m_dialog->exportConfiguration(path);
193}
194
195void KShortcutSchemesEditor::importShortcutsScheme()
196{
197 // ask user about dir
198 QString path = QFileDialog::getOpenFileName(parent: this, i18nc("@title:window", "Import Shortcuts"), dir: QDir::currentPath(), i18n("Shortcuts (*.shortcuts)"));
199 if (path.isEmpty()) {
200 return;
201 }
202
203 m_dialog->importConfiguration(path);
204}
205
206void KShortcutSchemesEditor::saveAsDefaultsForScheme()
207{
208 if (KShortcutSchemesHelper::saveShortcutScheme(collections: m_dialog->actionCollections(), schemeName: currentScheme())) {
209 KMessageBox::information(parent: this, i18n("Shortcut scheme successfully saved."));
210 } else {
211 // We'd need to return to return more than a bool, to show more details here.
212 KMessageBox::error(parent: this, i18n("Error saving the shortcut scheme."));
213 }
214}
215
216void KShortcutSchemesEditor::updateDeleteButton()
217{
218 m_deleteScheme->setEnabled(m_schemesList->count() >= 1);
219}
220
221void KShortcutSchemesEditor::addMoreMenuAction(QAction *action)
222{
223 m_moreActionsMenu->addAction(action);
224}
225

source code of kxmlgui/src/kshortcutschemeseditor.cpp