File indexing completed on 2024-06-09 04:26:05

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