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) {
378 QFile f(actionDefinition);
379 if (!f.open(QFile::ReadOnly)) {
380 qWarning() <<
"Could not open" << f.fileName() <<
"for reading:" << f.errorString();
384 doc.setContent(
f.readAll());
386 QDomElement base = doc.documentElement();
387 QString collectionName = base.attribute(
"name");
388 QString version = base.attribute(
"version");
389 if (version !=
"2") {
390 qWarning() <<
".action XML file" << actionDefinition <<
"has incorrect version; skipping.";
396 QDomElement actions = base.firstChild().toElement();
397 while (!actions.isNull()) {
400 QDomElement categoryTextNode = actions.firstChild().toElement();
401 QString categoryName = quietlyTranslate(categoryTextNode);
404 QDomElement actionXml = categoryTextNode.nextSiblingElement();
406 if (actionXml.isNull()) {
407 qWarning() << actionDefinition <<
"does not contain any valid actions! (Or the text element was left empty...)";
411 while (!actionXml.isNull()) {
412 if (actionXml.tagName() ==
"Action") {
414 QString
name = actionXml.attribute(
"name");
417 if (
name.isEmpty()) {
418 qWarning() <<
"Unnamed action in definitions file " << actionDefinition;
421 else if (actionInfoList.contains(name)) {
422 qWarning() <<
"NOT COOL: Duplicated action name from xml data: " <<
name;
427 info.xmlData = actionXml;
431 QString shortcutText = getChildContentForOS(actionXml,
"shortcut",
"macos");
433 QString shortcutText = getChildContentForOS(actionXml,
"shortcut");
435 if (!shortcutText.isEmpty()) {
436 info.setDefaultShortcuts(QKeySequence::listFromString(shortcutText));
439 info.categoryName = categoryName;
440 info.collectionName = collectionName;
442 actionInfoList.insert(name,info);
445 actionXml = actionXml.nextSiblingElement();
447 actions = actions.nextSiblingElement();
452void KisActionRegistry::Private::loadCustomShortcuts(QString filename)
454 const KConfigGroup localShortcuts(KSharedConfig::openConfig(filename),
455 QStringLiteral(
"Shortcuts"));
457 if (!localShortcuts.exists()) {
462 for (
auto i = actionInfoList.begin(); i != actionInfoList.end(); ++i) {
463 if (localShortcuts.hasKey(i.key())) {
464 QString entry = localShortcuts.readEntry(i.key(), QString());
465 if (entry == QStringLiteral(
"none")) {
468 i.value().setCustomShortcuts(QKeySequence::listFromString(entry),
false);
481 : componentName(_componentName),
482 categoryName(_categoryName),
489 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)