File indexing completed on 2024-04-14 03:57:15

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1999, 2000 Simon Hausmann <hausmann@kde.org>
0004     SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "config-xmlgui.h"
0010 
0011 #include "kxmlguifactory.h"
0012 
0013 #include "debug.h"
0014 #include "kactioncollection.h"
0015 #include "kshortcutschemeshelper_p.h"
0016 #include "kshortcutsdialog.h"
0017 #include "kxmlguibuilder.h"
0018 #include "kxmlguiclient.h"
0019 #include "kxmlguifactory_p.h"
0020 
0021 #include <QAction>
0022 #include <QCoreApplication>
0023 #include <QDir>
0024 #include <QDomDocument>
0025 #include <QFile>
0026 #include <QStandardPaths>
0027 #include <QTextStream>
0028 #include <QVariant>
0029 #include <QWidget>
0030 
0031 #include <KConfigGroup>
0032 #include <KSharedConfig>
0033 #if HAVE_GLOBALACCEL
0034 #include <KGlobalAccel>
0035 #endif
0036 
0037 using namespace KXMLGUI;
0038 
0039 class KXMLGUIFactoryPrivate : public BuildState
0040 {
0041 public:
0042     enum ShortcutOption { SetActiveShortcut = 1, SetDefaultShortcut = 2 };
0043 
0044     KXMLGUIFactoryPrivate()
0045     {
0046         m_rootNode = new ContainerNode(nullptr, QString(), QString());
0047         attrName = QStringLiteral("name");
0048     }
0049     ~KXMLGUIFactoryPrivate()
0050     {
0051         delete m_rootNode;
0052     }
0053 
0054     void pushState()
0055     {
0056         m_stateStack.push(*this);
0057     }
0058 
0059     void popState()
0060     {
0061         BuildState::operator=(m_stateStack.pop());
0062     }
0063 
0064     bool emptyState() const
0065     {
0066         return m_stateStack.isEmpty();
0067     }
0068 
0069     QWidget *findRecursive(KXMLGUI::ContainerNode *node, bool tag);
0070     QList<QWidget *> findRecursive(KXMLGUI::ContainerNode *node, const QString &tagName);
0071     void applyActionProperties(const QDomElement &element, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut);
0072     void configureAction(QAction *action, const QDomNamedNodeMap &attributes, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut);
0073     void configureAction(QAction *action, const QDomAttr &attribute, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut);
0074 
0075     void applyShortcutScheme(const QString &schemeName, KXMLGUIClient *client, const QList<QAction *> &actions);
0076     void refreshActionProperties(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &doc);
0077     void saveDefaultActionProperties(const QList<QAction *> &actions);
0078 
0079     ContainerNode *m_rootNode;
0080 
0081     /*
0082      * Contains the container which is searched for in ::container .
0083      */
0084     QString m_containerName;
0085 
0086     /*
0087      * List of all clients
0088      */
0089     QList<KXMLGUIClient *> m_clients;
0090 
0091     QString attrName;
0092 
0093     BuildStateStack m_stateStack;
0094 };
0095 
0096 QString KXMLGUIFactory::readConfigFile(const QString &filename, const QString &_componentName)
0097 {
0098     QString componentName = _componentName.isEmpty() ? QCoreApplication::applicationName() : _componentName;
0099     QString xml_file;
0100 
0101     if (!QDir::isRelativePath(filename)) {
0102         xml_file = filename;
0103     } else {
0104         // first look for any custom user config, admin config or the default deployed as file
0105         xml_file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kxmlgui5/") + componentName + QLatin1Char('/') + filename);
0106         if (!QFile::exists(xml_file)) {
0107             // fall-back to any built-in resource file
0108             xml_file = QLatin1String(":/kxmlgui5/") + componentName + QLatin1Char('/') + filename;
0109         }
0110     }
0111 
0112     QFile file(xml_file);
0113     if (xml_file.isEmpty() || !file.open(QIODevice::ReadOnly)) {
0114         qCCritical(DEBUG_KXMLGUI) << "No such XML file" << filename;
0115         return QString();
0116     }
0117 
0118     QByteArray buffer(file.readAll());
0119     return QString::fromUtf8(buffer.constData(), buffer.size());
0120 }
0121 
0122 bool KXMLGUIFactory::saveConfigFile(const QDomDocument &doc, const QString &filename, const QString &_componentName)
0123 {
0124     QString componentName = _componentName.isEmpty() ? QCoreApplication::applicationName() : _componentName;
0125     QString xml_file(filename);
0126 
0127     if (QDir::isRelativePath(xml_file)) {
0128         xml_file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kxmlgui5/%1/%2").arg(componentName, filename);
0129     }
0130 
0131     QFileInfo fileInfo(xml_file);
0132     QDir().mkpath(fileInfo.absolutePath());
0133     QFile file(xml_file);
0134     if (xml_file.isEmpty() || !file.open(QIODevice::WriteOnly)) {
0135         qCCritical(DEBUG_KXMLGUI) << "Could not write to" << filename;
0136         return false;
0137     }
0138 
0139     // write out our document
0140     QTextStream ts(&file);
0141     ts << doc;
0142 
0143     file.close();
0144     return true;
0145 }
0146 
0147 KXMLGUIFactory::KXMLGUIFactory(KXMLGUIBuilder *builder, QObject *parent)
0148     : QObject(parent)
0149     , d(new KXMLGUIFactoryPrivate)
0150 {
0151     Q_INIT_RESOURCE(kxmlgui);
0152 
0153     d->builder = builder;
0154     d->guiClient = nullptr;
0155     if (d->builder) {
0156         d->builderContainerTags = d->builder->containerTags();
0157         d->builderCustomTags = d->builder->customTags();
0158     }
0159 }
0160 
0161 KXMLGUIFactory::~KXMLGUIFactory()
0162 {
0163     for (KXMLGUIClient *client : std::as_const(d->m_clients)) {
0164         client->setFactory(nullptr);
0165     }
0166 }
0167 
0168 void KXMLGUIFactory::addClient(KXMLGUIClient *client)
0169 {
0170     // qCDebug(DEBUG_KXMLGUI) << client;
0171     if (client->factory()) {
0172         if (client->factory() == this) {
0173             return;
0174         } else {
0175             client->factory()->removeClient(client); // just in case someone does stupid things ;-)
0176         }
0177     }
0178 
0179     if (d->emptyState()) {
0180         Q_EMIT makingChanges(true);
0181     }
0182     d->pushState();
0183 
0184     //    QTime dt; dt.start();
0185 
0186     d->guiClient = client;
0187 
0188     // add this client to our client list
0189     if (!d->m_clients.contains(client)) {
0190         d->m_clients.append(client);
0191     }
0192     // else
0193     // qCDebug(DEBUG_KXMLGUI) << "XMLGUI client already added " << client;
0194 
0195     // Tell the client that plugging in is process and
0196     //  let it know what builder widget its mainwindow shortcuts
0197     //  should be attached to.
0198     client->beginXMLPlug(d->builder->widget());
0199 
0200     // try to use the build document for building the client's GUI, as the build document
0201     // contains the correct container state information (like toolbar positions, sizes, etc.) .
0202     // if there is non available, then use the "real" document.
0203     QDomDocument doc = client->xmlguiBuildDocument();
0204     if (doc.documentElement().isNull()) {
0205         doc = client->domDocument();
0206     }
0207 
0208     QDomElement docElement = doc.documentElement();
0209 
0210     d->m_rootNode->index = -1;
0211 
0212     // cache some variables
0213 
0214     d->clientName = docElement.attribute(d->attrName);
0215     d->clientBuilder = client->clientBuilder();
0216 
0217     if (d->clientBuilder) {
0218         d->clientBuilderContainerTags = d->clientBuilder->containerTags();
0219         d->clientBuilderCustomTags = d->clientBuilder->customTags();
0220     } else {
0221         d->clientBuilderContainerTags.clear();
0222         d->clientBuilderCustomTags.clear();
0223     }
0224 
0225     // load shortcut schemes, user-defined shortcuts and other action properties
0226     d->saveDefaultActionProperties(client->actionCollection()->actions());
0227     if (!doc.isNull()) {
0228         d->refreshActionProperties(client, client->actionCollection()->actions(), doc);
0229     }
0230 
0231     BuildHelper(*d, d->m_rootNode).build(docElement);
0232 
0233     // let the client know that we built its GUI.
0234     client->setFactory(this);
0235 
0236     // call the finalizeGUI method, to fix up the positions of toolbars for example.
0237     // Note: the client argument is ignored
0238     d->builder->finalizeGUI(d->guiClient);
0239 
0240     // reset some variables, for safety
0241     d->BuildState::reset();
0242 
0243     client->endXMLPlug();
0244 
0245     d->popState();
0246 
0247     Q_EMIT clientAdded(client);
0248 
0249     // build child clients
0250     const auto children = client->childClients();
0251     for (KXMLGUIClient *child : children) {
0252         addClient(child);
0253     }
0254 
0255     if (d->emptyState()) {
0256         Q_EMIT makingChanges(false);
0257     }
0258     /*
0259         QString unaddedActions;
0260         Q_FOREACH (KActionCollection* ac, KActionCollection::allCollections())
0261           Q_FOREACH (QAction* action, ac->actions())
0262             if (action->associatedWidgets().isEmpty())
0263               unaddedActions += action->objectName() + ' ';
0264 
0265         if (!unaddedActions.isEmpty())
0266           qCWarning(DEBUG_KXMLGUI) << "The following actions are not plugged into the gui (shortcuts will not work): " << unaddedActions;
0267     */
0268 
0269     //    qCDebug(DEBUG_KXMLGUI) << "addClient took " << dt.elapsed();
0270 }
0271 
0272 void KXMLGUIFactory::refreshActionProperties()
0273 {
0274     for (KXMLGUIClient *client : std::as_const(d->m_clients)) {
0275         d->guiClient = client;
0276         QDomDocument doc = client->xmlguiBuildDocument();
0277         if (doc.documentElement().isNull()) {
0278             client->reloadXML();
0279             doc = client->domDocument();
0280         }
0281         d->refreshActionProperties(client, client->actionCollection()->actions(), doc);
0282     }
0283     d->guiClient = nullptr;
0284 }
0285 
0286 static QString currentShortcutScheme()
0287 {
0288     const KConfigGroup cg = KSharedConfig::openConfig()->group(QStringLiteral("Shortcut Schemes"));
0289     return cg.readEntry("Current Scheme", "Default");
0290 }
0291 
0292 // Find the right ActionProperties element, otherwise return null element
0293 static QDomElement findActionPropertiesElement(const QDomDocument &doc)
0294 {
0295     const QLatin1String tagActionProp("ActionProperties");
0296     const QString schemeName = currentShortcutScheme();
0297     QDomElement e = doc.documentElement().firstChildElement();
0298     for (; !e.isNull(); e = e.nextSiblingElement()) {
0299         if (QString::compare(e.tagName(), tagActionProp, Qt::CaseInsensitive) == 0
0300             && (e.attribute(QStringLiteral("scheme"), QStringLiteral("Default")) == schemeName)) {
0301             return e;
0302         }
0303     }
0304     return QDomElement();
0305 }
0306 
0307 void KXMLGUIFactoryPrivate::refreshActionProperties(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &doc)
0308 {
0309     // try to find and apply shortcuts schemes
0310     const QString schemeName = KShortcutSchemesHelper::currentShortcutSchemeName();
0311     // qCDebug(DEBUG_KXMLGUI) << client->componentName() << ": applying shortcut scheme" << schemeName;
0312 
0313     if (schemeName != QLatin1String("Default")) {
0314         applyShortcutScheme(schemeName, client, actions);
0315     } else {
0316         // apply saved default shortcuts
0317         for (QAction *action : actions) {
0318             QVariant savedDefaultShortcut = action->property("_k_DefaultShortcut");
0319             if (savedDefaultShortcut.isValid()) {
0320                 QList<QKeySequence> shortcut = savedDefaultShortcut.value<QList<QKeySequence>>();
0321                 action->setShortcuts(shortcut);
0322                 action->setProperty("defaultShortcuts", QVariant::fromValue(shortcut));
0323                 // qCDebug(DEBUG_KXMLGUI) << "scheme said" << action->shortcut().toString() << "for action" << action->objectName();
0324             } else {
0325                 action->setShortcuts(QList<QKeySequence>());
0326             }
0327         }
0328     }
0329 
0330     // try to find and apply user-defined shortcuts
0331     const QDomElement actionPropElement = findActionPropertiesElement(doc);
0332     if (!actionPropElement.isNull()) {
0333         applyActionProperties(actionPropElement);
0334     }
0335 }
0336 
0337 void KXMLGUIFactoryPrivate::saveDefaultActionProperties(const QList<QAction *> &actions)
0338 {
0339     // This method is called every time the user activated a new
0340     // kxmlguiclient. We only want to execute the following code only once in
0341     // the lifetime of an action.
0342     for (QAction *action : actions) {
0343         // Skip nullptr actions or those we have seen already.
0344         if (!action || action->property("_k_DefaultShortcut").isValid()) {
0345             continue;
0346         }
0347 
0348         // Check if the default shortcut is set
0349         QList<QKeySequence> defaultShortcut = action->property("defaultShortcuts").value<QList<QKeySequence>>();
0350         QList<QKeySequence> activeShortcut = action->shortcuts();
0351         // qCDebug(DEBUG_KXMLGUI) << action->objectName() << "default=" << defaultShortcut.toString() << "active=" << activeShortcut.toString();
0352 
0353         // Check if we have an empty default shortcut and an non empty
0354         // custom shortcut. Print out a warning and correct the mistake.
0355         if ((!activeShortcut.isEmpty()) && defaultShortcut.isEmpty()) {
0356             qCCritical(DEBUG_KXMLGUI) << "Shortcut for action " << action->objectName() << action->text()
0357                                       << "set with QAction::setShortcut()! Use KActionCollection::setDefaultShortcut(s) instead.";
0358             action->setProperty("_k_DefaultShortcut", QVariant::fromValue(activeShortcut));
0359         } else {
0360             action->setProperty("_k_DefaultShortcut", QVariant::fromValue(defaultShortcut));
0361         }
0362     }
0363 }
0364 
0365 void KXMLGUIFactory::changeShortcutScheme(const QString &scheme)
0366 {
0367     qCDebug(DEBUG_KXMLGUI) << "Changing shortcut scheme to" << scheme;
0368     KConfigGroup cg = KSharedConfig::openConfig()->group(QStringLiteral("Shortcut Schemes"));
0369     cg.writeEntry("Current Scheme", scheme);
0370 
0371     refreshActionProperties();
0372 }
0373 
0374 void KXMLGUIFactory::forgetClient(KXMLGUIClient *client)
0375 {
0376     d->m_clients.erase(std::remove(d->m_clients.begin(), d->m_clients.end(), client), d->m_clients.end());
0377 }
0378 
0379 void KXMLGUIFactory::removeClient(KXMLGUIClient *client)
0380 {
0381     // qCDebug(DEBUG_KXMLGUI) << client;
0382 
0383     // don't try to remove the client's GUI if we didn't build it
0384     if (!client || client->factory() != this) {
0385         return;
0386     }
0387 
0388     if (d->emptyState()) {
0389         Q_EMIT makingChanges(true);
0390     }
0391 
0392     // remove this client from our client list
0393     forgetClient(client);
0394 
0395     // remove child clients first (create a copy of the list just in case the
0396     // original list is modified directly or indirectly in removeClient())
0397     const QList<KXMLGUIClient *> childClients(client->childClients());
0398     for (KXMLGUIClient *child : childClients) {
0399         removeClient(child);
0400     }
0401 
0402     // qCDebug(DEBUG_KXMLGUI) << "calling removeRecursive";
0403 
0404     d->pushState();
0405 
0406     // cache some variables
0407 
0408     d->guiClient = client;
0409     d->clientName = client->domDocument().documentElement().attribute(d->attrName);
0410     d->clientBuilder = client->clientBuilder();
0411 
0412     client->setFactory(nullptr);
0413 
0414     // if we don't have a build document for that client, yet, then create one by
0415     // cloning the original document, so that saving container information in the
0416     // DOM tree does not touch the original document.
0417     QDomDocument doc = client->xmlguiBuildDocument();
0418     if (doc.documentElement().isNull()) {
0419         doc = client->domDocument().cloneNode(true).toDocument();
0420         client->setXMLGUIBuildDocument(doc);
0421     }
0422 
0423     d->m_rootNode->destruct(doc.documentElement(), *d);
0424 
0425     // reset some variables
0426     d->BuildState::reset();
0427 
0428     // This will destruct the KAccel object built around the given widget.
0429     client->prepareXMLUnplug(d->builder->widget());
0430 
0431     d->popState();
0432 
0433     if (d->emptyState()) {
0434         Q_EMIT makingChanges(false);
0435     }
0436 
0437     Q_EMIT clientRemoved(client);
0438 }
0439 
0440 QList<KXMLGUIClient *> KXMLGUIFactory::clients() const
0441 {
0442     return d->m_clients;
0443 }
0444 
0445 QWidget *KXMLGUIFactory::container(const QString &containerName, KXMLGUIClient *client, bool useTagName)
0446 {
0447     d->pushState();
0448     d->m_containerName = containerName;
0449     d->guiClient = client;
0450 
0451     QWidget *result = d->findRecursive(d->m_rootNode, useTagName);
0452 
0453     d->guiClient = nullptr;
0454     d->m_containerName.clear();
0455 
0456     d->popState();
0457 
0458     return result;
0459 }
0460 
0461 QList<QWidget *> KXMLGUIFactory::containers(const QString &tagName)
0462 {
0463     return d->findRecursive(d->m_rootNode, tagName);
0464 }
0465 
0466 void KXMLGUIFactory::reset()
0467 {
0468     d->m_rootNode->reset();
0469 
0470     d->m_rootNode->clearChildren();
0471 }
0472 
0473 void KXMLGUIFactory::resetContainer(const QString &containerName, bool useTagName)
0474 {
0475     if (containerName.isEmpty()) {
0476         return;
0477     }
0478 
0479     ContainerNode *container = d->m_rootNode->findContainer(containerName, useTagName);
0480     if (container && container->parent) {
0481         container->parent->removeChild(container);
0482     }
0483 }
0484 
0485 QWidget *KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node, bool tag)
0486 {
0487     if (((!tag && node->name == m_containerName) || (tag && node->tagName == m_containerName)) //
0488         && (!guiClient || node->client == guiClient)) {
0489         return node->container;
0490     }
0491 
0492     for (ContainerNode *child : std::as_const(node->children)) {
0493         QWidget *cont = findRecursive(child, tag);
0494         if (cont) {
0495             return cont;
0496         }
0497     }
0498 
0499     return nullptr;
0500 }
0501 
0502 // Case insensitive equality without calling toLower which allocates a new string
0503 static inline bool equals(const QString &str1, const char *str2)
0504 {
0505     return str1.compare(QLatin1String(str2), Qt::CaseInsensitive) == 0;
0506 }
0507 static inline bool equals(const QString &str1, const QString &str2)
0508 {
0509     return str1.compare(str2, Qt::CaseInsensitive) == 0;
0510 }
0511 
0512 QList<QWidget *> KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node, const QString &tagName)
0513 {
0514     QList<QWidget *> res;
0515 
0516     if (equals(node->tagName, tagName)) {
0517         res.append(node->container);
0518     }
0519 
0520     for (KXMLGUI::ContainerNode *child : std::as_const(node->children)) {
0521         res << findRecursive(child, tagName);
0522     }
0523 
0524     return res;
0525 }
0526 
0527 void KXMLGUIFactory::plugActionList(KXMLGUIClient *client, const QString &name, const QList<QAction *> &actionList)
0528 {
0529     d->pushState();
0530     d->guiClient = client;
0531     d->actionListName = name;
0532     d->actionList = actionList;
0533     d->clientName = client->domDocument().documentElement().attribute(d->attrName);
0534 
0535     d->m_rootNode->plugActionList(*d);
0536 
0537     // Load shortcuts for these new actions
0538     d->saveDefaultActionProperties(actionList);
0539     d->refreshActionProperties(client, actionList, client->domDocument());
0540 
0541     d->BuildState::reset();
0542     d->popState();
0543 }
0544 
0545 void KXMLGUIFactory::unplugActionList(KXMLGUIClient *client, const QString &name)
0546 {
0547     d->pushState();
0548     d->guiClient = client;
0549     d->actionListName = name;
0550     d->clientName = client->domDocument().documentElement().attribute(d->attrName);
0551 
0552     d->m_rootNode->unplugActionList(*d);
0553 
0554     d->BuildState::reset();
0555     d->popState();
0556 }
0557 
0558 void KXMLGUIFactoryPrivate::applyActionProperties(const QDomElement &actionPropElement, ShortcutOption shortcutOption)
0559 {
0560     for (QDomElement e = actionPropElement.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
0561         if (!equals(e.tagName(), "action")) {
0562             continue;
0563         }
0564 
0565         QAction *action = guiClient->action(e);
0566         if (!action) {
0567             continue;
0568         }
0569 
0570         configureAction(action, e.attributes(), shortcutOption);
0571     }
0572 }
0573 
0574 void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomNamedNodeMap &attributes, ShortcutOption shortcutOption)
0575 {
0576     for (int i = 0; i < attributes.length(); i++) {
0577         QDomAttr attr = attributes.item(i).toAttr();
0578         if (attr.isNull()) {
0579             continue;
0580         }
0581 
0582         configureAction(action, attr, shortcutOption);
0583     }
0584 }
0585 
0586 void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomAttr &attribute, ShortcutOption shortcutOption)
0587 {
0588     QString attrName = attribute.name();
0589     // If the attribute is a deprecated "accel", change to "shortcut".
0590     if (equals(attrName, "accel")) {
0591         attrName = QStringLiteral("shortcut");
0592     }
0593 
0594     // No need to re-set name, particularly since it's "objectName" in Qt4
0595     if (equals(attrName, "name")) {
0596         return;
0597     }
0598 
0599     if (equals(attrName, "icon")) {
0600         action->setIcon(QIcon::fromTheme(attribute.value()));
0601         return;
0602     }
0603 
0604     QVariant propertyValue;
0605 
0606     const int propertyType = action->property(attrName.toLatin1().constData()).typeId();
0607     bool isShortcut = (propertyType == QMetaType::QKeySequence);
0608 
0609     if (propertyType == QMetaType::Int) {
0610         propertyValue = QVariant(attribute.value().toInt());
0611     } else if (propertyType == QMetaType::UInt) {
0612         propertyValue = QVariant(attribute.value().toUInt());
0613     } else if (isShortcut) {
0614         // Setting the shortcut by property also sets the default shortcut (which is incorrect), so we have to do it directly
0615         if (attrName == QLatin1String("globalShortcut")) {
0616 #if HAVE_GLOBALACCEL
0617             KGlobalAccel::self()->setShortcut(action, QKeySequence::listFromString(attribute.value()));
0618 #endif
0619         } else {
0620             action->setShortcuts(QKeySequence::listFromString(attribute.value()));
0621         }
0622         if (shortcutOption & KXMLGUIFactoryPrivate::SetDefaultShortcut) {
0623             action->setProperty("defaultShortcuts", QVariant::fromValue(QKeySequence::listFromString(attribute.value())));
0624         }
0625     } else {
0626         propertyValue = QVariant(attribute.value());
0627     }
0628     if (!isShortcut && !action->setProperty(attrName.toLatin1().constData(), propertyValue)) {
0629         qCWarning(DEBUG_KXMLGUI) << "Error: Unknown action property " << attrName << " will be ignored!";
0630     }
0631 }
0632 
0633 void KXMLGUIFactoryPrivate::applyShortcutScheme(const QString &schemeName, KXMLGUIClient *client, const QList<QAction *> &actions)
0634 {
0635     // First clear all existing shortcuts
0636     for (QAction *action : actions) {
0637         action->setShortcuts(QList<QKeySequence>());
0638         // We clear the default shortcut as well because the shortcut scheme will set its own defaults
0639         action->setProperty("defaultShortcuts", QVariant::fromValue(QList<QKeySequence>()));
0640     }
0641 
0642     // Find the document for the shortcut scheme using the current application path.
0643     // This allows to install a single XML file for a shortcut scheme for kdevelop
0644     // rather than 10.
0645     // Also look for the current xmlguiclient path.
0646     // Per component xml files make sense for making kmail shortcuts available in kontact.
0647     QString schemeFileName = KShortcutSchemesHelper::shortcutSchemeFileName(client->componentName(), schemeName);
0648     if (schemeFileName.isEmpty()) {
0649         schemeFileName = KShortcutSchemesHelper::applicationShortcutSchemeFileName(schemeName);
0650     }
0651     if (schemeFileName.isEmpty()) {
0652         qCWarning(DEBUG_KXMLGUI) << client->componentName() << ": shortcut scheme file not found:" << schemeName << "after trying"
0653                                  << QCoreApplication::applicationName() << "and" << client->componentName();
0654         return;
0655     }
0656 
0657     QDomDocument scheme;
0658     QFile schemeFile(schemeFileName);
0659     if (schemeFile.open(QIODevice::ReadOnly)) {
0660         qCDebug(DEBUG_KXMLGUI) << client->componentName() << ": found shortcut scheme XML" << schemeFileName;
0661         scheme.setContent(&schemeFile);
0662     }
0663 
0664     if (scheme.isNull()) {
0665         return;
0666     }
0667 
0668     QDomElement docElement = scheme.documentElement();
0669     QDomElement actionPropElement = docElement.namedItem(QStringLiteral("ActionProperties")).toElement();
0670 
0671     // Check if we really have the shortcut configuration here
0672     if (!actionPropElement.isNull()) {
0673         // qCDebug(DEBUG_KXMLGUI) << "Applying shortcut scheme for XMLGUI client" << client->componentName();
0674 
0675         // Apply all shortcuts we have
0676         applyActionProperties(actionPropElement, KXMLGUIFactoryPrivate::SetDefaultShortcut);
0677         //} else {
0678         // qCDebug(DEBUG_KXMLGUI) << "Invalid shortcut scheme file";
0679     }
0680 }
0681 
0682 void KXMLGUIFactory::showConfigureShortcutsDialog()
0683 {
0684     auto *dlg = new KShortcutsDialog(qobject_cast<QWidget *>(parent()));
0685     dlg->setAttribute(Qt::WA_DeleteOnClose);
0686 
0687     for (KXMLGUIClient *client : std::as_const(d->m_clients)) {
0688         if (client) {
0689             qCDebug(DEBUG_KXMLGUI) << "Adding collection from client" << client->componentName() << "with" << client->actionCollection()->count() << "actions";
0690 
0691             dlg->addCollection(client->actionCollection(), client->componentName());
0692         }
0693     }
0694 
0695     connect(dlg, &KShortcutsDialog::saved, this, &KXMLGUIFactory::shortcutsSaved);
0696     dlg->configure(true /*save settings on accept*/);
0697 }
0698 
0699 // Find or create
0700 QDomElement KXMLGUIFactory::actionPropertiesElement(QDomDocument &doc)
0701 {
0702     // first, lets see if we have existing properties
0703     QDomElement elem = findActionPropertiesElement(doc);
0704 
0705     // if there was none, create one
0706     if (elem.isNull()) {
0707         elem = doc.createElement(QStringLiteral("ActionProperties"));
0708         elem.setAttribute(QStringLiteral("scheme"), currentShortcutScheme());
0709         doc.documentElement().appendChild(elem);
0710     }
0711     return elem;
0712 }
0713 
0714 QDomElement KXMLGUIFactory::findActionByName(QDomElement &elem, const QString &sName, bool create)
0715 {
0716     const QLatin1String attrName("name");
0717     for (QDomNode it = elem.firstChild(); !it.isNull(); it = it.nextSibling()) {
0718         QDomElement e = it.toElement();
0719         if (e.attribute(attrName) == sName) {
0720             return e;
0721         }
0722     }
0723 
0724     if (create) {
0725         QDomElement act_elem = elem.ownerDocument().createElement(QStringLiteral("Action"));
0726         act_elem.setAttribute(attrName, sName);
0727         elem.appendChild(act_elem);
0728         return act_elem;
0729     }
0730     return QDomElement();
0731 }
0732 
0733 #include "moc_kxmlguifactory.cpp"