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