1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1998 Mark Donohoe <donohoe@kde.org>
4 SPDX-FileCopyrightText: 1997 Nicolas Hadacek <hadacek@kde.org>
5 SPDX-FileCopyrightText: 1998 Matthias Ettrich <ettrich@kde.org>
6 SPDX-FileCopyrightText: 2001 Ellis Whitehead <ellis@kde.org>
7 SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
8 SPDX-FileCopyrightText: 2007 Roberto Raggi <roberto@kdevelop.org>
9 SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
10 SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
11 SPDX-FileCopyrightText: 2008 Alexander Dymo <adymo@kdevelop.org>
12 SPDX-FileCopyrightText: 2009 Chani Armitage <chani@kde.org>
13
14 SPDX-License-Identifier: LGPL-2.0-or-later
15*/
16
17#include "kshortcutsdialog.h"
18#include "kshortcutschemeshelper_p.h"
19#include "kshortcutsdialog_p.h"
20
21#include <QApplication>
22#include <QDialogButtonBox>
23#include <QDomDocument>
24
25#include <KConfigGroup>
26#include <KLocalizedString>
27#include <KMessageBox>
28#include <KSharedConfig>
29
30#include "kactioncollection.h"
31#include "kxmlguiclient.h"
32#include "kxmlguifactory.h"
33
34/************************************************************************/
35/* KShortcutsDialog */
36/* */
37/* Originally by Nicolas Hadacek <hadacek@via.ecp.fr> */
38/* */
39/* Substantially revised by Mark Donohoe <donohoe@kde.org> */
40/* */
41/* And by Espen Sand <espen@kde.org> 1999-10-19 */
42/* (by using KDialog there is almost no code left ;) */
43/* */
44/************************************************************************/
45
46QKeySequence primarySequence(const QList<QKeySequence> &sequences)
47{
48 return sequences.isEmpty() ? QKeySequence() : sequences.at(i: 0);
49}
50
51QKeySequence alternateSequence(const QList<QKeySequence> &sequences)
52{
53 return sequences.size() <= 1 ? QKeySequence() : sequences.at(i: 1);
54}
55
56class KShortcutsDialogPrivate
57{
58public:
59 KShortcutsDialogPrivate(KShortcutsDialog *qq)
60 : q(qq)
61 {
62 }
63
64 QList<KActionCollection *> m_collections;
65
66 void changeShortcutScheme(const QString &scheme)
67 {
68 if (m_keyChooser->isModified()
69 && KMessageBox::questionTwoActions(parent: q,
70 i18n("The current shortcut scheme is modified. Save before switching to the new one?"),
71 title: QString(),
72 primaryAction: KStandardGuiItem::save(),
73 secondaryAction: KStandardGuiItem::discard())
74 == KMessageBox::PrimaryAction) {
75 m_keyChooser->save();
76 } else {
77 m_keyChooser->undo();
78 }
79
80 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
81 m_keyChooser->clearCollections();
82
83 for (KActionCollection *collection : std::as_const(t&: m_collections)) {
84 // passing an empty stream forces the clients to reread the XML
85 KXMLGUIClient *client = const_cast<KXMLGUIClient *>(collection->parentGUIClient());
86 if (client) {
87 client->setXMLGUIBuildDocument(QDomDocument());
88 }
89 }
90
91 // get xmlguifactory
92 if (!m_collections.isEmpty()) {
93 const KXMLGUIClient *client = m_collections.first()->parentGUIClient();
94 if (client) {
95 KXMLGUIFactory *factory = client->factory();
96 if (factory) {
97 factory->changeShortcutScheme(scheme);
98 }
99 }
100 }
101
102 for (KActionCollection *collection : std::as_const(t&: m_collections)) {
103 m_keyChooser->addCollection(collection);
104 }
105
106 QApplication::restoreOverrideCursor();
107 }
108
109 void undo()
110 {
111 m_keyChooser->undo();
112 }
113
114 void toggleDetails()
115 {
116 const bool isVisible = m_schemeEditor->isVisible();
117
118 m_schemeEditor->setVisible(!isVisible);
119 m_detailsButton->setText(detailsButtonText() + (isVisible ? QStringLiteral(" >>") : QStringLiteral(" <<")));
120 }
121
122 static QString detailsButtonText()
123 {
124 return i18n("Manage &Schemes");
125 }
126
127 void save()
128 {
129 m_keyChooser->save();
130 Q_EMIT q->saved();
131 }
132
133 KShortcutsDialog *const q;
134 KShortcutsEditor *m_keyChooser = nullptr; // ### move
135 KShortcutSchemesEditor *m_schemeEditor = nullptr;
136 QPushButton *m_detailsButton = nullptr;
137 bool m_saveSettings = false;
138};
139
140KShortcutsDialog::KShortcutsDialog(QWidget *parent)
141 : KShortcutsDialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, parent)
142{
143}
144
145KShortcutsDialog::KShortcutsDialog(KShortcutsEditor::ActionTypes types, KShortcutsEditor::LetterShortcuts allowLetterShortcuts, QWidget *parent)
146 : QDialog(parent)
147 , d(new KShortcutsDialogPrivate(this))
148{
149 setWindowTitle(i18nc("@title:window", "Configure Keyboard Shortcuts"));
150 setModal(true);
151
152 QVBoxLayout *layout = new QVBoxLayout(this);
153
154 d->m_keyChooser = new KShortcutsEditor(this, types, allowLetterShortcuts);
155 layout->addWidget(d->m_keyChooser);
156
157 d->m_schemeEditor = new KShortcutSchemesEditor(this);
158 connect(sender: d->m_schemeEditor, signal: &KShortcutSchemesEditor::shortcutsSchemeChanged, context: this, slot: [this](const QString &scheme) {
159 d->changeShortcutScheme(scheme);
160 });
161 d->m_schemeEditor->hide();
162 layout->addWidget(d->m_schemeEditor);
163
164 d->m_detailsButton = new QPushButton;
165 d->m_detailsButton->setText(KShortcutsDialogPrivate::detailsButtonText() + QLatin1String(" >>"));
166
167 QPushButton *printButton = new QPushButton;
168 KGuiItem::assign(button: printButton, item: KStandardGuiItem::print());
169
170 QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
171 buttonBox->addButton(button: d->m_detailsButton, role: QDialogButtonBox::ActionRole);
172 buttonBox->addButton(button: printButton, role: QDialogButtonBox::ActionRole);
173 buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults);
174 KGuiItem::assign(button: buttonBox->button(which: QDialogButtonBox::Ok), item: KStandardGuiItem::ok());
175 KGuiItem::assign(button: buttonBox->button(which: QDialogButtonBox::Cancel), item: KStandardGuiItem::cancel());
176 KGuiItem::assign(button: buttonBox->button(which: QDialogButtonBox::RestoreDefaults), item: KStandardGuiItem::defaults());
177 layout->addWidget(buttonBox);
178
179 connect(sender: buttonBox->button(which: QDialogButtonBox::RestoreDefaults), signal: &QAbstractButton::clicked, context: d->m_keyChooser, slot: &KShortcutsEditor::allDefault);
180 connect(sender: d->m_detailsButton, signal: &QPushButton::clicked, context: this, slot: [this]() {
181 d->toggleDetails();
182 });
183 connect(sender: printButton, signal: &QPushButton::clicked, context: d->m_keyChooser, slot: &KShortcutsEditor::printShortcuts);
184 connect(sender: buttonBox, signal: &QDialogButtonBox::rejected, context: this, slot: [this]() {
185 d->undo();
186 });
187
188 connect(sender: buttonBox, signal: &QDialogButtonBox::accepted, context: this, slot: &QDialog::accept);
189 connect(sender: buttonBox, signal: &QDialogButtonBox::rejected, context: this, slot: &QDialog::reject);
190
191 KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("KShortcutsDialog Settings"));
192 resize(group.readEntry(key: "Dialog Size", defaultValue: sizeHint()));
193}
194
195KShortcutsDialog::~KShortcutsDialog()
196{
197 KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("KShortcutsDialog Settings"));
198 group.writeEntry(key: "Dialog Size", value: size(), pFlags: KConfigGroup::Persistent | KConfigGroup::Global);
199}
200
201void KShortcutsDialog::addCollection(KActionCollection *collection, const QString &title)
202{
203 d->m_keyChooser->addCollection(collection, title);
204 d->m_collections << collection;
205}
206
207QList<KActionCollection *> KShortcutsDialog::actionCollections() const
208{
209 return d->m_collections;
210}
211
212// TODO KF6: remove this method, always save settings, and open the
213// dialog with show() not exec()
214bool KShortcutsDialog::configure(bool saveSettings)
215{
216 d->m_saveSettings = saveSettings;
217 if (isModal()) {
218 int retcode = exec();
219 return retcode;
220 } else {
221 show();
222 return false;
223 }
224}
225
226void KShortcutsDialog::accept()
227{
228 if (d->m_saveSettings) {
229 d->save();
230 }
231 QDialog::accept();
232}
233
234QSize KShortcutsDialog::sizeHint() const
235{
236 return QSize(600, 480);
237}
238
239// static
240void KShortcutsDialog::showDialog(KActionCollection *collection, KShortcutsEditor::LetterShortcuts allowLetterShortcuts, QWidget *parent)
241{
242 auto *dlg = new KShortcutsDialog(KShortcutsEditor::AllActions, allowLetterShortcuts, parent);
243 dlg->setAttribute(Qt::WA_DeleteOnClose);
244
245 dlg->d->m_saveSettings = true; // Always save settings if the dialog is accepted
246
247 dlg->addCollection(collection);
248 dlg->show();
249}
250
251void KShortcutsDialog::importConfiguration(const QString &path)
252{
253 KConfig config(path);
254 d->m_keyChooser->importConfiguration(config: static_cast<KConfigBase *>(&config));
255}
256
257void KShortcutsDialog::exportConfiguration(const QString &path) const
258{
259 KConfig config(path);
260 d->m_keyChooser->exportConfiguration(config: static_cast<KConfigBase *>(&config));
261}
262
263void KShortcutsDialog::refreshSchemes()
264{
265 d->m_schemeEditor->refreshSchemes();
266}
267
268void KShortcutsDialog::addActionToSchemesMoreButton(QAction *action)
269{
270 d->m_schemeEditor->addMoreMenuAction(action);
271}
272
273#include "moc_kshortcutsdialog.cpp"
274#include "moc_kshortcutsdialog_p.cpp"
275

source code of kxmlgui/src/kshortcutsdialog.cpp