1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1999, 2000 Simon Hausmann <hausmann@kde.org>
4 SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "config-xmlgui.h"
10
11#include "kxmlguifactory.h"
12
13#include "debug.h"
14#include "kactioncollection.h"
15#include "kshortcutschemeshelper_p.h"
16#include "kshortcutsdialog.h"
17#include "kxmlguibuilder.h"
18#include "kxmlguiclient.h"
19#include "kxmlguifactory_p.h"
20
21#include <QAction>
22#include <QCoreApplication>
23#include <QDir>
24#include <QDomDocument>
25#include <QFile>
26#include <QStandardPaths>
27#include <QTextStream>
28#include <QVariant>
29#include <QWidget>
30
31#include <KConfigGroup>
32#include <KSharedConfig>
33#if HAVE_GLOBALACCEL
34#include <KGlobalAccel>
35#endif
36
37using namespace KXMLGUI;
38
39class KXMLGUIFactoryPrivate : public BuildState
40{
41public:
42 enum ShortcutOption {
43 SetActiveShortcut = 1,
44 SetDefaultShortcut = 2
45 };
46
47 KXMLGUIFactoryPrivate()
48 {
49 m_rootNode = new ContainerNode(nullptr, QString(), QString());
50 attrName = QStringLiteral("name");
51 }
52 ~KXMLGUIFactoryPrivate()
53 {
54 delete m_rootNode;
55 }
56
57 void pushState()
58 {
59 m_stateStack.push(t: *this);
60 }
61
62 void popState()
63 {
64 BuildState::operator=(m_stateStack.pop());
65 }
66
67 bool emptyState() const
68 {
69 return m_stateStack.isEmpty();
70 }
71
72 QWidget *findRecursive(KXMLGUI::ContainerNode *node, bool tag);
73 QList<QWidget *> findRecursive(KXMLGUI::ContainerNode *node, const QString &tagName);
74 void applyActionProperties(const QDomElement &element, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut);
75 void configureAction(QAction *action, const QDomNamedNodeMap &attributes, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut);
76 void configureAction(QAction *action, const QDomAttr &attribute, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut);
77
78 void applyShortcutScheme(const QString &schemeName, KXMLGUIClient *client, const QList<QAction *> &actions);
79 void refreshActionProperties(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &doc);
80 void saveDefaultActionProperties(const QList<QAction *> &actions);
81
82 ContainerNode *m_rootNode;
83
84 /*
85 * Contains the container which is searched for in ::container .
86 */
87 QString m_containerName;
88
89 /*
90 * List of all clients
91 */
92 QList<KXMLGUIClient *> m_clients;
93
94 QString attrName;
95
96 BuildStateStack m_stateStack;
97};
98
99QString KXMLGUIFactory::readConfigFile(const QString &filename, const QString &_componentName)
100{
101 QString componentName = _componentName.isEmpty() ? QCoreApplication::applicationName() : _componentName;
102 QString xml_file;
103
104 if (!QDir::isRelativePath(path: filename)) {
105 xml_file = filename;
106 } else {
107 // first look for any custom user config, admin config or the default deployed as file
108 xml_file = QStandardPaths::locate(type: QStandardPaths::GenericDataLocation, fileName: QLatin1String("kxmlgui5/") + componentName + QLatin1Char('/') + filename);
109 if (!QFile::exists(fileName: xml_file)) {
110 // fall-back to any built-in resource file
111 xml_file = QLatin1String(":/kxmlgui5/") + componentName + QLatin1Char('/') + filename;
112 }
113 }
114
115 QFile file(xml_file);
116 if (xml_file.isEmpty() || !file.open(flags: QIODevice::ReadOnly)) {
117 qCCritical(DEBUG_KXMLGUI) << "No such XML file" << filename;
118 return QString();
119 }
120
121 QByteArray buffer(file.readAll());
122 return QString::fromUtf8(utf8: buffer.constData(), size: buffer.size());
123}
124
125bool KXMLGUIFactory::saveConfigFile(const QDomDocument &doc, const QString &filename, const QString &_componentName)
126{
127 QString componentName = _componentName.isEmpty() ? QCoreApplication::applicationName() : _componentName;
128 QString xml_file(filename);
129
130 if (QDir::isRelativePath(path: xml_file)) {
131 xml_file = QStandardPaths::writableLocation(type: QStandardPaths::GenericDataLocation) + QLatin1String("/kxmlgui5/%1/%2").arg(args&: componentName, args: filename);
132 }
133
134 QFileInfo fileInfo(xml_file);
135 QDir().mkpath(dirPath: fileInfo.absolutePath());
136 QFile file(xml_file);
137 if (xml_file.isEmpty() || !file.open(flags: QIODevice::WriteOnly)) {
138 qCCritical(DEBUG_KXMLGUI) << "Could not write to" << filename;
139 return false;
140 }
141
142 // write out our document
143 QTextStream ts(&file);
144 ts << doc;
145
146 file.close();
147 return true;
148}
149
150KXMLGUIFactory::KXMLGUIFactory(KXMLGUIBuilder *builder, QObject *parent)
151 : QObject(parent)
152 , d(new KXMLGUIFactoryPrivate)
153{
154 Q_INIT_RESOURCE(kxmlgui);
155
156 d->builder = builder;
157 d->guiClient = nullptr;
158 if (d->builder) {
159 d->builderContainerTags = d->builder->containerTags();
160 d->builderCustomTags = d->builder->customTags();
161 }
162}
163
164KXMLGUIFactory::~KXMLGUIFactory()
165{
166 for (KXMLGUIClient *client : std::as_const(t&: d->m_clients)) {
167 client->setFactory(nullptr);
168 }
169}
170
171void KXMLGUIFactory::addClient(KXMLGUIClient *client)
172{
173 // qCDebug(DEBUG_KXMLGUI) << client;
174 if (client->factory()) {
175 if (client->factory() == this) {
176 return;
177 } else {
178 client->factory()->removeClient(client); // just in case someone does stupid things ;-)
179 }
180 }
181
182 if (d->emptyState()) {
183 Q_EMIT makingChanges(true);
184 }
185 d->pushState();
186
187 // QTime dt; dt.start();
188
189 d->guiClient = client;
190
191 // add this client to our client list
192 if (!d->m_clients.contains(t: client)) {
193 d->m_clients.append(t: client);
194 }
195 // else
196 // qCDebug(DEBUG_KXMLGUI) << "XMLGUI client already added " << client;
197
198 // Tell the client that plugging in is process and
199 // let it know what builder widget its mainwindow shortcuts
200 // should be attached to.
201 client->beginXMLPlug(d->builder->widget());
202
203 // try to use the build document for building the client's GUI, as the build document
204 // contains the correct container state information (like toolbar positions, sizes, etc.) .
205 // if there is non available, then use the "real" document.
206 QDomDocument doc = client->xmlguiBuildDocument();
207 if (doc.documentElement().isNull()) {
208 doc = client->domDocument();
209 }
210
211 QDomElement docElement = doc.documentElement();
212
213 d->m_rootNode->index = -1;
214
215 // cache some variables
216
217 d->clientName = docElement.attribute(name: d->attrName);
218 d->clientBuilder = client->clientBuilder();
219
220 if (d->clientBuilder) {
221 d->clientBuilderContainerTags = d->clientBuilder->containerTags();
222 d->clientBuilderCustomTags = d->clientBuilder->customTags();
223 } else {
224 d->clientBuilderContainerTags.clear();
225 d->clientBuilderCustomTags.clear();
226 }
227
228 // load shortcut schemes, user-defined shortcuts and other action properties
229 d->saveDefaultActionProperties(actions: client->actionCollection()->actions());
230 if (!doc.isNull()) {
231 d->refreshActionProperties(client, actions: client->actionCollection()->actions(), doc);
232 }
233
234 BuildHelper(*d, d->m_rootNode).build(element: docElement);
235
236 // let the client know that we built its GUI.
237 client->setFactory(this);
238
239 // call the finalizeGUI method, to fix up the positions of toolbars for example.
240 // Note: the client argument is ignored
241 d->builder->finalizeGUI(client: d->guiClient);
242
243 // reset some variables, for safety
244 d->BuildState::reset();
245
246 client->endXMLPlug();
247
248 d->popState();
249
250 Q_EMIT clientAdded(client);
251
252 // build child clients
253 const auto children = client->childClients();
254 for (KXMLGUIClient *child : children) {
255 addClient(client: child);
256 }
257
258 if (d->emptyState()) {
259 Q_EMIT makingChanges(false);
260 }
261 /*
262 QString unaddedActions;
263 Q_FOREACH (KActionCollection* ac, KActionCollection::allCollections())
264 Q_FOREACH (QAction* action, ac->actions())
265 if (action->associatedWidgets().isEmpty())
266 unaddedActions += action->objectName() + ' ';
267
268 if (!unaddedActions.isEmpty())
269 qCWarning(DEBUG_KXMLGUI) << "The following actions are not plugged into the gui (shortcuts will not work): " << unaddedActions;
270 */
271
272 // qCDebug(DEBUG_KXMLGUI) << "addClient took " << dt.elapsed();
273}
274
275void KXMLGUIFactory::refreshActionProperties()
276{
277 for (KXMLGUIClient *client : std::as_const(t&: d->m_clients)) {
278 d->guiClient = client;
279 QDomDocument doc = client->xmlguiBuildDocument();
280 if (doc.documentElement().isNull()) {
281 client->reloadXML();
282 doc = client->domDocument();
283 }
284 d->refreshActionProperties(client, actions: client->actionCollection()->actions(), doc);
285 }
286 d->guiClient = nullptr;
287}
288
289static QString currentShortcutScheme()
290{
291 const KConfigGroup cg = KSharedConfig::openConfig()->group(QStringLiteral("Shortcut Schemes"));
292 return cg.readEntry(key: "Current Scheme", aDefault: "Default");
293}
294
295// Find the right ActionProperties element, otherwise return null element
296static QDomElement findActionPropertiesElement(const QDomDocument &doc)
297{
298 const QLatin1String tagActionProp("ActionProperties");
299 const QString schemeName = currentShortcutScheme();
300 QDomElement e = doc.documentElement().firstChildElement();
301 for (; !e.isNull(); e = e.nextSiblingElement()) {
302 if (QString::compare(s1: e.tagName(), s2: tagActionProp, cs: Qt::CaseInsensitive) == 0
303 && (e.attribute(QStringLiteral("scheme"), QStringLiteral("Default")) == schemeName)) {
304 return e;
305 }
306 }
307 return QDomElement();
308}
309
310void KXMLGUIFactoryPrivate::refreshActionProperties(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &doc)
311{
312 // try to find and apply shortcuts schemes
313 const QString schemeName = KShortcutSchemesHelper::currentShortcutSchemeName();
314 // qCDebug(DEBUG_KXMLGUI) << client->componentName() << ": applying shortcut scheme" << schemeName;
315
316 if (schemeName != QLatin1String("Default")) {
317 applyShortcutScheme(schemeName, client, actions);
318 } else {
319 // apply saved default shortcuts
320 for (QAction *action : actions) {
321 QVariant savedDefaultShortcut = action->property(name: "_k_DefaultShortcut");
322 if (savedDefaultShortcut.isValid()) {
323 QList<QKeySequence> shortcut = savedDefaultShortcut.value<QList<QKeySequence>>();
324 action->setShortcuts(shortcut);
325 action->setProperty(name: "defaultShortcuts", value: QVariant::fromValue(value: shortcut));
326 // qCDebug(DEBUG_KXMLGUI) << "scheme said" << action->shortcut().toString() << "for action" << action->objectName();
327 } else {
328 action->setShortcuts(QList<QKeySequence>());
329 }
330 }
331 }
332
333 // try to find and apply user-defined shortcuts
334 const QDomElement actionPropElement = findActionPropertiesElement(doc);
335 if (!actionPropElement.isNull()) {
336 applyActionProperties(element: actionPropElement);
337 }
338}
339
340void KXMLGUIFactoryPrivate::saveDefaultActionProperties(const QList<QAction *> &actions)
341{
342 // This method is called every time the user activated a new
343 // kxmlguiclient. We only want to execute the following code only once in
344 // the lifetime of an action.
345 for (QAction *action : actions) {
346 // Skip nullptr actions or those we have seen already.
347 if (!action || action->property(name: "_k_DefaultShortcut").isValid()) {
348 continue;
349 }
350
351 // Check if the default shortcut is set
352 QList<QKeySequence> defaultShortcut = action->property(name: "defaultShortcuts").value<QList<QKeySequence>>();
353 QList<QKeySequence> activeShortcut = action->shortcuts();
354 // qCDebug(DEBUG_KXMLGUI) << action->objectName() << "default=" << defaultShortcut.toString() << "active=" << activeShortcut.toString();
355
356 // Check if we have an empty default shortcut and an non empty
357 // custom shortcut. Print out a warning and correct the mistake.
358 if ((!activeShortcut.isEmpty()) && defaultShortcut.isEmpty()) {
359 qCCritical(DEBUG_KXMLGUI) << "Shortcut for action " << action->objectName() << action->text()
360 << "set with QAction::setShortcut()! Use KActionCollection::setDefaultShortcut(s) instead.";
361 action->setProperty(name: "_k_DefaultShortcut", value: QVariant::fromValue(value: activeShortcut));
362 } else {
363 action->setProperty(name: "_k_DefaultShortcut", value: QVariant::fromValue(value: defaultShortcut));
364 }
365 }
366}
367
368void KXMLGUIFactory::changeShortcutScheme(const QString &scheme)
369{
370 qCDebug(DEBUG_KXMLGUI) << "Changing shortcut scheme to" << scheme;
371 KConfigGroup cg = KSharedConfig::openConfig()->group(QStringLiteral("Shortcut Schemes"));
372 cg.writeEntry(key: "Current Scheme", value: scheme);
373
374 refreshActionProperties();
375}
376
377void KXMLGUIFactory::forgetClient(KXMLGUIClient *client)
378{
379 d->m_clients.erase(abegin: std::remove(first: d->m_clients.begin(), last: d->m_clients.end(), value: client), aend: d->m_clients.end());
380}
381
382void KXMLGUIFactory::removeClient(KXMLGUIClient *client)
383{
384 // qCDebug(DEBUG_KXMLGUI) << client;
385
386 // don't try to remove the client's GUI if we didn't build it
387 if (!client || client->factory() != this) {
388 return;
389 }
390
391 if (d->emptyState()) {
392 Q_EMIT makingChanges(true);
393 }
394
395 // remove this client from our client list
396 forgetClient(client);
397
398 // remove child clients first (create a copy of the list just in case the
399 // original list is modified directly or indirectly in removeClient())
400 const QList<KXMLGUIClient *> childClients(client->childClients());
401 for (KXMLGUIClient *child : childClients) {
402 removeClient(client: child);
403 }
404
405 // qCDebug(DEBUG_KXMLGUI) << "calling removeRecursive";
406
407 d->pushState();
408
409 // cache some variables
410
411 d->guiClient = client;
412 d->clientName = client->domDocument().documentElement().attribute(name: d->attrName);
413 d->clientBuilder = client->clientBuilder();
414
415 client->setFactory(nullptr);
416
417 // if we don't have a build document for that client, yet, then create one by
418 // cloning the original document, so that saving container information in the
419 // DOM tree does not touch the original document.
420 QDomDocument doc = client->xmlguiBuildDocument();
421 if (doc.documentElement().isNull()) {
422 doc = client->domDocument().cloneNode(deep: true).toDocument();
423 client->setXMLGUIBuildDocument(doc);
424 }
425
426 d->m_rootNode->destruct(element: doc.documentElement(), state&: *d);
427
428 // reset some variables
429 d->BuildState::reset();
430
431 // This will destruct the KAccel object built around the given widget.
432 client->prepareXMLUnplug(d->builder->widget());
433
434 d->popState();
435
436 if (d->emptyState()) {
437 Q_EMIT makingChanges(false);
438 }
439
440 Q_EMIT clientRemoved(client);
441}
442
443QList<KXMLGUIClient *> KXMLGUIFactory::clients() const
444{
445 return d->m_clients;
446}
447
448QWidget *KXMLGUIFactory::container(const QString &containerName, KXMLGUIClient *client, bool useTagName)
449{
450 d->pushState();
451 d->m_containerName = containerName;
452 d->guiClient = client;
453
454 QWidget *result = d->findRecursive(node: d->m_rootNode, tag: useTagName);
455
456 d->guiClient = nullptr;
457 d->m_containerName.clear();
458
459 d->popState();
460
461 return result;
462}
463
464QList<QWidget *> KXMLGUIFactory::containers(const QString &tagName)
465{
466 return d->findRecursive(node: d->m_rootNode, tagName);
467}
468
469void KXMLGUIFactory::reset()
470{
471 d->m_rootNode->reset();
472
473 d->m_rootNode->clearChildren();
474}
475
476void KXMLGUIFactory::resetContainer(const QString &containerName, bool useTagName)
477{
478 if (containerName.isEmpty()) {
479 return;
480 }
481
482 ContainerNode *container = d->m_rootNode->findContainer(name: containerName, tag: useTagName);
483 if (container && container->parent) {
484 container->parent->removeChild(child: container);
485 }
486}
487
488QWidget *KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node, bool tag)
489{
490 if (((!tag && node->name == m_containerName) || (tag && node->tagName == m_containerName)) //
491 && (!guiClient || node->client == guiClient)) {
492 return node->container;
493 }
494
495 for (ContainerNode *child : std::as_const(t&: node->children)) {
496 QWidget *cont = findRecursive(node: child, tag);
497 if (cont) {
498 return cont;
499 }
500 }
501
502 return nullptr;
503}
504
505// Case insensitive equality without calling toLower which allocates a new string
506static inline bool equals(const QString &str1, const char *str2)
507{
508 return str1.compare(other: QLatin1String(str2), cs: Qt::CaseInsensitive) == 0;
509}
510static inline bool equals(const QString &str1, const QString &str2)
511{
512 return str1.compare(s: str2, cs: Qt::CaseInsensitive) == 0;
513}
514
515QList<QWidget *> KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node, const QString &tagName)
516{
517 QList<QWidget *> res;
518
519 if (equals(str1: node->tagName, str2: tagName)) {
520 res.append(t: node->container);
521 }
522
523 for (KXMLGUI::ContainerNode *child : std::as_const(t&: node->children)) {
524 res << findRecursive(node: child, tagName);
525 }
526
527 return res;
528}
529
530void KXMLGUIFactory::plugActionList(KXMLGUIClient *client, const QString &name, const QList<QAction *> &actionList)
531{
532 d->pushState();
533 d->guiClient = client;
534 d->actionListName = name;
535 d->actionList = actionList;
536 d->clientName = client->domDocument().documentElement().attribute(name: d->attrName);
537
538 d->m_rootNode->plugActionList(state&: *d);
539
540 // Load shortcuts for these new actions
541 d->saveDefaultActionProperties(actions: actionList);
542 d->refreshActionProperties(client, actions: actionList, doc: client->domDocument());
543
544 d->BuildState::reset();
545 d->popState();
546}
547
548void KXMLGUIFactory::unplugActionList(KXMLGUIClient *client, const QString &name)
549{
550 d->pushState();
551 d->guiClient = client;
552 d->actionListName = name;
553 d->clientName = client->domDocument().documentElement().attribute(name: d->attrName);
554
555 d->m_rootNode->unplugActionList(state&: *d);
556
557 d->BuildState::reset();
558 d->popState();
559}
560
561void KXMLGUIFactoryPrivate::applyActionProperties(const QDomElement &actionPropElement, ShortcutOption shortcutOption)
562{
563 for (QDomElement e = actionPropElement.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
564 if (!equals(str1: e.tagName(), str2: "action")) {
565 continue;
566 }
567
568 QAction *action = guiClient->action(element: e);
569 if (!action) {
570 continue;
571 }
572
573 configureAction(action, attributes: e.attributes(), shortcutOption);
574 }
575}
576
577void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomNamedNodeMap &attributes, ShortcutOption shortcutOption)
578{
579 for (int i = 0; i < attributes.length(); i++) {
580 QDomAttr attr = attributes.item(index: i).toAttr();
581 if (attr.isNull()) {
582 continue;
583 }
584
585 configureAction(action, attribute: attr, shortcutOption);
586 }
587}
588
589void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomAttr &attribute, ShortcutOption shortcutOption)
590{
591 QString attrName = attribute.name();
592 // If the attribute is a deprecated "accel", change to "shortcut".
593 if (equals(str1: attrName, str2: "accel")) {
594 attrName = QStringLiteral("shortcut");
595 }
596
597 // No need to re-set name, particularly since it's "objectName" in Qt4
598 if (equals(str1: attrName, str2: "name")) {
599 return;
600 }
601
602 if (equals(str1: attrName, str2: "icon")) {
603 action->setIcon(QIcon::fromTheme(name: attribute.value()));
604 return;
605 }
606
607 QVariant propertyValue;
608
609 const int propertyType = action->property(name: attrName.toLatin1().constData()).typeId();
610 bool isShortcut = (propertyType == QMetaType::QKeySequence);
611
612 if (propertyType == QMetaType::Int) {
613 propertyValue = QVariant(attribute.value().toInt());
614 } else if (propertyType == QMetaType::UInt) {
615 propertyValue = QVariant(attribute.value().toUInt());
616 } else if (isShortcut) {
617 // Setting the shortcut by property also sets the default shortcut (which is incorrect), so we have to do it directly
618 if (attrName == QLatin1String("globalShortcut")) {
619#if HAVE_GLOBALACCEL
620 KGlobalAccel::self()->setShortcut(action, shortcut: QKeySequence::listFromString(str: attribute.value()));
621#endif
622 } else {
623 action->setShortcuts(QKeySequence::listFromString(str: attribute.value()));
624 }
625 if (shortcutOption & KXMLGUIFactoryPrivate::SetDefaultShortcut) {
626 action->setProperty(name: "defaultShortcuts", value: QVariant::fromValue(value: QKeySequence::listFromString(str: attribute.value())));
627 }
628 } else {
629 propertyValue = QVariant(attribute.value());
630 }
631 if (!isShortcut && !action->setProperty(name: attrName.toLatin1().constData(), value: propertyValue)) {
632 qCWarning(DEBUG_KXMLGUI) << "Error: Unknown action property " << attrName << " will be ignored!";
633 }
634}
635
636void KXMLGUIFactoryPrivate::applyShortcutScheme(const QString &schemeName, KXMLGUIClient *client, const QList<QAction *> &actions)
637{
638 // First clear all existing shortcuts
639 for (QAction *action : actions) {
640 action->setShortcuts(QList<QKeySequence>());
641 // We clear the default shortcut as well because the shortcut scheme will set its own defaults
642 action->setProperty(name: "defaultShortcuts", value: QVariant::fromValue(value: QList<QKeySequence>()));
643 }
644
645 // Find the document for the shortcut scheme using the current application path.
646 // This allows to install a single XML file for a shortcut scheme for kdevelop
647 // rather than 10.
648 // Also look for the current xmlguiclient path.
649 // Per component xml files make sense for making kmail shortcuts available in kontact.
650 QString schemeFileName = KShortcutSchemesHelper::shortcutSchemeFileName(componentName: client->componentName(), schemeName);
651 if (schemeFileName.isEmpty()) {
652 schemeFileName = KShortcutSchemesHelper::applicationShortcutSchemeFileName(schemeName);
653 }
654 if (schemeFileName.isEmpty()) {
655 qCWarning(DEBUG_KXMLGUI) << client->componentName() << ": shortcut scheme file not found:" << schemeName << "after trying"
656 << QCoreApplication::applicationName() << "and" << client->componentName();
657 return;
658 }
659
660 QDomDocument scheme;
661 QFile schemeFile(schemeFileName);
662 if (schemeFile.open(flags: QIODevice::ReadOnly)) {
663 qCDebug(DEBUG_KXMLGUI) << client->componentName() << ": found shortcut scheme XML" << schemeFileName;
664 scheme.setContent(device: &schemeFile);
665 }
666
667 if (scheme.isNull()) {
668 return;
669 }
670
671 QDomElement docElement = scheme.documentElement();
672 QDomElement actionPropElement = docElement.namedItem(QStringLiteral("ActionProperties")).toElement();
673
674 // Check if we really have the shortcut configuration here
675 if (!actionPropElement.isNull()) {
676 // qCDebug(DEBUG_KXMLGUI) << "Applying shortcut scheme for XMLGUI client" << client->componentName();
677
678 // Apply all shortcuts we have
679 applyActionProperties(actionPropElement, shortcutOption: KXMLGUIFactoryPrivate::SetDefaultShortcut);
680 //} else {
681 // qCDebug(DEBUG_KXMLGUI) << "Invalid shortcut scheme file";
682 }
683}
684
685void KXMLGUIFactory::showConfigureShortcutsDialog()
686{
687 auto *dlg = new KShortcutsDialog(qobject_cast<QWidget *>(o: parent()));
688 dlg->setAttribute(Qt::WA_DeleteOnClose);
689
690 for (KXMLGUIClient *client : std::as_const(t&: d->m_clients)) {
691 if (client) {
692 qCDebug(DEBUG_KXMLGUI) << "Adding collection from client" << client->componentName() << "with" << client->actionCollection()->count() << "actions";
693
694 dlg->addCollection(collection: client->actionCollection(), title: client->componentName());
695 }
696 }
697
698 connect(sender: dlg, signal: &KShortcutsDialog::saved, context: this, slot: &KXMLGUIFactory::shortcutsSaved);
699 dlg->configure(saveSettings: true /*save settings on accept*/);
700}
701
702// Find or create
703QDomElement KXMLGUIFactory::actionPropertiesElement(QDomDocument &doc)
704{
705 // first, lets see if we have existing properties
706 QDomElement elem = findActionPropertiesElement(doc);
707
708 // if there was none, create one
709 if (elem.isNull()) {
710 elem = doc.createElement(QStringLiteral("ActionProperties"));
711 elem.setAttribute(QStringLiteral("scheme"), value: currentShortcutScheme());
712 doc.documentElement().appendChild(newChild: elem);
713 }
714 return elem;
715}
716
717QDomElement KXMLGUIFactory::findActionByName(QDomElement &elem, const QString &sName, bool create)
718{
719 const QLatin1String attrName("name");
720 for (QDomNode it = elem.firstChild(); !it.isNull(); it = it.nextSibling()) {
721 QDomElement e = it.toElement();
722 if (e.attribute(name: attrName) == sName) {
723 return e;
724 }
725 }
726
727 if (create) {
728 QDomElement act_elem = elem.ownerDocument().createElement(QStringLiteral("Action"));
729 act_elem.setAttribute(name: attrName, value: sName);
730 elem.appendChild(newChild: act_elem);
731 return act_elem;
732 }
733 return QDomElement();
734}
735
736#include "moc_kxmlguifactory.cpp"
737

source code of kxmlgui/src/kxmlguifactory.cpp