9#include <QGlobalStatic>
13#include <KSharedConfig>
14#include <klocalizedstring.h>
16#include <KConfigGroup>
37 struct ActionInfoItem {
40 QString collectionName;
44 return m_defaultShortcuts;
48 m_defaultShortcuts =
value;
52 return m_customShortcuts;
56 m_customShortcuts =
value;
57 m_explicitlyReset = explicitlyReset;
61 return m_customShortcuts.isEmpty() && !m_explicitlyReset ?
62 m_defaultShortcuts : m_customShortcuts;
69 bool m_explicitlyReset =
false;
73 QDomElement getChild(QDomElement xml, QString node) {
74 return xml.firstChildElement(node);
78 QString getChildContent(QDomElement xml, QString node) {
79 return xml.firstChildElement(node).text();
82 QString getChildContentForOS(QDomElement xml, QString tagName, QString os = QString()) {
85 QDomElement node = xml.firstChildElement(tagName);
88 while(!found && !node.isNull()) {
89 if (node.attribute(
"operatingSystem") == os) {
93 else if (node.hasAttribute(
"operatingSystemElse")) {
96 else if (nodeElse.isNull()) {
100 node = node.nextSiblingElement(tagName);
103 if (!found && !nodeElse.isNull()) {
104 return nodeElse.text();
111 QString quietlyTranslate(
const QDomElement &s) {
112 if (s.isNull() || s.text().isEmpty()) {
115 QString translatedString;
116 const QString attrContext = QStringLiteral(
"context");
117 const QString attrDomain = QStringLiteral(
"translationDomain");
118 QString context = QStringLiteral(
"action");
120 if (!s.attribute(attrContext).isEmpty()) {
121 context = s.attribute(attrContext);
124 QByteArray domain = s.attribute(attrDomain).toUtf8();
125 if (domain.isEmpty()) {
126 domain = s.ownerDocument().documentElement().attribute(attrDomain).toUtf8();
127 if (domain.isEmpty()) {
128 domain = KLocalizedString::applicationDomain();
131 translatedString = i18ndc(domain.constData(), context.toUtf8().constData(), s.text().toUtf8().constData());
132 if (translatedString == s.text()) {
133 translatedString = i18n(s.text().toUtf8().constData());
135 if (translatedString.isEmpty()) {
136 dbgAction <<
"No translation found for" << s.text();
140 return translatedString;
159 if (!actionInfoList.contains(name)) {
160 dbgAction <<
"Tried to look up info for unknown action" << name;
162 return actionInfoList[name];
174 if (!s_instance.exists()) {
182 return d->actionInfoList.contains(name);
189 KConfigGroup cg = KSharedConfig::openConfig()->group(
"Shortcut Schemes");
190 QString schemeName = cg.readEntry(
"Current Scheme",
"Default");
192 if (!QFileInfo(schemeFileName).exists()) {
193 schemeName =
"Default";
207 const ActionInfoItem info =
d->actionInfoList.value(name);
213 d->loadCustomShortcuts();
218 d->loadCustomShortcuts();
224 if (schemeName != QStringLiteral(
"Default")) {
226 if (schemeFileName.isEmpty() || !QFileInfo(schemeFileName).exists()) {
230 KConfig schemeConfig(schemeFileName, KConfig::SimpleConfig);
240 QAction * a =
new QAction(parent);
241 if (!
d->actionInfoList.contains(name)) {
242 qWarning() <<
"Warning: requested data for unknown action" << name;
243 a->setObjectName(name);
264 d->actionInfoList.clear();
265 d->loadActionFiles();
271 const auto schemeEntries = config->group(QStringLiteral(
"Shortcuts")).entryMap();
273 auto it = schemeEntries.constBegin();
274 while (it != schemeEntries.end()) {
275 ActionInfoItem &info =
d->actionInfo(it.key());
276 info.setDefaultShortcuts(QKeySequence::listFromString(it.value()));
284 const ActionInfoItem &info =
d->actionInfo(name);
285 action->setShortcuts(info.effectiveShortcuts());
286 action->setProperty(
"defaultShortcuts", QVariant::fromValue(info.defaultShortcuts()));
288 d->sanityPropertizedShortcuts.insert(name);
293 QString plainTip = quietlyTranslate(getChild(info.xmlData,
"toolTip"));
294 if (action->shortcut().isEmpty()) {
295 action->setToolTip(plainTip);
298 action->setToolTip(plainTip +
" (" + action->shortcut().toString(QKeySequence::NativeText) +
")");
304 return d->sanityPropertizedShortcuts.contains(name);
309 return d->actionInfoList.keys();
314 if (!
d->actionInfoList.contains(name)) {
315 warnAction <<
"propertizeAction: No XML data found for action" << name;
319 const ActionInfoItem info =
d->actionInfo(name);
321 QDomElement actionXml = info.xmlData;
322 if (!actionXml.text().isEmpty()) {
324 auto getChildContent_i18n = [=](QString node){
return quietlyTranslate(getChild(actionXml, node));};
327 QString icon = getChildContent(actionXml,
"icon");
328 QString text = getChildContent_i18n(
"text");
329 QString whatsthis = getChildContent_i18n(
"whatsThis");
332 QString statusTip = getChildContent_i18n(
"statusTip");
333 QString iconText = getChildContent_i18n(
"iconText");
334 bool isCheckable = getChildContent(actionXml,
"isCheckable") == QString(
"true");
336 a->setObjectName(name);
337 if (!icon.isEmpty()) {
339 a->setProperty(
"iconName", QVariant::fromValue(icon));
342 a->setObjectName(name);
343 a->setWhatsThis(whatsthis);
345 a->setStatusTip(statusTip);
346 a->setIconText(iconText);
347 a->setCheckable(isCheckable);
358 ActionInfoItem info =
d->actionInfo(name);
359 QDomElement actionXml = info.xmlData;
360 if (actionXml.text().isEmpty()) {
361 dbgAction <<
"getActionProperty: No XML data found for action" << name;
365 return getChildContent(actionXml, property);
370void KisActionRegistry::Private::loadActionFiles()
374 dbgAction <<
"Action Definitions" << actionDefinitions;
377 Q_FOREACH (
const QString &actionDefinition, actionDefinitions) {
379 QFile f(actionDefinition);
380 f.open(QFile::ReadOnly);
381 doc.setContent(f.readAll());
383 QDomElement base = doc.documentElement();
384 QString collectionName = base.attribute(
"name");
385 QString version = base.attribute(
"version");
386 if (version !=
"2") {
387 qWarning() <<
".action XML file" << actionDefinition <<
"has incorrect version; skipping.";
393 QDomElement actions = base.firstChild().toElement();
394 while (!actions.isNull()) {
397 QDomElement categoryTextNode = actions.firstChild().toElement();
398 QString categoryName = quietlyTranslate(categoryTextNode);
401 QDomElement actionXml = categoryTextNode.nextSiblingElement();
403 if (actionXml.isNull()) {
404 qWarning() << actionDefinition <<
"does not contain any valid actions! (Or the text element was left empty...)";
408 while (!actionXml.isNull()) {
409 if (actionXml.tagName() ==
"Action") {
411 QString
name = actionXml.attribute(
"name");
414 if (
name.isEmpty()) {
415 qWarning() <<
"Unnamed action in definitions file " << actionDefinition;
418 else if (actionInfoList.contains(name)) {
419 qWarning() <<
"NOT COOL: Duplicated action name from xml data: " <<
name;
424 info.xmlData = actionXml;
428 QString shortcutText = getChildContentForOS(actionXml,
"shortcut",
"macos");
430 QString shortcutText = getChildContentForOS(actionXml,
"shortcut");
432 if (!shortcutText.isEmpty()) {
433 info.setDefaultShortcuts(QKeySequence::listFromString(shortcutText));
436 info.categoryName = categoryName;
437 info.collectionName = collectionName;
439 actionInfoList.insert(name,info);
442 actionXml = actionXml.nextSiblingElement();
444 actions = actions.nextSiblingElement();
449void KisActionRegistry::Private::loadCustomShortcuts(QString filename)
451 const KConfigGroup localShortcuts(KSharedConfig::openConfig(filename),
452 QStringLiteral(
"Shortcuts"));
454 if (!localShortcuts.exists()) {
459 for (
auto i = actionInfoList.begin(); i != actionInfoList.end(); ++i) {
460 if (localShortcuts.hasKey(i.key())) {
461 QString entry = localShortcuts.readEntry(i.key(), QString());
462 if (entry == QStringLiteral(
"none")) {
465 i.value().setCustomShortcuts(QKeySequence::listFromString(entry),
false);
478 : componentName(_componentName),
479 categoryName(_categoryName),
486 return m_isValid && !categoryName.isEmpty() && !componentName.isEmpty();
float value(const T *src, size_t ch)
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
PythonPluginManager * instance
bool hasAction(const QString &name) const
QMap< QString, ActionInfoItem > actionInfoList
QList< QString > registeredShortcutIds() const
void updateShortcut(const QString &name, QAction *ac)
void applyShortcutScheme(const KConfigBase *config=0)
QAction * makeQAction(const QString &name, QObject *parent=0)
ActionCategory fetchActionCategory(const QString &name) const
bool propertizeAction(const QString &name, QAction *a)
void loadCustomShortcuts(QString filename=QStringLiteral("kritashortcutsrc"))
void notifySettingsUpdated()
void loadShortcutScheme(const QString &schemeName)
loadShortcutScheme
ActionInfoItem & actionInfo(const QString &name)
const QScopedPointer< Private > d
QString getActionProperty(const QString &name, const QString &property)
bool sanityCheckPropertized(const QString &name)
void loadCustomShortcuts()
Private(KisActionRegistry *_q)
QSet< QString > sanityPropertizedShortcuts
static QHash< QString, QString > schemeFileLocations()
static QStringList findAllAssets(const QString &type, const QString &filter=QString(), SearchOptions options=NoSearchOptions)
const char * name(StandardAction id)
QIcon loadIcon(const QString &name)