diff --git a/libs/widgetutils/kis_action_registry.cpp b/libs/widgetutils/kis_action_registry.cpp index 198c61ebdc..540c6e2821 100644 --- a/libs/widgetutils/kis_action_registry.cpp +++ b/libs/widgetutils/kis_action_registry.cpp @@ -1,425 +1,471 @@ /* * Copyright (c) 2015 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include "kis_debug.h" #include "KoResourcePaths.h" #include "kis_icon_utils.h" #include "kactioncollection.h" #include "kactioncategory.h" #include "kis_action_registry.h" +#include "kshortcutschemeshelper_p.h" namespace { /** * We associate several pieces of information with each shortcut. The first * piece of information is a QDomElement, containing the raw data from the * .action XML file. The second and third are QKeySequences, the first of * which is the default shortcut, the last of which is any custom shortcut. * The last two are the KActionCollection and KActionCategory used to * organize the shortcut editor. */ struct ActionInfoItem { QDomElement xmlData; QKeySequence defaultShortcut; QKeySequence customShortcut; QString collectionName; QString categoryName; }; // Convenience macros to extract text of a child node. QString getChildContent(QDomElement xml, QString node) { return xml.firstChildElement(node).text(); }; ActionInfoItem emptyActionInfo; // Used as default return value QString quietlyTranslate(const QString &s) { if (s.isEmpty()) { return s; } if (i18n(s.toUtf8().constData()).isEmpty()) { dbgAction << "No translation found for" << s; return s; } return i18n(s.toUtf8().constData()); }; QKeySequence preferredShortcut(ActionInfoItem action) { if (action.customShortcut.isEmpty()) { return action.defaultShortcut; } else { return action.customShortcut; } }; }; class Q_DECL_HIDDEN KisActionRegistry::Private { public: Private(KisActionRegistry *_q) : q(_q) {}; // This is the main place containing ActionInfoItems. QMap actionInfoList; void loadActionFiles(); void loadActionCollections(); void loadCustomShortcuts(QString filename = QStringLiteral("kritashortcutsrc")); ActionInfoItem &actionInfo(const QString &name) { if (!actionInfoList.contains(name)) { dbgAction << "Tried to look up info for unknown action" << name; } return actionInfoList[name]; }; KisActionRegistry *q; KActionCollection * defaultActionCollection; QMap actionCollections; }; Q_GLOBAL_STATIC(KisActionRegistry, s_instance); KisActionRegistry *KisActionRegistry::instance() { return s_instance; }; KisActionRegistry::KisActionRegistry() : d(new KisActionRegistry::Private(this)) { d->loadActionFiles(); + + KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes"); + QString schemeName = cg.readEntry("Current Scheme", "Default"); + loadShortcutScheme(schemeName); + d->loadCustomShortcuts(); KoResourcePaths::addResourceType("kis_shortcuts", "data", "krita/shortcuts/"); } -// Not the most efficient logic, but simple and readable. +QKeySequence KisActionRegistry::getCustomShortcut(const QString &name) +{ + return d->actionInfo(name).customShortcut; +}; + QKeySequence KisActionRegistry::getPreferredShortcut(const QString &name) { return preferredShortcut(d->actionInfo(name)); }; QKeySequence KisActionRegistry::getCategory(const QString &name) { return d->actionInfo(name).categoryName; }; QStringList KisActionRegistry::allActions() { return d->actionInfoList.keys(); }; KActionCollection * KisActionRegistry::getDefaultCollection() { return d->actionCollections.value("Krita"); }; void KisActionRegistry::addAction(const QString &name, QAction *a) { auto info = d->actionInfo(name); KActionCollection *collection = d->actionCollections.value(info.collectionName); if (!collection) { dbgAction << "No collection found for action" << name; return; } if (collection->action(name)) { dbgAction << "duplicate action" << name << "in collection" << collection->componentName(); } else { } collection->addCategorizedAction(name, a, info.categoryName); }; void KisActionRegistry::notifySettingsUpdated() { d->loadCustomShortcuts(); }; +void KisActionRegistry::loadCustomShortcuts() +{ + d->loadCustomShortcuts(); +}; + +void KisActionRegistry::loadShortcutScheme(const QString &schemeName) +{ + // Load scheme file + if (schemeName != QStringLiteral("Default")) { + QString schemeFileName = KShortcutSchemesHelper::schemeFileLocations().value(schemeName); + if (schemeFileName.isEmpty()) { + // qDebug() << "No configuration file found for scheme" << schemeName; + return; + } + KConfig schemeConfig(schemeFileName, KConfig::SimpleConfig); + applyShortcutScheme(&schemeConfig); + } else { + // Apply default scheme, updating KisActionRegistry data + applyShortcutScheme(); + } +} + QAction * KisActionRegistry::makeQAction(const QString &name, QObject *parent) { QAction * a = new QAction(parent); if (!d->actionInfoList.contains(name)) { dbgAction << "Warning: requested data for unknown action" << name; return a; } propertizeAction(name, a); return a; }; void KisActionRegistry::configureShortcuts(KActionCollection *ac) { KisShortcutsDialog dlg; for (auto i = d->actionCollections.constBegin(); i != d->actionCollections.constEnd(); i++ ) { dlg.addCollection(i.value(), i.key()); } /* Testing */ // QStringList mainWindowActions; // foreach (auto a, ac->actions()) { // mainWindowActions << a->objectName(); // } // dlg.addCollection(ac, "TESTING: XMLGUI-MAINWINDOW"); dlg.configure(); // Show the dialog. d->loadCustomShortcuts(); emit shortcutsUpdated(); } void KisActionRegistry::applyShortcutScheme(const KConfigBase *config) { - - /** - * Stuff from XMLGUI starts here - */ - // First, update the things in KisActionRegistry if (config == 0) { // Simplest just to reload everything d->loadActionFiles(); } else { const auto schemeEntries = config->group(QStringLiteral("Shortcuts")).entryMap(); // Load info item for each shortcut, reset custom shortcuts auto it = schemeEntries.constBegin(); while (it != schemeEntries.end()) { ActionInfoItem &info = d->actionInfo(it.key()); info.defaultShortcut = it.value(); - info.customShortcut = QKeySequence(); it++; } } } void KisActionRegistry::updateShortcut(const QString &name, QAction *action) { const ActionInfoItem info = d->actionInfo(name); action->setShortcut(preferredShortcut(info)); auto propertizedShortcut = qVariantFromValue(QList() << info.defaultShortcut); action->setProperty("defaultShortcuts", propertizedShortcut); } bool KisActionRegistry::propertizeAction(const QString &name, QAction * a) { const ActionInfoItem info = d->actionInfo(name); QDomElement actionXml = info.xmlData; if (actionXml.text().isEmpty()) { dbgAction << "No XML data found for action" << name; return false; } // i18n requires converting format from QString. auto getChildContent_i18n = [=](QString node){return quietlyTranslate(getChildContent(actionXml, node));}; // Note: the fields in the .action documents marked for translation are determined by extractrc. QString icon = getChildContent(actionXml, "icon"); QString text = getChildContent(actionXml, "text"); QString whatsthis = getChildContent_i18n("whatsThis"); QString toolTip = getChildContent_i18n("toolTip"); QString statusTip = getChildContent_i18n("statusTip"); QString iconText = getChildContent_i18n("iconText"); bool isCheckable = getChildContent(actionXml, "isCheckable") == QString("true"); a->setObjectName(name); // This is helpful, should be added more places in Krita a->setIcon(KisIconUtils::loadIcon(icon.toLatin1())); a->setText(text); a->setObjectName(name); a->setWhatsThis(whatsthis); a->setToolTip(toolTip); a->setStatusTip(statusTip); a->setIconText(iconText); a->setCheckable(isCheckable); updateShortcut(name, a); // TODO: check for colliding shortcuts, either with some code like this, or // by relying on the code existing inside kactioncollection // // QMap existingShortcuts; // Q_FOREACH (QAction* action, actionCollection->actions()) { // if(action->shortcut() == QKeySequence(0)) { // continue; // } // if (existingShortcuts.contains(action->shortcut())) { // dbgAction << QString("Actions %1 and %2 have the same shortcut: %3") \ // .arg(action->text()) \ // .arg(existingShortcuts[action->shortcut()]->text()) \ // .arg(action->shortcut()); // } // else { // existingShortcuts[action->shortcut()] = action; // } // } return true; } QString KisActionRegistry::getActionProperty(const QString &name, const QString &property) { ActionInfoItem info = d->actionInfo(name); QDomElement actionXml = info.xmlData; if (actionXml.text().isEmpty()) { dbgAction << "No XML data found for action" << name; return QString(); } return getChildContent(actionXml, property); } +void KisActionRegistry::writeCustomShortcuts() const +{ + KConfigGroup cg(KSharedConfig::openConfig("kritashortcutsrc"), + QStringLiteral("Shortcuts")); + + QList writeActions; + for (auto it = d->actionInfoList.constBegin(); + it != d->actionInfoList.constEnd(); ++it) { + + QString actionName = it.key(); + QString s = it.value().customShortcut.toString(); + if (s.isEmpty()) { + cg.deleteEntry(actionName, KConfigGroup::Persistent); + } else { + cg.writeEntry(actionName, s, KConfigGroup::Persistent); + } + } + cg.sync(); +} + void KisActionRegistry::Private::loadActionFiles() { KoResourcePaths::addResourceType("kis_actions", "data", "krita/actions"); auto searchType = KoResourcePaths::Recursive | KoResourcePaths::NoDuplicates; QStringList actionDefinitions = KoResourcePaths::findAllResources("kis_actions", "*.action", searchType); // Extract actions all XML .action files. Q_FOREACH (const QString &actionDefinition, actionDefinitions) { QDomDocument doc; QFile f(actionDefinition); f.open(QFile::ReadOnly); doc.setContent(f.readAll()); QDomElement base = doc.documentElement(); // "ActionCollection" outer group QString collectionName = base.attribute("name"); QString version = base.attribute("version"); if (version != "2") { errAction << ".action XML file" << actionDefinition << "has incorrect version; skipping."; continue; } KActionCollection *actionCollection; if (!actionCollections.contains(collectionName)) { actionCollection = new KActionCollection(q, collectionName); actionCollections.insert(collectionName, actionCollection); dbgAction << "Adding a new action collection " << collectionName; } else { actionCollection = actionCollections.value(collectionName); } // Loop over nodes. Each of these corresponds to a // KActionCategory, producing a group of actions in the shortcut dialog. QDomElement actions = base.firstChild().toElement(); while (!actions.isNull()) { // field QDomElement categoryTextNode = actions.firstChild().toElement(); QString categoryName = quietlyTranslate(categoryTextNode.text()); KActionCategory *category = actionCollection->getCategory(categoryName); dbgAction << "Using category" << categoryName; // tags QDomElement actionXml = categoryTextNode.nextSiblingElement(); // Loop over individual actions while (!actionXml.isNull()) { if (actionXml.tagName() == "Action") { // Read name from format QString name = actionXml.attribute("name"); // Bad things if (name.isEmpty()) { errAction << "Unnamed action in definitions file " << actionDefinition; } else if (actionInfoList.contains(name)) { // errAction << "NOT COOL: Duplicated action name from xml data: " << name; } else { ActionInfoItem info; info.xmlData = actionXml; info.defaultShortcut = getChildContent(actionXml, "shortcut"); info.customShortcut = QKeySequence(); info.categoryName = categoryName; info.collectionName = collectionName; // dbgAction << "default shortcut for" << name << " - " << info.defaultShortcut; actionInfoList.insert(name,info); } } actionXml = actionXml.nextSiblingElement(); } actions = actions.nextSiblingElement(); } } }; void KisActionRegistry::Private::loadCustomShortcuts(QString filename) { Q_UNUSED(filename); const KConfigGroup localShortcuts(KSharedConfig::openConfig("kritashortcutsrc"), QStringLiteral("Shortcuts")); if (!localShortcuts.exists()) { return; } for (auto i = actionInfoList.begin(); i != actionInfoList.end(); ++i) { if (localShortcuts.hasKey(i.key())) { QString entry = localShortcuts.readEntry(i.key(), QString()); i.value().customShortcut = QKeySequence(entry); } } }; diff --git a/libs/widgetutils/kis_action_registry.h b/libs/widgetutils/kis_action_registry.h index 0f838aba11..6b84f3b1d7 100644 --- a/libs/widgetutils/kis_action_registry.h +++ b/libs/widgetutils/kis_action_registry.h @@ -1,139 +1,152 @@ /* * Copyright (c) 2015 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "kritawidgetutils_export.h" class KActionCollection; class QDomElement; class KConfigBase; /** * KisShortcutRegistry is intended to manage the global shortcut configuration * for Krita. It is intended to provide the user's choice of shortcuts * the .action files, the configuration files that were done with XMLGUI, and * the * * It is a global static. Grab an ::instance. */ class KRITAWIDGETUTILS_EXPORT KisActionRegistry : public QObject { Q_OBJECT public: static KisActionRegistry *instance(); /** * Get shortcut for an action */ QKeySequence getPreferredShortcut(const QString &name); /** * Get shortcut for an action */ QKeySequence getDefaultShortcut(const QString &name); /** * Get custom shortcut for an action */ QKeySequence getCustomShortcut(const QString &name); /** * Get category name */ QKeySequence getCategory(const QString &name); /** * @return value @p property for an action @p name. * * Allow flexible info structure for KisActions, etc. */ QString getActionProperty(const QString &name, const QString &property); /** * Saves action in a category. Note that this grabs ownership of the action. */ void addAction(const QString &name, QAction *a); /** * Produces a new QAction based on the .action data files. * * N.B. this action will not be saved in the registry. */ QAction * makeQAction(const QString &name, QObject *parent); /** * Fills the standard QAction properties of an action. * * @return true if the action was loaded successfully. */ bool propertizeAction(const QString &name, QAction *a); /** * @return list of actions with data available. */ QStringList allActions(); /** * Save settings. Not implemented yet. */ // void writeSettings(KActionCollection *ac); /** * Display the shortcut configuration dialog. */ void configureShortcuts(KActionCollection *ac); + /** + * Reload custom shortcuts from kritashortcutsrc + */ + void loadCustomShortcuts(); + + + /** + * Read custom shortcuts from kritashortcutsrc + */ + void writeCustomShortcuts() const; + + /** * Call after settings are changed. */ void notifySettingsUpdated(); /** * Constructor. Please don't touch! */ KisActionRegistry(); // Undocumented void updateShortcut(const QString &name, QAction *ac); KActionCollection * getDefaultCollection(); + void loadShortcutScheme(const QString &schemeName); // If config == 0, reload defaults void applyShortcutScheme(const KConfigBase *config = 0); Q_SIGNALS: void shortcutsUpdated(); private: class Private; Private * const d; }; diff --git a/libs/widgetutils/xmlgui/KisShortcutsDialog_p.cpp b/libs/widgetutils/xmlgui/KisShortcutsDialog_p.cpp index 5e3771f4cb..e89cdb8336 100644 --- a/libs/widgetutils/xmlgui/KisShortcutsDialog_p.cpp +++ b/libs/widgetutils/xmlgui/KisShortcutsDialog_p.cpp @@ -1,104 +1,91 @@ /* * Copyright (c) 2015 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisShortcutsDialog_p.h" #include "kshortcutschemeshelper_p.h" #include "kxmlguiclient.h" #include #include "kactioncollection.h" #include "kxmlguifactory.h" #include #include #include #include "kis_action_registry.h" #include #include QKeySequence primarySequence(const QList &sequences) { return sequences.isEmpty() ? QKeySequence() : sequences.at(0); } QKeySequence alternateSequence(const QList &sequences) { return sequences.size() <= 1 ? QKeySequence() : sequences.at(1); } KisShortcutsDialog::KisShortcutsDialogPrivate::KisShortcutsDialogPrivate(KisShortcutsDialog *q) : q(q) { } void KisShortcutsDialog::KisShortcutsDialogPrivate::changeShortcutScheme(const QString &schemeName) { QString dialogText = i18n("The current shortcut scheme is modified. Save before switching to the new one?"); if (m_shortcutsEditor->isModified() && KMessageBox::questionYesNo( q,dialogText ) == KMessageBox::Yes) { m_shortcutsEditor->save(); } else { m_shortcutsEditor->undo(); } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_shortcutsEditor->clearCollections(); KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes"); cg.writeEntry("Current Scheme", schemeName); + KisActionRegistry::instance()->loadShortcutScheme(schemeName); - // Load scheme file - if (schemeName != QStringLiteral("Default")) { - QString schemeFileName = KShortcutSchemesHelper::schemeFileLocations().value(schemeName); - if (schemeFileName.isEmpty()) { - // qDebug() << "No configuration file found for scheme" << schemeName; - return; - } - KConfig schemeConfig(schemeFileName, KConfig::SimpleConfig); - KisActionRegistry::instance()->applyShortcutScheme(&schemeConfig); - } else { - // Apply default scheme, updating KisActionRegistry data - KisActionRegistry::instance()->applyShortcutScheme(); - } - // Update actions themselves, and re-add to dialog box to refresh auto it = m_collections.constBegin(); while (it != m_collections.constEnd()) { it.value()->updateShortcuts(); m_shortcutsEditor->addCollection(it.value(), it.key()); it++; } QApplication::restoreOverrideCursor(); } void KisShortcutsDialog::KisShortcutsDialogPrivate::undo() { m_shortcutsEditor->undo(); } void KisShortcutsDialog::KisShortcutsDialogPrivate::save() { m_shortcutsEditor->save(); emit q->saved(); }; #include "moc_KisShortcutsDialog_p.cpp" diff --git a/libs/widgetutils/xmlgui/KisShortcutsEditor.cpp b/libs/widgetutils/xmlgui/KisShortcutsEditor.cpp index acac50f77f..7cc7ea77a3 100644 --- a/libs/widgetutils/xmlgui/KisShortcutsEditor.cpp +++ b/libs/widgetutils/xmlgui/KisShortcutsEditor.cpp @@ -1,312 +1,314 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe Copyright (C) 1997 Nicolas Hadacek Copyright (C) 1998 Matthias Ettrich Copyright (C) 2001 Ellis Whitehead Copyright (C) 2006 Hamish Rodda Copyright (C) 2007 Roberto Raggi Copyright (C) 2007 Andreas Hartmetz Copyright (C) 2008 Michael Jansen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisShortcutsEditor.h" #include "KisShortcutsEditor_p.h" #include "kshortcutschemeshelper_p.h" #include "config-xmlgui.h" #include "kis_action_registry.h" // The following is needed for KisShortcutsEditorPrivate and QTreeWidgetHack // #include "KisShortcutsDialog_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kactioncollection.h" #include "kactioncategory.h" #include //--------------------------------------------------------------------- // KisShortcutsEditor //--------------------------------------------------------------------- Q_DECLARE_METATYPE(KisShortcutsEditorItem *) KisShortcutsEditor::KisShortcutsEditor(KActionCollection *collection, QWidget *parent, ActionTypes actionType, LetterShortcuts allowLetterShortcuts) : QWidget(parent) , d(new KisShortcutsEditorPrivate(this)) { d->initGUI(actionType, allowLetterShortcuts); addCollection(collection); } KisShortcutsEditor::KisShortcutsEditor(QWidget *parent, ActionTypes actionType, LetterShortcuts allowLetterShortcuts) : QWidget(parent) , d(new KisShortcutsEditorPrivate(this)) { d->initGUI(actionType, allowLetterShortcuts); } KisShortcutsEditor::~KisShortcutsEditor() { delete d; } bool KisShortcutsEditor::isModified() const { // Iterate over all items QTreeWidgetItemIterator it(d->ui.list, QTreeWidgetItemIterator::NoChildren); for (; (*it); ++it) { KisShortcutsEditorItem *item = dynamic_cast(*it); if (item && item->isModified()) { return true; } } return false; } void KisShortcutsEditor::clearCollections() { d->delegate->contractAll(); d->ui.list->clear(); d->actionCollections.clear(); QTimer::singleShot(0, this, SLOT(resizeColumns())); } void KisShortcutsEditor::addCollection(KActionCollection *collection, const QString &title) { // KXmlGui add action collections unconditionally. If some plugin doesn't // provide actions we don't want to create empty subgroups. if (collection->isEmpty()) { return; } // Pause updating. setUpdatesEnabled(false); /** * Forward this actioncollection to the delegate which will do conflict checking. * This _replaces_ existing collections in the delegate. */ d->actionCollections.append(collection); d->delegate->setCheckActionCollections(d->actionCollections); // Determine how we should label this collection in the widget. QString collectionTitle; if (!title.isEmpty()) { collectionTitle = title; } else { // Use the programName (Translated). collectionTitle = collection->componentDisplayName(); } // Create the collection root node. QTreeWidgetItem *hierarchy[3]; hierarchy[KisShortcutsEditorPrivate::Root] = d->ui.list->invisibleRootItem(); hierarchy[KisShortcutsEditorPrivate::Program] = d->findOrMakeItem(hierarchy[KisShortcutsEditorPrivate::Root], collectionTitle); hierarchy[KisShortcutsEditorPrivate::Action] = NULL; // Remember which actions we have seen. We will be adding categorized // actions first, so this will help us keep track of which actions haven't // been categorized yet, so we can add them as uncategorized at the end. QSet actionsSeen; // Add a subtree for each category? Perhaps easier to think that this // doesn't exist. Basically you add KActionCategory as a QObject child of // KActionCollection, and then tag objects as belonging to the category. foreach (KActionCategory *category, collection->categories()) { // Don't display empty categories. if (category->actions().isEmpty()) { continue; } hierarchy[KisShortcutsEditorPrivate::Action] = d->findOrMakeItem(hierarchy[KisShortcutsEditorPrivate::Program], category->text()); // Add every item from the category. foreach (QAction *action, category->actions()) { actionsSeen.insert(action); d->addAction(action, hierarchy, KisShortcutsEditorPrivate::Action); } // Fold in each KActionCategory by default. hierarchy[KisShortcutsEditorPrivate::Action]->setExpanded(false); } // Finally, tack on any uncategorized actions. foreach (QAction *action, collection->actions()) { if (!actionsSeen.contains(action)) { d->addAction(action, hierarchy, KisShortcutsEditorPrivate::Program); } } // sort the list d->ui.list->sortItems(Name, Qt::AscendingOrder); // Now turn on updating again. setUpdatesEnabled(true); QTimer::singleShot(0, this, SLOT(resizeColumns())); } void KisShortcutsEditor::clearConfiguration() { d->clearConfiguration(); } void KisShortcutsEditor::importConfiguration(KConfigBase *config) { Q_ASSERT(config); if (!config) { return; } // Reload the configuration file KisActionRegistry::instance()->applyShortcutScheme(config); - // Update the actions themselves - // TODO + // XXX: Any need to update the actions themselves? + // Update the dialog entry items const KConfigGroup schemeShortcuts(config, QStringLiteral("Shortcuts")); for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) { if (!(*it)->parent()) { continue; } KisShortcutsEditorItem *item = static_cast(*it); const QString actionId = item->data(Id).toString(); if (!schemeShortcuts.hasKey(actionId)) continue; QList sc = QKeySequence::listFromString(schemeShortcuts.readEntry(actionId, QString())); d->changeKeyShortcut(item, LocalPrimary, primarySequence(sc)); d->changeKeyShortcut(item, LocalAlternate, alternateSequence(sc)); } } void KisShortcutsEditor::exportConfiguration(KConfigBase *config) const { Q_ASSERT(config); if (!config) { return; } if (d->actionTypes) { QString groupName(QStringLiteral("Shortcuts")); KConfigGroup group(config, groupName); foreach (KActionCollection *collection, d->actionCollections) { collection->writeSettings(&group, true); } } KisActionRegistry::instance()->notifySettingsUpdated(); } -void KisShortcutsEditor::writeConfiguration(KConfigGroup *config) const +void KisShortcutsEditor::saveShortcuts(KConfigGroup *config) const { // This is a horrible mess with pointers... auto cg = KConfigGroup(KSharedConfig::openConfig("kritashortcutsrc"), "Shortcuts"); if (config == 0) { config = &cg; } + // Clear and reset temporary shortcuts + config->deleteGroup(); foreach (KActionCollection *collection, d->actionCollections) { - collection->writeSettings(config, true); + collection->writeSettings(config, false); } KisActionRegistry::instance()->notifySettingsUpdated(); } //slot void KisShortcutsEditor::resizeColumns() { for (int i = 0; i < d->ui.list->columnCount(); i++) { d->ui.list->resizeColumnToContents(i); } } void KisShortcutsEditor::commit() { for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) { if (KisShortcutsEditorItem *item = dynamic_cast(*it)) { item->commit(); } } } void KisShortcutsEditor::save() { - writeConfiguration(); + saveShortcuts(); commit(); // Not doing this would be bad } void KisShortcutsEditor::undo() { // TODO: is this working? for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) { if (KisShortcutsEditorItem *item = dynamic_cast(*it)) { item->undo(); } } } void KisShortcutsEditor::allDefault() { d->allDefault(); } void KisShortcutsEditor::printShortcuts() const { d->printShortcuts(); } KisShortcutsEditor::ActionTypes KisShortcutsEditor::actionTypes() const { return d->actionTypes; } void KisShortcutsEditor::setActionTypes(ActionTypes actionTypes) { d->setActionTypes(actionTypes); } #include "moc_KisShortcutsEditor.cpp" diff --git a/libs/widgetutils/xmlgui/KisShortcutsEditor.h b/libs/widgetutils/xmlgui/KisShortcutsEditor.h index 3325ab1ede..d723fe6fe8 100644 --- a/libs/widgetutils/xmlgui/KisShortcutsEditor.h +++ b/libs/widgetutils/xmlgui/KisShortcutsEditor.h @@ -1,244 +1,256 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Nicolas Hadacek Copyright (C) 2001,2001 Ellis Whitehead Copyright (C) 2006 Hamish Rodda Copyright (C) 2007 Roberto Raggi Copyright (C) 2007 Andreas Hartmetz Copyright (C) 2008 Michael Jansen Copyright (c) 2015 Michael Abrahams This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISSHORTCUTSEDITOR_H #define KISSHORTCUTSEDITOR_H #include #include class KActionCollection; class KConfig; class KConfigBase; class KConfigGroup; class KGlobalAccel; class KisShortcutsEditorPrivate; /** * WARNING: KisShortcutsEditor expects that the list of existing shortcuts is * already free of conflicts. If it is not, nothing will crash, but your users * won't like the resulting behavior. * * TODO: What exactly is the problem? */ /** * @short Widget for configuration of KAccel and KGlobalAccel. * * Configure dictionaries of key/action associations for KActions, * including global shortcuts. * * The class takes care of all aspects of configuration, including * handling key conflicts internally. Connect to the allDefault() * slot if you want to set all configurable shortcuts to their * default values. * * @see KShortcutsDialog * @author Nicolas Hadacek * @author Hamish Rodda (KDE 4 porting) * @author Michael Jansen */ class KRITAWIDGETUTILS_EXPORT KisShortcutsEditor : public QWidget { Q_OBJECT Q_PROPERTY(ActionTypes actionTypes READ actionTypes WRITE setActionTypes) public: /* * These attempt to build some sort of characterization of all actions. The * idea is to determine which sorts of actions will be configured in the * dialog. * * Enumerating all possible actions is a sorrowful, pitiable endeavor, * useless for Krita. We should do something about this. */ enum ActionType { /// Actions which are triggered by any keypress in a widget which has the action added to it WidgetAction = Qt::WidgetShortcut /*0*/, /// Actions which are triggered by any keypress in a window which has the action added to it or its child widget(s) WindowAction = Qt::WindowShortcut /*1*/, /// Actions which are triggered by any keypress in the application ApplicationAction = Qt::ApplicationShortcut /*2*/, /// Actions which are triggered by any keypress in the windowing system GlobalAction = 4, /// All actions AllActions = 0xffffffff }; Q_DECLARE_FLAGS(ActionTypes, ActionType) enum LetterShortcuts { /// Shortcuts without a modifier are not allowed, so 'A' would not be /// valid, whereas 'Ctrl+A' would be. This only applies to printable /// characters, however. 'F1', 'Insert' etc. could still be used. LetterShortcutsDisallowed = 0, /// Letter shortcuts are allowed LetterShortcutsAllowed }; /** * Constructor. * * @param collection the KActionCollection to configure * @param parent parent widget * @param actionTypes types of actions to display in this widget. * @param allowLetterShortcuts set to LetterShortcutsDisallowed if unmodified alphanumeric * keys ('A', '1', etc.) are not permissible shortcuts. */ KisShortcutsEditor(KActionCollection *collection, QWidget *parent, ActionTypes actionTypes = AllActions, LetterShortcuts allowLetterShortcuts = LetterShortcutsAllowed); /** * \overload * * Creates a key chooser without a starting action collection. * * @param parent parent widget * @param actionTypes types of actions to display in this widget. * @param allowLetterShortcuts set to LetterShortcutsDisallowed if unmodified alphanumeric * keys ('A', '1', etc.) are not permissible shortcuts. */ explicit KisShortcutsEditor(QWidget *parent, ActionTypes actionTypes = AllActions, LetterShortcuts allowLetterShortcuts = LetterShortcutsAllowed); /// Destructor virtual ~KisShortcutsEditor(); /** * @ret true if there are unsaved changes. */ bool isModified() const; /** * Removes all action collections from the editor */ void clearCollections(); /** * Note: the reason this is so damn complicated is because it's supposed to * support having multiple applications running inside of each other through * KisParts. That means we have to be able to separate sections within each * configuration file. * * Insert an action collection, i.e. add all its actions to the ones * already associated with the KisShortcutsEditor object. * * @param title subtree title of this collection of shortcut. */ void addCollection(KActionCollection *, const QString &title = QString()); /** * Undo all change made since the last commit(). */ void undo(); /** * Save the changes. * * Before saving the changes are committed. This saves the actions to disk. * Any KActionCollection objects with the xmlFile() value set will be * written to an XML file. All other will be written to the application's * rc file. */ void save(); /** * Update the dialog entries without saving. * * @since 4.2 */ void commit(); /** * Removes all configured shortcuts. */ void clearConfiguration(); /** - * Write the current settings to the \p config object. + * Write the current custom shortcut settings to the \p config object. * * @param config Config object to save to. Default is kritashortcutsrc. * */ - void writeConfiguration(KConfigGroup *config = 0) const; + void saveShortcuts(KConfigGroup *config = 0) const; + + /** + * Write the current shortcuts to a new scheme to configuration file + * + * @param config Config object to save to. + */ void exportConfiguration(KConfigBase *config) const; + + /** + * Import a shortcut scheme from configuration file + * + * @param config Config object to save to. + */ void importConfiguration(KConfigBase *config); /** * Sets the types of actions to display in this widget. * * @param actionTypes New types of actions * @since 5.0 */ void setActionTypes(ActionTypes actionTypes); /** * * @return The types of actions currently displayed in this widget. * @since 5.0 */ ActionTypes actionTypes() const; Q_SIGNALS: /** * Emitted when an action's shortcut has been changed. **/ void keyChange(); public Q_SLOTS: /** * Resize columns to width required */ void resizeColumns(); /** * Set all shortcuts to their default values (bindings). **/ void allDefault(); /** * Opens a printing dialog to print all the shortcuts */ void printShortcuts() const; private: Q_PRIVATE_SLOT(d, void capturedShortcut(QVariant, const QModelIndex &)) private: friend class KisShortcutsDialog; friend class KisShortcutsEditorPrivate; KisShortcutsEditorPrivate *const d; Q_DISABLE_COPY(KisShortcutsEditor) }; Q_DECLARE_OPERATORS_FOR_FLAGS(KisShortcutsEditor::ActionTypes) #endif // KISSHORTCUTSEDITOR_H diff --git a/libs/widgetutils/xmlgui/kactioncollection.cpp b/libs/widgetutils/xmlgui/kactioncollection.cpp index f5963eb6f9..8aad560067 100644 --- a/libs/widgetutils/xmlgui/kactioncollection.cpp +++ b/libs/widgetutils/xmlgui/kactioncollection.cpp @@ -1,769 +1,762 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2002 Joseph Wenninger (C) 2005-2007 Hamish Rodda This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kactioncollection.h" #include "config-xmlgui.h" #include "kactioncategory.h" #include "kxmlguiclient.h" #include "kxmlguifactory.h" #include "kactioncategory.h" #include "kis_action_registry.h" #include #include #include #include #include #include #include #include #include #include #include #include class KActionCollectionPrivate { public: KActionCollectionPrivate() : m_parentGUIClient(0L), configGroup(QStringLiteral("Shortcuts")), connectTriggered(false), connectHovered(false), q(0) { } void setComponentForAction(QAction *action) { Q_UNUSED(action) } static QList s_allCollections; void _k_associatedWidgetDestroyed(QObject *obj); void _k_actionDestroyed(QObject *obj); bool writeKXMLGUIConfigFile(); QString m_componentName; QString m_componentDisplayName; //! Remove a action from our internal bookkeeping. Returns NULL if the //! action doesn't belong to us. QAction *unlistAction(QAction *); QMap actionByName; QList actions; const KXMLGUIClient *m_parentGUIClient; QString configGroup; bool configIsGlobal : 1; bool connectTriggered : 1; bool connectHovered : 1; KActionCollection *q; QList associatedWidgets; }; QList KActionCollectionPrivate::s_allCollections; KActionCollection::KActionCollection(QObject *parent, const QString &cName) : QObject(parent) , d(new KActionCollectionPrivate) { d->q = this; KActionCollectionPrivate::s_allCollections.append(this); setComponentName(cName); } KActionCollection::KActionCollection(const KXMLGUIClient *parent) : QObject(0) , d(new KActionCollectionPrivate) { d->q = this; KActionCollectionPrivate::s_allCollections.append(this); d->m_parentGUIClient = parent; d->m_componentName = parent->componentName(); } KActionCollection::~KActionCollection() { KActionCollectionPrivate::s_allCollections.removeAll(this); delete d; } QList KActionCollection::categories() const { return this->findChildren(); } KActionCategory *KActionCollection::getCategory(const QString &name) { KActionCategory *category = 0; foreach (KActionCategory *c, categories()) { if (c->text() == name) { category = c; } } if (category == 0) { category = new KActionCategory(name, this); } return category; }; void KActionCollection::clear() { d->actionByName.clear(); qDeleteAll(d->actions); d->actions.clear(); } QAction *KActionCollection::action(const QString &name) const { QAction *action = 0L; if (!name.isEmpty()) { action = d->actionByName.value(name); } return action; } QAction *KActionCollection::action(int index) const { // ### investigate if any apps use this at all return actions().value(index); } int KActionCollection::count() const { return d->actions.count(); } bool KActionCollection::isEmpty() const { return count() == 0; } void KActionCollection::setComponentName(const QString &cName) { if (count() > 0) { // Its component name is part of an action's signature in the context of // global shortcuts and the semantics of changing an existing action's // signature are, as it seems, impossible to get right. // As of now this only matters for global shortcuts. We could // thus relax the requirement and only refuse to change the component data // if we have actions with global shortcuts in this collection. qWarning() << "this does not work on a KActionCollection containing actions!"; } if (!cName.isEmpty()) { d->m_componentName = cName; } else { d->m_componentName = QCoreApplication::applicationName(); } } QString KActionCollection::componentName() const { return d->m_componentName; } void KActionCollection::setComponentDisplayName(const QString &displayName) { d->m_componentDisplayName = displayName; } QString KActionCollection::componentDisplayName() const { if (!d->m_componentDisplayName.isEmpty()) { return d->m_componentDisplayName; } if (!QGuiApplication::applicationDisplayName().isEmpty()) { return QGuiApplication::applicationDisplayName(); } return QCoreApplication::applicationName(); } const KXMLGUIClient *KActionCollection::parentGUIClient() const { return d->m_parentGUIClient; } QList KActionCollection::actions() const { return d->actions; } const QList< QAction * > KActionCollection::actionsWithoutGroup() const { QList ret; Q_FOREACH (QAction *action, d->actions) if (!action->actionGroup()) { ret.append(action); } return ret; } const QList< QActionGroup * > KActionCollection::actionGroups() const { QSet set; Q_FOREACH (QAction *action, d->actions) if (action->actionGroup()) { set.insert(action->actionGroup()); } return set.toList(); } QAction *KActionCollection::addCategorizedAction(const QString &name, QAction *action, const QString &categoryName) { return getCategory(categoryName)->addAction(name, action); } QAction *KActionCollection::addAction(const QString &name, QAction *action) { if (!action) { return action; } const QString objectName = action->objectName(); QString indexName = name; if (indexName.isEmpty()) { // No name provided. Use the objectName. indexName = objectName; } else { // Set the new name action->setObjectName(indexName); } // No name provided and the action had no name. Make one up. This will not // work when trying to save shortcuts. if (indexName.isEmpty()) { indexName = indexName.sprintf("unnamed-%p", (void *)action); action->setObjectName(indexName); } // From now on the objectName has to have a value. Else we cannot safely // remove actions. Q_ASSERT(!action->objectName().isEmpty()); // look if we already have THIS action under THIS name ;) if (d->actionByName.value(indexName, 0) == action) { // This is not a multi map! Q_ASSERT(d->actionByName.count(indexName) == 1); return action; } if (!KAuthorized::authorizeKAction(indexName)) { // Disable this action action->setEnabled(false); action->setVisible(false); action->blockSignals(true); } // Check if we have another action under this name if (QAction *oldAction = d->actionByName.value(indexName)) { takeAction(oldAction); } // Check if we have this action under a different name. // Not using takeAction because we don't want to remove it from categories, // and because it has the new name already. const int oldIndex = d->actions.indexOf(action); if (oldIndex != -1) { d->actionByName.remove(d->actionByName.key(action)); d->actions.removeAt(oldIndex); } // Add action to our lists. d->actionByName.insert(indexName, action); d->actions.append(action); Q_FOREACH (QWidget *widget, d->associatedWidgets) { widget->addAction(action); } connect(action, SIGNAL(destroyed(QObject*)), SLOT(_k_actionDestroyed(QObject*))); d->setComponentForAction(action); if (d->connectHovered) { connect(action, SIGNAL(hovered()), SLOT(slotActionHovered())); } if (d->connectTriggered) { connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered())); } emit inserted(action); return action; } void KActionCollection::addActions(const QList &actions) { Q_FOREACH (QAction *action, actions) { addAction(action->objectName(), action); } } void KActionCollection::removeAction(QAction *action) { delete takeAction(action); } QAction *KActionCollection::takeAction(QAction *action) { if (!d->unlistAction(action)) { return NULL; } // Remove the action from all widgets Q_FOREACH (QWidget *widget, d->associatedWidgets) { widget->removeAction(action); } action->disconnect(this); emit removed(action); //deprecated return action; } QAction *KActionCollection::addAction(KStandardAction::StandardAction actionType, const QObject *receiver, const char *member) { QAction *action = KStandardAction::create(actionType, receiver, member, this); return action; } QAction *KActionCollection::addAction(KStandardAction::StandardAction actionType, const QString &name, const QObject *receiver, const char *member) { // pass 0 as parent, because if the parent is a KActionCollection KStandardAction::create automatically // adds the action to it under the default name. We would trigger the // warning about renaming the action then. QAction *action = KStandardAction::create(actionType, receiver, member, 0); // Give it a parent for gc. action->setParent(this); // Remove the name to get rid of the "rename action" warning above action->setObjectName(name); // And now add it with the desired name. return addAction(name, action); } QAction *KActionCollection::addAction(const QString &name, const QObject *receiver, const char *member) { QAction *a = new QAction(this); if (receiver && member) { connect(a, SIGNAL(triggered(bool)), receiver, member); } return addAction(name, a); } QKeySequence KActionCollection::defaultShortcut(QAction *action) const { const QList shortcuts = defaultShortcuts(action); return shortcuts.isEmpty() ? QKeySequence() : shortcuts.first(); } QList KActionCollection::defaultShortcuts(QAction *action) const { return action->property("defaultShortcuts").value >(); } void KActionCollection::setDefaultShortcut(QAction *action, const QKeySequence &shortcut) { setDefaultShortcuts(action, QList() << shortcut); } void KActionCollection::setDefaultShortcuts(QAction *action, const QList &shortcuts) { action->setShortcuts(shortcuts); action->setProperty("defaultShortcuts", QVariant::fromValue(shortcuts)); } bool KActionCollection::isShortcutsConfigurable(QAction *action) const { // Considered as true by default const QVariant value = action->property("isShortcutConfigurable"); return value.isValid() ? value.toBool() : true; } void KActionCollection::setShortcutsConfigurable(QAction *action, bool configurable) { action->setProperty("isShortcutConfigurable", configurable); } QString KActionCollection::configGroup() const { return d->configGroup; } void KActionCollection::setConfigGroup(const QString &group) { d->configGroup = group; } void KActionCollection::updateShortcuts() { auto actionRegistry = KisActionRegistry::instance(); for (QMap::ConstIterator it = d->actionByName.constBegin(); it != d->actionByName.constEnd(); ++it) { actionRegistry->updateShortcut(it.key(), it.value()); } } -void KActionCollection::readSettings(KConfigGroup *config) +void KActionCollection::readSettings() { - // TODO: perhaps get rid of this - KConfigGroup cg(KSharedConfig::openConfig("kritashortcutsrc"), configGroup()); - if (!config) { - config = &cg; - } - - if (!config->exists()) { - return; - } + auto ar = KisActionRegistry::instance(); + ar->loadCustomShortcuts(); for (QMap::ConstIterator it = d->actionByName.constBegin(); it != d->actionByName.constEnd(); ++it) { QAction *action = it.value(); if (!action) { continue; } if (isShortcutsConfigurable(action)) { QString actionName = it.key(); - QString entry = config->readEntry(actionName, QString()); - if (!entry.isEmpty()) { - action->setShortcuts(QKeySequence::listFromString(entry)); - } else { - action->setShortcuts(defaultShortcuts(action)); - } + ar->updateShortcut(actionName, action); } } - } bool KActionCollectionPrivate::writeKXMLGUIConfigFile() { const KXMLGUIClient *kxmlguiClient = q->parentGUIClient(); // return false if there is no KXMLGUIClient if (!kxmlguiClient || kxmlguiClient->xmlFile().isEmpty()) { return false; } QString attrShortcut = QStringLiteral("shortcut"); // Read XML file QString sXml(KXMLGUIFactory::readConfigFile(kxmlguiClient->xmlFile(), q->componentName())); QDomDocument doc; doc.setContent(sXml); // Process XML data // Get hold of ActionProperties tag QDomElement elem = KXMLGUIFactory::actionPropertiesElement(doc); // now, iterate through our actions for (QMap::ConstIterator it = actionByName.constBegin(); it != actionByName.constEnd(); ++it) { QAction *action = it.value(); if (!action) { continue; } QString actionName = it.key(); // If the action name starts with unnamed- spit out a warning and ignore // it. That name will change at will and will break loading writing if (actionName.startsWith(QLatin1String("unnamed-"))) { qCritical() << "Skipped writing shortcut for action " << actionName << "(" << action->text() << ")!"; continue; } bool bSameAsDefault = (action->shortcuts() == q->defaultShortcuts(action)); // now see if this element already exists // and create it if necessary (unless bSameAsDefault) QDomElement act_elem = KXMLGUIFactory::findActionByName(elem, actionName, !bSameAsDefault); if (act_elem.isNull()) { continue; } if (bSameAsDefault) { act_elem.removeAttribute(attrShortcut); if (act_elem.attributes().count() == 1) { elem.removeChild(act_elem); } } else { act_elem.setAttribute(attrShortcut, QKeySequence::listToString(action->shortcuts())); } } // Write back to XML file KXMLGUIFactory::saveConfigFile(doc, kxmlguiClient->localXMLFile(), q->componentName()); return true; } void KActionCollection::writeSettings(KConfigGroup *config, - bool writeAll, + bool writeScheme, QAction *oneAction) const { // If the caller didn't provide a config group we try to save the KXMLGUI // Configuration file. (This will work if the parentGUI was set and has a // valid configuration file.) if (config == 0 && d->writeKXMLGUIConfigFile()) { return; } KConfigGroup cg(KSharedConfig::openConfig(), configGroup()); if (!config) { config = &cg; } QList writeActions; if (oneAction) { writeActions.append(oneAction); } else { writeActions = actions(); } for (QMap::ConstIterator it = d->actionByName.constBegin(); it != d->actionByName.constEnd(); ++it) { QAction *action = it.value(); if (!action) { continue; } QString actionName = it.key(); // If the action name starts with unnamed- spit out a warning and ignore // it. That name will change at will and will break loading writing if (actionName.startsWith(QLatin1String("unnamed-"))) { qCritical() << "Skipped saving shortcut for action without name " \ << action->text() << "!"; continue; } // Write the shortcut if (isShortcutsConfigurable(action)) { bool bConfigHasAction = !config->readEntry(actionName, QString()).isEmpty(); bool bSameAsDefault = (action->shortcuts() == defaultShortcuts(action)); - // If we're using a global config (no) or this setting - // differs from the default, then we want to write. + // If the current shortcut differs from the default, we want to write. + KConfigGroup::WriteConfigFlags flags = KConfigGroup::Persistent; - if (writeAll || !bSameAsDefault) { + if (writeScheme || !bSameAsDefault) { // We are instructed to write all shortcuts or the shortcut is // not set to its default value. Write it QString s = QKeySequence::listToString(action->shortcuts()); if (s.isEmpty()) { - s = QStringLiteral("none"); + if (writeScheme) { + // A scheme file should explicitly set "none" for the shortcut + s = QStringLiteral("none"); + config->writeEntry(actionName, s, flags); + } else { + config->deleteEntry(actionName, flags); + } + } else { + config->writeEntry(actionName, s, flags); } - config->writeEntry(actionName, s, flags); - } else if (bConfigHasAction) { - // Otherwise, this key is the same as default but exists in - // config file. Remove it. + // This key is the same as default but exists in config file. + // Remove it. config->deleteEntry(actionName, flags); } } } config->sync(); } void KActionCollection::slotActionTriggered() { QAction *action = qobject_cast(sender()); if (action) { emit actionTriggered(action); } } void KActionCollection::slotActionHighlighted() { slotActionHovered(); } void KActionCollection::slotActionHovered() { QAction *action = qobject_cast(sender()); if (action) { emit actionHighlighted(action); emit actionHovered(action); } } void KActionCollectionPrivate::_k_actionDestroyed(QObject *obj) { // obj isn't really a QAction anymore. So make sure we don't do fancy stuff // with it. QAction *action = static_cast(obj); if (!unlistAction(action)) { return; } //HACK the object we emit is partly destroyed emit q->removed(action); //deprecated. remove in KDE5 } void KActionCollection::connectNotify(const QMetaMethod &signal) { if (d->connectHovered && d->connectTriggered) { return; } if (signal.methodSignature() == "actionHighlighted(QAction*)" || signal.methodSignature() == "actionHovered(QAction*)") { if (!d->connectHovered) { d->connectHovered = true; Q_FOREACH (QAction *action, actions()) { connect(action, SIGNAL(hovered()), SLOT(slotActionHovered())); } } } else if (signal.methodSignature() == "actionTriggered(QAction*)") { if (!d->connectTriggered) { d->connectTriggered = true; Q_FOREACH (QAction *action, actions()) { connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered())); } } } QObject::connectNotify(signal); } const QList< KActionCollection * > &KActionCollection::allCollections() { return KActionCollectionPrivate::s_allCollections; } void KActionCollection::associateWidget(QWidget *widget) const { Q_FOREACH (QAction *action, actions()) { if (!widget->actions().contains(action)) { widget->addAction(action); } } } void KActionCollection::addAssociatedWidget(QWidget *widget) { if (!d->associatedWidgets.contains(widget)) { widget->addActions(actions()); d->associatedWidgets.append(widget); connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*))); } } void KActionCollection::removeAssociatedWidget(QWidget *widget) { Q_FOREACH (QAction *action, actions()) { widget->removeAction(action); } d->associatedWidgets.removeAll(widget); disconnect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*))); } QAction *KActionCollectionPrivate::unlistAction(QAction *action) { // ATTENTION: // This method is called with an QObject formerly known as a QAction // during _k_actionDestroyed(). So don't do fancy stuff here that needs a // real QAction! // Get the index for the action int index = actions.indexOf(action); // Action not found. if (index == -1) { return NULL; } // An action collection can't have the same action twice. Q_ASSERT(actions.indexOf(action, index + 1) == -1); // Get the actions name const QString name = action->objectName(); // Remove the action actionByName.remove(name); actions.removeAt(index); // Remove the action from the categories. Should be only one QList categories = q->findChildren(); Q_FOREACH (KActionCategory *category, categories) { category->unlistAction(action); } return action; } QList< QWidget * > KActionCollection::associatedWidgets() const { return d->associatedWidgets; } void KActionCollection::clearAssociatedWidgets() { Q_FOREACH (QWidget *widget, d->associatedWidgets) Q_FOREACH (QAction *action, actions()) { widget->removeAction(action); } d->associatedWidgets.clear(); } void KActionCollectionPrivate::_k_associatedWidgetDestroyed(QObject *obj) { associatedWidgets.removeAll(static_cast(obj)); } #include "moc_kactioncollection.cpp" diff --git a/libs/widgetutils/xmlgui/kactioncollection.h b/libs/widgetutils/xmlgui/kactioncollection.h index 4f315d1905..86a0fbf58e 100644 --- a/libs/widgetutils/xmlgui/kactioncollection.h +++ b/libs/widgetutils/xmlgui/kactioncollection.h @@ -1,514 +1,514 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2005-2006 Hamish Rodda This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KACTIONCOLLECTION_H #define KACTIONCOLLECTION_H #include "config-xmlgui.h" #include #include #include class QAction; class KXMLGUIClient; class KConfigGroup; class QActionGroup; class QString; class KActionCategory; /** * \short A container for a set of QAction objects. * * KActionCollection manages a set of QAction objects. It * allows them to be grouped for organized presentation of configuration to the user, * saving + loading of configuration, and optionally for automatic plugging into * specified widget(s). * * Additionally, KActionCollection provides several convenience functions for locating * named actions, and actions grouped by QActionGroup. * * \note If you create your own action collection and need to assign shortcuts * to the actions within, you have to call associateWidget() or * addAssociatedWidget() to have them working. */ class KRITAWIDGETUTILS_EXPORT KActionCollection : public QObject { friend class KXMLGUIClient; Q_OBJECT Q_PROPERTY(QString configGroup READ configGroup WRITE setConfigGroup) public: /** * Constructor. Allows specification of a component name other than the default * application name, where needed (remember to call setComponentDisplayName() too). */ explicit KActionCollection(QObject *parent, const QString &cName = QString()); /** * Destructor. */ virtual ~KActionCollection(); /** * Access the list of all action collections in existence for this app */ static const QList &allCollections(); /** * Clears the entire action collection, deleting all actions. */ void clear(); /** * Associate all actions in this collection to the given \a widget. * Unlike addAssociatedWidget, this method only adds all current actions * in the collection to the given widget. Any action added after this call * will not be added to the given widget automatically. * So this is just a shortcut for a foreach loop and a widget->addAction call. */ void associateWidget(QWidget *widget) const; /** * Associate all actions in this collection to the given \a widget, including any actions * added after this association is made. * * This does not change the action's shortcut context, so if you need to have the actions only * trigger when the widget has focus, you'll need to set the shortcut context on each action * to Qt::WidgetShortcut (or better still, Qt::WidgetWithChildrenShortcut with Qt 4.4+) */ void addAssociatedWidget(QWidget *widget); /** * Remove an association between all actions in this collection and the given \a widget, i.e. * remove those actions from the widget, and stop associating newly added actions as well. */ void removeAssociatedWidget(QWidget *widget); /** * Return a list of all associated widgets. */ QList associatedWidgets() const; /** * Clear all associated widgets and remove the actions from those widgets. */ void clearAssociatedWidgets(); /** * Returns the KConfig group with which settings will be loaded and saved. */ QString configGroup() const; /** * Sets \a group as the KConfig group with which settings will be loaded and saved. */ void setConfigGroup(const QString &group); /** * Read all key associations from @p config. * * If @p config is zero, read all key associations from the * application's configuration file KSharedConfig::openConfig(), * in the group set by setConfigGroup(). */ - void readSettings(KConfigGroup *config = 0); + void readSettings(); /** * Update shortcuts from the KisActionRegistry. */ void updateShortcuts(); /** * Write the current configurable key associations. If @a is nonzero, use * that configuration group. * * Otherwise, the output file is determined as follows. If this action * collection belongs to a KXMLGuiClient the setting are saved to the * kxmlgui definition file. If not the settings are written to the * applications config file. * * \note oneAction() and writeDefaults() have no meaning for the kxmlgui * configuration file. * * \param config Config object to save to, or null (see above) - * \param writeDefaults set to true to write settings which are already at defaults. + * \param writeScheme set to true if we export all settings as new shortcut scheme * \param oneAction pass an action here if you just want to save the values for one action, eg. * if you know that action is the only one which has changed. */ - void writeSettings(KConfigGroup *config = 0, bool writeDefaults = false, QAction *oneAction = 0) const; + void writeSettings(KConfigGroup *config = 0, bool writeScheme = false, QAction *oneAction = 0) const; /** * Returns the number of actions in the collection. * * This is equivalent to actions().count(). */ int count() const; /** * Returns whether the action collection is empty or not. */ bool isEmpty() const; /** * Return the QAction* at position "index" in the action collection. * * This is equivalent to actions().value(index); */ QAction *action(int index) const; /** * Get the action with the given \a name from the action collection. * * @param name Name of the QAction * @return A pointer to the QAction in the collection which matches the parameters or * null if nothing matches. */ QAction *action(const QString &name) const; /** * Returns the list of QActions which belong to this action collection. * * The list is guaranteed to be in the same order the action were put into * the collection. */ QList actions() const; /** * Returns the list of QActions without an QAction::actionGroup() which belong to this action collection. */ const QList actionsWithoutGroup() const; /** * Returns the list of all QActionGroups associated with actions in this action collection. */ const QList actionGroups() const; /** * Set the \a componentName associated with this action collection. * * \warning Don't call this method on a KActionCollection that contains * actions. This is not supported. * * \param componentData the name which is to be associated with this action collection, * or QString() to indicate the app name. This is used to load/save settings into XML files. * KXmlGuiClient::setComponentName takes care of calling this. */ void setComponentName(const QString &componentName); /** The component name with which this class is associated. */ QString componentName() const; /** * Set the component display name associated with this action collection. * (e.g. for the toolbar editor) * KXmlGuiClient::setComponentName takes care of calling this. */ void setComponentDisplayName(const QString &displayName); /** The display name for the associated component. */ QString componentDisplayName() const; /** * The parent KXMLGUIClient, or null if not available. */ const KXMLGUIClient *parentGUIClient() const; /** * Returns the KActionCategories inside this collection */ QList categories() const; /** * Gets a category with name @p name inside this collection. * * Creates a new category if one does not exist. */ KActionCategory *getCategory(const QString &categoryName); Q_SIGNALS: /** * Indicates that \a action was inserted into this action collection. */ void inserted(QAction *action); /** * Indicates that \a action was removed from this action collection. * @deprecated */ QT_MOC_COMPAT void removed(QAction *action); /** * Indicates that \a action was highlighted (hovered over). * @deprecated Replaced by actionHovered(QAction* action); */ QT_MOC_COMPAT void actionHighlighted(QAction *action); /** * Indicates that \a action was hovered. */ void actionHovered(QAction *action); /** * Indicates that \a action was triggered */ void actionTriggered(QAction *action); protected: /// Overridden to perform connections when someone wants to know whether an action was highlighted or triggered void connectNotify(const QMetaMethod &signal) Q_DECL_OVERRIDE; protected Q_SLOTS: virtual void slotActionTriggered(); /** * @internal * @deprecated Replaced by slotActionHovered(); */ QT_MOC_COMPAT virtual void slotActionHighlighted(); private Q_SLOTS: void slotActionHovered(); public: /** * Add an action under the given name to the collection. * * Inserting an action that was previously inserted under a different name will replace the * old entry, i.e. the action will not be available under the old name anymore but only under * the new one. * * Inserting an action under a name that is already used for another action will replace * the other action in the collection (but will not delete it). * * If KAuthorized::authorizeKAction() reports that the action is not * authorized, it will be disabled and hidden. * * @param name The name by which the action be retrieved again from the collection. * @param action The action to add. * @return the same as the action given as parameter. This is just for convenience * (chaining calls) and consistency with the other addAction methods, you can also * simply ignore the return value. */ Q_INVOKABLE QAction *addAction(const QString &name, QAction *action); /** * Adds a new action to the collection in category @p category. * * The category will be created if it does not already exist. */ Q_INVOKABLE QAction *addCategorizedAction(const QString &name, QAction *action, const QString &categoryName); /** * Adds a list of actions to the collection. * * The objectName of the actions is used as their internal name in the collection. * * Uses addAction(QString, QAction*). * * @param actions the list of the actions to add. * * @see addAction() * @since 5.0 */ void addActions(const QList &actions); /** * Removes an action from the collection and deletes it. * @param action The action to remove. */ void removeAction(QAction *action); /** * Removes an action from the collection. * @param action the action to remove. */ QAction *takeAction(QAction *action); /** * Creates a new standard action, adds it to the collection and connects the * action's triggered(bool) signal to the specified receiver/member. The * newly created action is also returned. * * Note: Using KStandardAction::OpenRecent will cause a different signal than * triggered(bool) to be used, see KStandardAction for more information. * * The action can be retrieved later from the collection by its standard name as per * KStandardAction::stdName. * * @param actionType The standard action type of the action to create. * @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @return new action of the given type ActionType. */ QAction *addAction(KStandardAction::StandardAction actionType, const QObject *receiver = 0, const char *member = 0); /** * Creates a new standard action, adds to the collection under the given name * and connects the action's triggered(bool) signal to the specified * receiver/member. The newly created action is also returned. * * Note: Using KStandardAction::OpenRecent will cause a different signal than * triggered(bool) to be used, see KStandardAction for more information. * * The action can be retrieved later from the collection by the specified name. * * @param actionType The standard action type of the action to create. * @param name The name by which the action be retrieved again from the collection. * @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @return new action of the given type ActionType. */ QAction *addAction(KStandardAction::StandardAction actionType, const QString &name, const QObject *receiver = 0, const char *member = 0); /** * Creates a new action under the given name to the collection and connects * the action's triggered(bool) signal to the specified receiver/member. The * newly created action is returned. * * NOTE: KDE prior to 4.2 used the triggered() signal instead of the triggered(bool) * signal. * * Inserting an action that was previously inserted under a different name will replace the * old entry, i.e. the action will not be available under the old name anymore but only under * the new one. * * Inserting an action under a name that is already used for another action will replace * the other action in the collection. * * @param name The name by which the action be retrieved again from the collection. * @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @return new action of the given type ActionType. */ QAction *addAction(const QString &name, const QObject *receiver = 0, const char *member = 0); /** * Creates a new action under the given name, adds it to the collection and connects the action's triggered(bool) * signal to the specified receiver/member. The receiver slot may accept either a bool or no * parameters at all (i.e. slotTriggered(bool) or slotTriggered() ). * The type of the action is specified by the template parameter ActionType. * * NOTE: KDE prior to 4.2 connected the triggered() signal instead of the triggered(bool) * signal. * * @param name The internal name of the action (e.g. "file-open"). * @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @return new action of the given type ActionType. * * @see addAction() */ template ActionType *add(const QString &name, const QObject *receiver = 0, const char *member = 0) { ActionType *a = new ActionType(this); if (receiver && member) { connect(a, SIGNAL(triggered(bool)), receiver, member); } addAction(name, a); return a; } /** * Get the default primary shortcut for the given action. * * @param action the action for which the default primary shortcut should be returned. * @return the default primary shortcut of the given action * @since 5.0 */ QKeySequence defaultShortcut(QAction *action) const; /** * Get the default shortcuts for the given action. * * @param action the action for which the default shortcuts should be returned. * @return the default shortcuts of the given action * @since 5.0 */ QList defaultShortcuts(QAction *action) const; //TODO KF6: Make setDefaultShortcut static /** * Set the default shortcut for the given action. * Since 5.2, this also calls action->setShortcut(shortcut), i.e. the default shortcut is * made active initially. * * @param action the action for which the default shortcut should be set. * @param shortcut the shortcut to use for the given action in its specified shortcutContext() * @since 5.0 */ void setDefaultShortcut(QAction *action, const QKeySequence &shortcut); /** * Set the default shortcuts for the given action. * Since 5.2, this also calls action->setShortcuts(shortcuts), i.e. the default shortcut is * made active initially. * * @param action the action for which the default shortcut should be set. * @param shortcuts the shortcuts to use for the given action in its specified shortcutContext() * @since 5.0 */ Q_INVOKABLE void setDefaultShortcuts(QAction *action, const QList &shortcuts); /** * Returns true if the given action's shortcuts may be configured by the user. * * @param action the action for the hint should be verified. * @since 5.0 */ bool isShortcutsConfigurable(QAction *action) const; /** * Indicate whether the user may configure the action's shortcuts. * * @param action the action for the hint should be verified. * @param configurable set to true if the shortcuts of the given action may be configured by the user, otherwise false. * @since 5.0 */ void setShortcutsConfigurable(QAction *action, bool configurable); private: Q_PRIVATE_SLOT(d, void _k_actionDestroyed(QObject *)) Q_PRIVATE_SLOT(d, void _k_associatedWidgetDestroyed(QObject *)) KActionCollection(const KXMLGUIClient *parent); // used by KXMLGUIClient friend class KActionCollectionPrivate; class KActionCollectionPrivate *const d; }; #endif diff --git a/libs/widgetutils/xmlgui/kshortcutschemeseditor.cpp b/libs/widgetutils/xmlgui/kshortcutschemeseditor.cpp index d928a80e0a..ed379de6f9 100644 --- a/libs/widgetutils/xmlgui/kshortcutschemeseditor.cpp +++ b/libs/widgetutils/xmlgui/kshortcutschemeseditor.cpp @@ -1,187 +1,189 @@ /* This file is part of the KDE libraries Copyright (C) 2008 Alexander Dymo This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisShortcutsDialog_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisShortcutsDialog.h" #include "kshortcutschemeshelper_p.h" #include "kactioncollection.h" #include "kxmlguiclient.h" #include "KoResourcePaths.h" KShortcutSchemesEditor::KShortcutSchemesEditor(KisShortcutsDialog *parent) : QHBoxLayout(parent) , m_dialog(parent) { KConfigGroup group(KSharedConfig::openConfig(), "Shortcut Schemes"); QStringList schemes; schemes << QStringLiteral("Default"); auto schemeFileLocations = KShortcutSchemesHelper::schemeFileLocations(); schemes << schemeFileLocations.keys(); const QString currentScheme = group.readEntry("Current Scheme", "Default"); setMargin(0); QLabel *schemesLabel = new QLabel(i18n("Shortcut Schemes:"), m_dialog); addWidget(schemesLabel); m_schemesList = new QComboBox(m_dialog); m_schemesList->setEditable(false); m_schemesList->addItems(schemes); m_schemesList->setCurrentIndex(m_schemesList->findText(currentScheme)); schemesLabel->setBuddy(m_schemesList); addWidget(m_schemesList); m_newScheme = new QPushButton(i18n("New...")); addWidget(m_newScheme); m_deleteScheme = new QPushButton(i18n("Delete")); addWidget(m_deleteScheme); QPushButton *moreActions = new QPushButton(i18n("More Actions")); addWidget(moreActions); QMenu *moreActionsMenu = new QMenu(m_dialog); moreActionsMenu->addAction(i18n("Save as Scheme Defaults"), this, SLOT(saveAsDefaultsForScheme())); moreActionsMenu->addAction(i18n("Export Scheme..."), this, SLOT(exportShortcutsScheme())); moreActionsMenu->addAction(i18n("Import Scheme..."), this, SLOT(importShortcutsScheme())); moreActionsMenu->addAction(i18n("Restore Defaults"), m_dialog, SLOT(allDefault())); moreActions->setMenu(moreActionsMenu); addStretch(1); connect(m_schemesList, SIGNAL(activated(QString)), this, SIGNAL(shortcutsSchemeChanged(QString))); connect(m_newScheme, SIGNAL(clicked()), this, SLOT(newScheme())); connect(m_deleteScheme, SIGNAL(clicked()), this, SLOT(deleteScheme())); updateDeleteButton(); } void KShortcutSchemesEditor::newScheme() { bool ok; const QString newName = QInputDialog::getText(m_dialog, i18n("Name for New Scheme"), i18n("Name for new scheme:"), QLineEdit::Normal, i18n("New Scheme"), &ok); if (!ok) { return; } if (m_schemesList->findText(newName) != -1) { KMessageBox::sorry(m_dialog, i18n("A scheme with this name already exists.")); return; } const QString newSchemeFileName = KShortcutSchemesHelper::shortcutSchemeFileName(newName); QFile schemeFile(newSchemeFileName); if (!schemeFile.open(QFile::WriteOnly | QFile::Truncate)) { return; } schemeFile.close(); m_dialog->exportConfiguration(newSchemeFileName); m_schemesList->addItem(newName); m_schemesList->setCurrentIndex(m_schemesList->findText(newName)); m_schemeFileLocations.insert(newName, newSchemeFileName); updateDeleteButton(); emit shortcutsSchemeChanged(newName); } void KShortcutSchemesEditor::deleteScheme() { if (KMessageBox::questionYesNo(m_dialog, i18n("Do you really want to delete the scheme %1?\n\ Note that this will not remove any system wide shortcut schemes.", currentScheme())) == KMessageBox::No) { return; } //delete the scheme for the app itself QFile::remove(KShortcutSchemesHelper::shortcutSchemeFileName(currentScheme())); m_schemesList->removeItem(m_schemesList->findText(currentScheme())); updateDeleteButton(); emit shortcutsSchemeChanged(currentScheme()); } QString KShortcutSchemesEditor::currentScheme() { return m_schemesList->currentText(); } void KShortcutSchemesEditor::exportShortcutsScheme() { //ask user about dir - QString path = QFileDialog::getSaveFileName(m_dialog, i18n("Export Shortcuts"), QDir::currentPath(), i18n("Shortcuts (*.shortcuts)")); + QString path = QFileDialog::getSaveFileName(m_dialog, i18n("Export Shortcuts"), + KoResourcePaths::saveLocation("kis_shortcuts"), + i18n("Shortcuts (*.shortcuts)")); if (path.isEmpty()) { return; } m_dialog->exportConfiguration(path); } void KShortcutSchemesEditor::importShortcutsScheme() { //ask user about dir QString path = QFileDialog::getOpenFileName(m_dialog, i18n("Import Shortcuts"), QDir::currentPath(), i18n("Shortcuts (*.shortcuts)")); if (path.isEmpty()) { return; } m_dialog->importConfiguration(path); } void KShortcutSchemesEditor::saveAsDefaultsForScheme() { // XXX: Not implemented #if 0 foreach (KActionCollection *collection, m_dialog->actionCollections()) { KShortcutSchemesHelper::exportActionCollection(collection, currentScheme()); } #endif } void KShortcutSchemesEditor::updateDeleteButton() { m_deleteScheme->setEnabled(m_schemesList->count() >= 1); }