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

source code of kxmlgui/src/kxmlguifactory.cpp