| 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 | |
| 46 | QKeySequence primarySequence(const QList<QKeySequence> &sequences) |
| 47 | { |
| 48 | return sequences.isEmpty() ? QKeySequence() : sequences.at(i: 0); |
| 49 | } |
| 50 | |
| 51 | QKeySequence alternateSequence(const QList<QKeySequence> &sequences) |
| 52 | { |
| 53 | return sequences.size() <= 1 ? QKeySequence() : sequences.at(i: 1); |
| 54 | } |
| 55 | |
| 56 | class KShortcutsDialogPrivate |
| 57 | { |
| 58 | public: |
| 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 | |
| 140 | KShortcutsDialog::KShortcutsDialog(QWidget *parent) |
| 141 | : KShortcutsDialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, parent) |
| 142 | { |
| 143 | } |
| 144 | |
| 145 | KShortcutsDialog::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 | |
| 195 | KShortcutsDialog::~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 | |
| 201 | void KShortcutsDialog::addCollection(KActionCollection *collection, const QString &title) |
| 202 | { |
| 203 | d->m_keyChooser->addCollection(collection, title); |
| 204 | d->m_collections << collection; |
| 205 | } |
| 206 | |
| 207 | QList<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() |
| 214 | bool 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 | |
| 226 | void KShortcutsDialog::accept() |
| 227 | { |
| 228 | if (d->m_saveSettings) { |
| 229 | d->save(); |
| 230 | } |
| 231 | QDialog::accept(); |
| 232 | } |
| 233 | |
| 234 | QSize KShortcutsDialog::sizeHint() const |
| 235 | { |
| 236 | return QSize(600, 480); |
| 237 | } |
| 238 | |
| 239 | // static |
| 240 | void 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 | |
| 251 | void KShortcutsDialog::importConfiguration(const QString &path) |
| 252 | { |
| 253 | KConfig config(path); |
| 254 | d->m_keyChooser->importConfiguration(config: static_cast<KConfigBase *>(&config)); |
| 255 | } |
| 256 | |
| 257 | void KShortcutsDialog::exportConfiguration(const QString &path) const |
| 258 | { |
| 259 | KConfig config(path); |
| 260 | d->m_keyChooser->exportConfiguration(config: static_cast<KConfigBase *>(&config)); |
| 261 | } |
| 262 | |
| 263 | void KShortcutsDialog::refreshSchemes() |
| 264 | { |
| 265 | d->m_schemeEditor->refreshSchemes(); |
| 266 | } |
| 267 | |
| 268 | void 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 | |