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 | |
31 | KShortcutSchemesEditor::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 | |
79 | void 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 | |
111 | void 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 | |
148 | void KShortcutSchemesEditor::deleteScheme() |
149 | { |
150 | if (KMessageBox::questionTwoActions(parent: this, |
151 | i18n("Do you really want to delete the scheme %1?\n\ |
152 | Note 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 | |
179 | QString KShortcutSchemesEditor::currentScheme() |
180 | { |
181 | return m_schemesList->currentText(); |
182 | } |
183 | |
184 | void 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 | |
195 | void 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 | |
206 | void 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 | |
216 | void KShortcutSchemesEditor::updateDeleteButton() |
217 | { |
218 | m_deleteScheme->setEnabled(m_schemesList->count() >= 1); |
219 | } |
220 | |
221 | void KShortcutSchemesEditor::(QAction *action) |
222 | { |
223 | m_moreActionsMenu->addAction(action); |
224 | } |
225 | |