Commit 664bb1fa authored by Michael Abrahams's avatar Michael Abrahams
Browse files

Add KisActionRegistry

Summary:
This serves as a database for QAction and KisAction static (xml) data.

This commit should not change any internal behavior.

Tasks: T947

Reviewers: rempt

Reviewed By: rempt

Differential Revision: https://phabricator.kde.org/D476
parent c8b1efd0
......@@ -76,8 +76,10 @@
#include "KisDocumentEntry.h"
#include "kis_color_manager.h"
#include "kis_debug.h"
#include "kis_action.h"
#include "kis_action_registry.h"
Q_GLOBAL_STATIC(KisPart, s_instance)
......@@ -114,98 +116,90 @@ public:
void loadActions();
};
void KisPart::Private::loadActions()
{
actionCollection = new KActionCollection(part, "krita");
KisActionRegistry * actionRegistry = KisActionRegistry::instance();
KoResourcePaths::addResourceType("kis_actions", "data", "krita/actions");
QStringList actionDefinitions = KoResourcePaths::findAllResources("kis_actions", "*.action", KoResourcePaths::Recursive | KoResourcePaths::NoDuplicates);
// Loop is a routine to extract text from an XML .action file.
foreach(const QString &actionDefinition, actionDefinitions) {
QDomDocument doc;
QFile f(actionDefinition);
f.open(QFile::ReadOnly);
doc.setContent(f.readAll());
QDomElement actions = doc.documentElement(); // Top level document node
QString collection = actions.attribute("name");
QDomElement action = actions.firstChild().toElement(); // Action
while (!action.isNull()) {
if (action.tagName() == "Action") {
// This is the global identifier, so we give it special syntax
QString name = action.attribute("name");
// Convenience macro to extract text of a child node.
auto getChildContent = [=](QString node){return action.firstChildElement(node).text();};
// i18n requires converting format from QString.
auto getChildContent_i18n = [=](QString node) {
if (getChildContent(node).isEmpty()) {
// dbgUI << "Found empty string to translate for property" << node;
return QString();
}
return i18n(getChildContent(node).toUtf8().constData());
};
QString icon = getChildContent("icon");
QString text = getChildContent("text");
// Note: these fields in the .action definitions are marked for translation.
QString whatsthis = getChildContent_i18n("whatsThis");
QString toolTip = getChildContent_i18n("toolTip");
QString statusTip = getChildContent_i18n("statusTip");
QString iconText = getChildContent_i18n("iconText");
bool isCheckable = getChildContent("isCheckable") == QString("true");
QKeySequence shortcut = QKeySequence(getChildContent("shortcut"));
QKeySequence defaultShortcut = QKeySequence(getChildContent("defaultShortcut"));
if (name.isEmpty()) {
errUI << text << "has no name! From:" << actionDefinition;
}
qDebug() << "default shortcut for" << name << " - " << defaultShortcut << " - from text" << getChildContent("defaultShortcut");
KisAction *action = new KisAction(KisIconUtils::loadIcon(icon.toLatin1()), text);
action->setObjectName(name);
action->setWhatsThis(whatsthis);
action->setToolTip(toolTip);
action->setStatusTip(statusTip);
action->setIconText(iconText);
action->setDefaultShortcut(shortcut);
action->setShortcut(shortcut); //TODO: Make this configurable from custom settings
action->setCheckable(isCheckable);
if (!actionCollection->action(name)) {
actionCollection->addAction(name, action);
}
else {
dbgUI << "duplicate action" << name << action << "from" << collection;
// delete action;
}
}
action = action.nextSiblingElement();
}
actionCollection->readSettings();
}
QStringList actionNames = actionRegistry->allActions();
actionCollection->readSettings(); // XXX: consider relocating & managing this
//check for colliding shortcuts
QMap<QKeySequence, QAction*> existingShortcuts;
foreach(QAction* action, actionCollection->actions()) {
if(action->shortcut() == QKeySequence(0)) {
continue;
}
if (existingShortcuts.contains(action->shortcut())) {
dbgUI << "action" << action->text() << "and" << existingShortcuts[action->shortcut()]->text() << "have the same shortcut:" << action->shortcut();
foreach (const QString &name, actionNames) {
QDomElement actionXml = actionRegistry->getActionXml(name);
// Convenience macros to extract text of a child node.
auto getChildContent = [=](QString node){return actionXml.firstChildElement(node).text();};
// i18n requires converting format from QString.
auto getChildContent_i18n = [=](QString node) {
if (getChildContent(node).isEmpty()) {
// dbgAction << "Found empty string to translate for property" << node;
return QString();
}
return i18n(getChildContent(node).toUtf8().constData());
};
QString icon = getChildContent("icon");
QString text = getChildContent("text");
// Note: these fields in the .action definitions are marked for translation.
QString whatsthis = getChildContent_i18n("whatsThis");
QString toolTip = getChildContent_i18n("toolTip");
QString statusTip = getChildContent_i18n("statusTip");
QString iconText = getChildContent_i18n("iconText");
bool isCheckable = getChildContent("isCheckable") == QString("true");
QKeySequence shortcut = QKeySequence(getChildContent("shortcut"));
QKeySequence defaultShortcut = QKeySequence(getChildContent("defaultShortcut"));
KisAction *a = new KisAction(KisIconUtils::loadIcon(icon.toLatin1()), text);
a->setObjectName(name);
a->setWhatsThis(whatsthis);
a->setToolTip(toolTip);
a->setStatusTip(statusTip);
a->setIconText(iconText);
a->setDefaultShortcut(shortcut);
a->setShortcut(shortcut); //TODO: Make this configurable from custom settings
a->setCheckable(isCheckable);
// XXX: these checks may become important again after refactoring
if (!actionCollection->action(name)) {
actionCollection->addAction(name, a);
}
else {
existingShortcuts[action->shortcut()] = action;
dbgAction << "duplicate action" << name << a << "from" << actionCollection;
// delete a;
}
}
}
// TODO: check for colliding shortcuts
//
// Ultimately we want to have more than one KActionCollection, so we can
// have things like Ctrl+I be italics in the text editor widget, while not
// complaining about conflicts elsewhere. Right now, we use only one
// collection, and we don't make things like the text editor configurable,
// so duplicate shortcuts are handled mostly automatically by the shortcut
// editor.
//
// QMap<QKeySequence, QAction*> existingShortcuts;
// 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;
// }
// }
};
KisPart* KisPart::instance()
{
......
......@@ -6,7 +6,6 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(kritawidgets_LIB_SRCS
KoGradientEditWidget.cpp
KoResourcePaths.cpp
KoVBox.cpp
KoDialog.cpp
KoGlobal.cpp
......
......@@ -8,6 +8,7 @@ configure_file(xmlgui/config-xmlgui.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-x
set(kritawidgetutils_LIB_SRCS
kis_icon_utils.cpp
kis_action_registry.cpp
KoGroupButton.cpp
KoProgressBar.cpp
KoProgressUpdater.cpp
......@@ -15,6 +16,7 @@ set(kritawidgetutils_LIB_SRCS
KoUpdaterPrivate_p.cpp
KoProperties.cpp
KoFileDialog.cpp
KoResourcePaths.cpp
config/kcolorscheme.cpp
config/kcolorschememanager.cpp
......
......@@ -22,7 +22,7 @@
#include <QString>
#include <QStringList>
#include <kritawidgets_export.h>
#include <kritawidgetutils_export.h>
/**
......@@ -40,7 +40,7 @@
* convenient enable/disable functionality.
*/
class KRITAWIDGETS_EXPORT KoResourcePaths
class KRITAWIDGETUTILS_EXPORT KoResourcePaths
{
public:
......
/*
* Copyright (c) 2015 Michael Abrahams <miabraha@gmail.com>
*
* 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 <QString>
#include <QHash>
#include <utility>
#include <KSharedConfig>
#include <QGlobalStatic>
#include <QDomElement>
#include <kis_debug.h>
#include <tuple>
#include "kis_debug.h"
#include <KoResourcePaths.h>
#include <QFile>
#include "kis_action_registry.h"
namespace {
struct actionInfoItem {
QDomElement xmlData;
QKeySequence defaultShortcut;
QKeySequence customShortcut;
};
QKeySequence getShortcutFromXml(QDomElement node) {
return node.firstChildElement("shortcut").text();
};
actionInfoItem emptyActionInfo; // Used as default return value
};
class Q_DECL_HIDDEN KisActionRegistry::Private
{
public:
/**
* We associate three pieces of information with each shortcut name. 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.
*
* QHash is most efficient as long as the action name keys are kept relatively short.
*/
QHash<QString, actionInfoItem> actionInfoList;
KSharedConfigPtr cfg{0};
void loadActions();
actionInfoItem actionInfo(QString name) {
return actionInfoList.value(name, emptyActionInfo);
};
};
Q_GLOBAL_STATIC(KisActionRegistry, s_instance);
KisActionRegistry *KisActionRegistry::instance()
{
return s_instance;
};
KisActionRegistry::KisActionRegistry()
: d(new KisActionRegistry::Private())
{
d->cfg = KSharedConfig::openConfig("kritashortcutsrc");
d->loadActions();
}
// No this isn't the most efficient logic, but it's nice and readable.
QKeySequence KisActionRegistry::getPreferredShortcut(QString name)
{
QKeySequence customShortcut = getCustomShortcut(name);
if (customShortcut.isEmpty()) {
return getDefaultShortcut(name);
} else {
return getCustomShortcut(name);
}
};
QKeySequence KisActionRegistry::getDefaultShortcut(QString name)
{
return d->actionInfo(name).defaultShortcut;
};
QKeySequence KisActionRegistry::getCustomShortcut(QString name)
{
return d->actionInfo(name).customShortcut;
};
QStringList KisActionRegistry::allActions()
{
return d->actionInfoList.keys();
};
QDomElement KisActionRegistry::getActionXml(QString name)
{
return d->actionInfo(name).xmlData;
};
void KisActionRegistry::Private::loadActions()
{
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.
foreach(const QString &actionDefinition, actionDefinitions) {
QDomDocument doc;
QFile f(actionDefinition);
f.open(QFile::ReadOnly);
doc.setContent(f.readAll());
QDomElement actions = doc.documentElement(); // Whole document
QString collection = actions.attribute("name");
QDomElement actionXml = actions.firstChild().toElement(); // Single Action
while (!actionXml.isNull()) {
if (actionXml.tagName() == "Action") {
// Read name from format <Action name="save">
QString name = actionXml.attribute("name");
// Very bad things
if (name.isEmpty()) {
errAction << "Unnamed action in definitions file " << actionDefinition;
continue;
}
if (actionInfoList.contains(name)) {
errAction << "Warning: Duplicated action name: " << name;
}
actionInfoItem info;
info.xmlData = actionXml;
info.defaultShortcut = getShortcutFromXml(actionXml);
info.customShortcut = info.defaultShortcut; //TODO: Read from KisConfig
dbgAction << "default shortcut for" << name << " - " << info.defaultShortcut;
actionInfoList.insert(name,info);
}
actionXml = actionXml.nextSiblingElement();
}
}
};
/*
* Copyright (c) 2015 Michael Abrahams <miabraha@gmail.com>
*
* 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 <QString>
#include <QKeySequence>
#include <QDomElement>
#include "kritawidgetutils_export.h"
class KActionCollection;
class QDomElement;
/**
* 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(QString name);
/**
* Get shortcut for an action
*/
QKeySequence getDefaultShortcut(QString name);
/**
* Get custom shortcut for an action
*/
QKeySequence getCustomShortcut(QString name);
/**
* @return DOM info for an action @a name.
*
* Allows somewhat flexible info structure for KisActions, QActions,
* whatever else we decide on doing later.
*/
QDomElement getActionXml(QString name);
/**
* @return list of actions with data available.
*/
QStringList allActions();
/**
* Save settings. Not implemented yet.
*/
// void writeSettings(KActionCollection *ac);
/**
* Constructor. Please don't touch!
*/
KisActionRegistry();
private:
class Private;
Private * const d;
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment