File indexing completed on 2024-07-14 14:38:44

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2001 Simon Hausmann <hausmann@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kxmlguifactory_p.h"
0009 
0010 #include "ktoolbar.h"
0011 #include "kxmlguibuilder.h"
0012 #include "kxmlguiclient.h"
0013 
0014 #include <QVector>
0015 #include <QWidget>
0016 
0017 #include "debug.h"
0018 #include <cassert>
0019 
0020 using namespace KXMLGUI;
0021 
0022 void ActionList::plug(QWidget *container, int index) const
0023 {
0024     QAction *before = nullptr; // Insert after end of widget's current actions (default).
0025 
0026     if ((index < 0) || (index > container->actions().count())) {
0027         qCWarning(DEBUG_KXMLGUI) << "Index " << index << " is not within range (0 - " << container->actions().count() << ")";
0028     } else if (index != container->actions().count()) {
0029         before = container->actions().at(index); // Insert before indexed action.
0030     }
0031 
0032     for (QAction *action : *this) {
0033         container->insertAction(before, action);
0034         // before = action; // BUG FIX: do not insert actions in reverse order.
0035     }
0036 }
0037 
0038 ContainerNode::ContainerNode(QWidget *_container,
0039                              const QString &_tagName,
0040                              const QString &_name,
0041                              ContainerNode *_parent,
0042                              KXMLGUIClient *_client,
0043                              KXMLGUIBuilder *_builder,
0044                              QAction *_containerAction,
0045                              const QString &_mergingName,
0046                              const QString &_groupName,
0047                              const QStringList &customTags,
0048                              const QStringList &containerTags)
0049     : parent(_parent)
0050     , client(_client)
0051     , builder(_builder)
0052     , builderCustomTags(customTags)
0053     , builderContainerTags(containerTags)
0054     , container(_container)
0055     , containerAction(_containerAction)
0056     , tagName(_tagName)
0057     , name(_name)
0058     , groupName(_groupName)
0059     , index(0)
0060     , mergingName(_mergingName)
0061 {
0062     if (parent) {
0063         parent->children.append(this);
0064     }
0065 }
0066 
0067 ContainerNode::~ContainerNode()
0068 {
0069     qDeleteAll(children);
0070     qDeleteAll(clients);
0071 }
0072 
0073 void ContainerNode::removeChild(ContainerNode *child)
0074 {
0075     children.removeAll(child);
0076     deleteChild(child);
0077 }
0078 
0079 void ContainerNode::deleteChild(ContainerNode *child)
0080 {
0081     MergingIndexList::iterator mergingIt = findIndex(child->mergingName);
0082     adjustMergingIndices(-1, mergingIt, QString());
0083     delete child;
0084 }
0085 
0086 /*
0087  * Find a merging index with the given name. Used to find an index defined by <Merge name="blah"/>
0088  * or by a <DefineGroup name="foo" /> tag.
0089  */
0090 MergingIndexList::iterator ContainerNode::findIndex(const QString &name)
0091 {
0092     return std::find_if(mergingIndices.begin(), mergingIndices.end(), [&name](const MergingIndex &idx) {
0093         return idx.mergingName == name;
0094     });
0095 }
0096 
0097 /*
0098  * Find a container recursively with the given name. Either compares _name with the
0099  * container's tag name or the value of the container's name attribute. Specified by
0100  * the tag bool .
0101  */
0102 ContainerNode *ContainerNode::findContainer(const QString &_name, bool tag)
0103 {
0104     if ((tag && tagName == _name) || (!tag && name == _name)) {
0105         return this;
0106     }
0107 
0108     for (ContainerNode *child : std::as_const(children)) {
0109         ContainerNode *res = child->findContainer(_name, tag);
0110         if (res) {
0111             return res;
0112         }
0113     }
0114 
0115     return nullptr;
0116 }
0117 
0118 /*
0119  * Finds a child container node (not recursively) with the given name and tagname. Explicitly
0120  * leaves out container widgets specified in the exludeList . Also ensures that the containers
0121  * belongs to currClient.
0122  */
0123 ContainerNode *ContainerNode::findContainer(const QString &name, const QString &tagName, const QList<QWidget *> *excludeList, KXMLGUIClient * /*currClient*/)
0124 {
0125     if (!name.isEmpty()) {
0126         auto it = std::find_if(children.cbegin(), children.cend(), [&name, excludeList](ContainerNode *node) {
0127             return node->name == name && !excludeList->contains(node->container);
0128         });
0129         return it != children.cend() ? *it : nullptr;
0130     }
0131 
0132     if (!tagName.isEmpty()) {
0133         // It is a bad idea to also compare the client (node->client == currClient),
0134         // because we don't want to do so in situations like these:
0135         //
0136         // <MenuBar>
0137         //   <Menu>
0138         //     ...
0139         //
0140         // other client:
0141         // <MenuBar>
0142         //   <Menu>
0143         //    ...
0144         auto it = std::find_if(children.cbegin(), children.cend(), [&tagName, excludeList](ContainerNode *node) {
0145             return node->tagName == tagName && !excludeList->contains(node->container);
0146         });
0147         return it != children.cend() ? *it : nullptr;
0148     };
0149 
0150     return {};
0151 }
0152 
0153 ContainerClient *
0154 ContainerNode::findChildContainerClient(KXMLGUIClient *currentGUIClient, const QString &groupName, const MergingIndexList::iterator &mergingIdx)
0155 {
0156     auto it = std::find_if(clients.cbegin(), clients.cend(), [&groupName, currentGUIClient](ContainerClient *cl) {
0157         return cl->client == currentGUIClient && (groupName.isEmpty() || groupName == cl->groupName);
0158     });
0159     if (it != clients.cend()) {
0160         return *it;
0161     }
0162 
0163     ContainerClient *client = new ContainerClient;
0164     client->client = currentGUIClient;
0165     client->groupName = groupName;
0166 
0167     if (mergingIdx != mergingIndices.end()) {
0168         client->mergingName = (*mergingIdx).mergingName;
0169     }
0170 
0171     clients.append(client);
0172 
0173     return client;
0174 }
0175 
0176 void ContainerNode::plugActionList(BuildState &state)
0177 {
0178     MergingIndexList::iterator mIt(mergingIndices.begin());
0179     MergingIndexList::iterator mEnd(mergingIndices.end());
0180     for (; mIt != mEnd; ++mIt) {
0181         plugActionList(state, mIt);
0182     }
0183 
0184     for (ContainerNode *child : std::as_const(children)) {
0185         child->plugActionList(state);
0186     }
0187 }
0188 
0189 void ContainerNode::plugActionList(BuildState &state, const MergingIndexList::iterator &mergingIdxIt)
0190 {
0191     const QLatin1String tagActionList("actionlist");
0192 
0193     const MergingIndex &mergingIdx = *mergingIdxIt;
0194     if (mergingIdx.clientName != state.clientName) {
0195         return;
0196     }
0197     if (!mergingIdx.mergingName.startsWith(tagActionList)) {
0198         return;
0199     }
0200     const QString k = mergingIdx.mergingName.mid(tagActionList.size());
0201     if (k != state.actionListName) {
0202         return;
0203     }
0204 
0205     ContainerClient *client = findChildContainerClient(state.guiClient, QString(), mergingIndices.end());
0206 
0207     client->actionLists.insert(k, state.actionList);
0208 
0209     state.actionList.plug(container, mergingIdx.value);
0210 
0211     adjustMergingIndices(state.actionList.count(), mergingIdxIt, QString());
0212 }
0213 
0214 void ContainerNode::unplugActionList(BuildState &state)
0215 {
0216     MergingIndexList::iterator mIt(mergingIndices.begin());
0217     MergingIndexList::iterator mEnd(mergingIndices.end());
0218     for (; mIt != mEnd; ++mIt) {
0219         unplugActionList(state, mIt);
0220     }
0221 
0222     for (ContainerNode *child : std::as_const(children)) {
0223         child->unplugActionList(state);
0224     }
0225 }
0226 
0227 void ContainerNode::unplugActionList(BuildState &state, const MergingIndexList::iterator &mergingIdxIt)
0228 {
0229     const QLatin1String tagActionList("actionlist");
0230 
0231     MergingIndex mergingIdx = *mergingIdxIt;
0232 
0233     QString k = mergingIdx.mergingName;
0234 
0235     if (k.indexOf(tagActionList) == -1) {
0236         return;
0237     }
0238 
0239     k.remove(0, tagActionList.size());
0240 
0241     if (mergingIdx.clientName != state.clientName) {
0242         return;
0243     }
0244 
0245     if (k != state.actionListName) {
0246         return;
0247     }
0248 
0249     ContainerClient *client = findChildContainerClient(state.guiClient, QString(), mergingIndices.end());
0250 
0251     ActionListMap::Iterator lIt(client->actionLists.find(k));
0252     if (lIt == client->actionLists.end()) {
0253         return;
0254     }
0255 
0256     removeActions(lIt.value());
0257 
0258     client->actionLists.erase(lIt);
0259 }
0260 
0261 void ContainerNode::adjustMergingIndices(int offset, const MergingIndexList::iterator &it, const QString &currentClientName)
0262 {
0263     MergingIndexList::iterator mergingIt = it;
0264     MergingIndexList::iterator mergingEnd = mergingIndices.end();
0265 
0266     for (; mergingIt != mergingEnd; ++mergingIt) {
0267         if ((*mergingIt).clientName != currentClientName) {
0268             (*mergingIt).value += offset;
0269         }
0270     }
0271 
0272     index += offset;
0273 }
0274 
0275 bool ContainerNode::destruct(
0276     QDomElement element,
0277     BuildState &state) // krazy:exclude=passbyvalue (this is correct QDom usage, and a ref wouldn't allow passing doc.documentElement() as argument)
0278 {
0279     destructChildren(element, state);
0280 
0281     unplugActions(state);
0282 
0283     // remove all merging indices the client defined
0284     QMutableVectorIterator<MergingIndex> cmIt = mergingIndices;
0285     while (cmIt.hasNext()) {
0286         if (cmIt.next().clientName == state.clientName) {
0287             cmIt.remove();
0288         }
0289     }
0290 
0291     // ### check for merging index count, too?
0292     if (clients.isEmpty() && children.isEmpty() && container && client == state.guiClient) {
0293         QWidget *parentContainer = nullptr;
0294         if (parent && parent->container) {
0295             parentContainer = parent->container;
0296         }
0297 
0298         Q_ASSERT(builder);
0299         builder->removeContainer(container, parentContainer, element, containerAction);
0300 
0301         client = nullptr;
0302         return true;
0303     }
0304 
0305     if (client == state.guiClient) {
0306         client = nullptr;
0307     }
0308 
0309     return false;
0310 }
0311 
0312 void ContainerNode::destructChildren(const QDomElement &element, BuildState &state)
0313 {
0314     QMutableListIterator<ContainerNode *> childIt = children;
0315     while (childIt.hasNext()) {
0316         ContainerNode *childNode = childIt.next();
0317 
0318         QDomElement childElement = findElementForChild(element, childNode);
0319 
0320         // destruct returns true in case the container really got deleted
0321         if (childNode->destruct(childElement, state)) {
0322             deleteChild(childNode);
0323             childIt.remove();
0324         }
0325     }
0326 }
0327 
0328 QDomElement ContainerNode::findElementForChild(const QDomElement &baseElement, ContainerNode *childNode)
0329 {
0330     // ### slow
0331     for (QDomNode n = baseElement.firstChild(); !n.isNull(); n = n.nextSibling()) {
0332         QDomElement e = n.toElement();
0333         if (e.tagName().toLower() == childNode->tagName //
0334             && e.attribute(QStringLiteral("name")) == childNode->name) {
0335             return e;
0336         }
0337     }
0338 
0339     return QDomElement();
0340 }
0341 
0342 void ContainerNode::unplugActions(BuildState &state)
0343 {
0344     if (!container) {
0345         return;
0346     }
0347 
0348     QMutableListIterator<ContainerClient *> clientIt(clients);
0349     while (clientIt.hasNext()) {
0350         // only unplug the actions of the client we want to remove, as the container might be owned
0351         // by a different client
0352         ContainerClient *cClient = clientIt.next();
0353         if (cClient->client == state.guiClient) {
0354             unplugClient(cClient);
0355             delete cClient;
0356             clientIt.remove();
0357         }
0358     }
0359 }
0360 
0361 void ContainerNode::removeActions(const QList<QAction *> &actions)
0362 {
0363     for (QAction *action : actions) {
0364         const int pos = container->actions().indexOf(action);
0365         if (pos != -1) {
0366             container->removeAction(action);
0367             for (MergingIndex &idx : mergingIndices) {
0368                 if (idx.value > pos) {
0369                     --idx.value;
0370                 }
0371             }
0372             --index;
0373         }
0374     }
0375 }
0376 
0377 void ContainerNode::unplugClient(ContainerClient *client)
0378 {
0379     assert(builder);
0380 
0381     KToolBar *bar = qobject_cast<KToolBar *>(container);
0382     if (bar) {
0383         bar->removeXMLGUIClient(client->client);
0384     }
0385 
0386     // now quickly remove all custom elements (i.e. separators) and unplug all actions
0387     removeActions(client->customElements);
0388     removeActions(client->actions);
0389 
0390     // unplug all actionslists
0391 
0392     for (const auto &actionList : std::as_const(client->actionLists)) {
0393         removeActions(actionList);
0394     }
0395 }
0396 
0397 void ContainerNode::reset()
0398 {
0399     for (ContainerNode *child : std::as_const(children)) {
0400         child->reset();
0401     }
0402 
0403     if (client) {
0404         client->setFactory(nullptr);
0405     }
0406 }
0407 
0408 int ContainerNode::calcMergingIndex(const QString &mergingName, MergingIndexList::iterator &it, BuildState &state, bool ignoreDefaultMergingIndex)
0409 {
0410     const MergingIndexList::iterator mergingIt = findIndex(mergingName.isEmpty() ? state.clientName : mergingName);
0411     const MergingIndexList::iterator mergingEnd = mergingIndices.end();
0412 
0413     if (ignoreDefaultMergingIndex || (mergingIt == mergingEnd && state.currentDefaultMergingIt == mergingEnd)) {
0414         it = mergingEnd;
0415         return index;
0416     }
0417 
0418     if (mergingIt != mergingEnd) {
0419         it = mergingIt;
0420     } else {
0421         it = state.currentDefaultMergingIt;
0422     }
0423 
0424     return (*it).value;
0425 }
0426 
0427 void ContainerNode::dump(int offset)
0428 {
0429     QString indent;
0430     indent.fill(QLatin1Char(' '), offset);
0431     qCDebug(DEBUG_KXMLGUI) << qPrintable(indent) << name << tagName << groupName << mergingName << mergingIndices;
0432     for (ContainerNode *child : std::as_const(children)) {
0433         child->dump(offset + 2);
0434     }
0435 }
0436 
0437 // TODO: return a struct with 3 members rather than 1 ret val + 2 out params
0438 int BuildHelper::calcMergingIndex(const QDomElement &element, MergingIndexList::iterator &it, QString &group)
0439 {
0440     const QLatin1String attrGroup("group");
0441 
0442     bool haveGroup = false;
0443     group = element.attribute(attrGroup);
0444     if (!group.isEmpty()) {
0445         group.prepend(attrGroup);
0446         haveGroup = true;
0447     }
0448 
0449     int idx;
0450     if (haveGroup) {
0451         idx = parentNode->calcMergingIndex(group, it, m_state, ignoreDefaultMergingIndex);
0452     } else {
0453         it = m_state.currentClientMergingIt;
0454         if (it == parentNode->mergingIndices.end()) {
0455             idx = parentNode->index;
0456         } else {
0457             idx = (*it).value;
0458         }
0459     }
0460 
0461     return idx;
0462 }
0463 
0464 BuildHelper::BuildHelper(BuildState &state, ContainerNode *node)
0465     : containerClient(nullptr)
0466     , ignoreDefaultMergingIndex(false)
0467     , m_state(state)
0468     , parentNode(node)
0469 {
0470     // create a list of supported container and custom tags
0471     customTags = m_state.builderCustomTags;
0472     containerTags = m_state.builderContainerTags;
0473 
0474     if (parentNode->builder != m_state.builder) {
0475         customTags += parentNode->builderCustomTags;
0476         containerTags += parentNode->builderContainerTags;
0477     }
0478 
0479     if (m_state.clientBuilder) {
0480         customTags = m_state.clientBuilderCustomTags + customTags;
0481         containerTags = m_state.clientBuilderContainerTags + containerTags;
0482     }
0483 
0484     m_state.currentDefaultMergingIt = parentNode->findIndex(QStringLiteral("<default>"));
0485     parentNode->calcMergingIndex(QString(), m_state.currentClientMergingIt, m_state, /*ignoreDefaultMergingIndex*/ false);
0486 }
0487 
0488 void BuildHelper::build(const QDomElement &element)
0489 {
0490     for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
0491         QDomElement e = n.toElement();
0492         if (e.isNull()) {
0493             continue;
0494         }
0495         processElement(e);
0496     }
0497 }
0498 
0499 void BuildHelper::processElement(const QDomElement &e)
0500 {
0501     QString tag(e.tagName().toLower());
0502     QString currName(e.attribute(QStringLiteral("name")));
0503 
0504     const bool isActionTag = (tag == QLatin1String("action"));
0505 
0506     if (isActionTag || customTags.indexOf(tag) != -1) {
0507         processActionOrCustomElement(e, isActionTag);
0508     } else if (containerTags.indexOf(tag) != -1) {
0509         processContainerElement(e, tag, currName);
0510     } else if (tag == QLatin1String("merge") || tag == QLatin1String("definegroup") || tag == QLatin1String("actionlist")) {
0511         processMergeElement(tag, currName, e);
0512     } else if (tag == QLatin1String("state")) {
0513         processStateElement(e);
0514     }
0515 }
0516 
0517 void BuildHelper::processActionOrCustomElement(const QDomElement &e, bool isActionTag)
0518 {
0519     if (!parentNode->container) {
0520         return;
0521     }
0522 
0523     MergingIndexList::iterator it(m_state.currentClientMergingIt);
0524 
0525     QString group;
0526     int idx = calcMergingIndex(e, it, group);
0527 
0528     containerClient = parentNode->findChildContainerClient(m_state.guiClient, group, it);
0529 
0530     bool guiElementCreated = false;
0531     if (isActionTag) {
0532         guiElementCreated = processActionElement(e, idx);
0533     } else {
0534         guiElementCreated = processCustomElement(e, idx);
0535     }
0536 
0537     if (guiElementCreated) {
0538         // adjust any following merging indices and the current running index for the container
0539         parentNode->adjustMergingIndices(1, it, m_state.clientName);
0540     }
0541 }
0542 
0543 bool BuildHelper::processActionElement(const QDomElement &e, int idx)
0544 {
0545     assert(m_state.guiClient);
0546 
0547     // look up the action and plug it in
0548     QAction *action = m_state.guiClient->action(e);
0549 
0550     if (!action) {
0551         return false;
0552     }
0553 
0554     // qCDebug(DEBUG_KXMLGUI) << e.attribute(QStringLiteral("name")) << "->" << action << "inserting at idx=" << idx;
0555 
0556     QAction *before = nullptr;
0557     if (idx >= 0 && idx < parentNode->container->actions().count()) {
0558         before = parentNode->container->actions().at(idx);
0559     }
0560 
0561     parentNode->container->insertAction(before, action);
0562 
0563     // save a reference to the plugged action, in order to properly unplug it afterwards.
0564     containerClient->actions.append(action);
0565 
0566     return true;
0567 }
0568 
0569 bool BuildHelper::processCustomElement(const QDomElement &e, int idx)
0570 {
0571     assert(parentNode->builder);
0572 
0573     QAction *action = parentNode->builder->createCustomElement(parentNode->container, idx, e);
0574     if (!action) {
0575         return false;
0576     }
0577 
0578     containerClient->customElements.append(action);
0579     return true;
0580 }
0581 
0582 void BuildHelper::processStateElement(const QDomElement &element)
0583 {
0584     QString stateName = element.attribute(QStringLiteral("name"));
0585 
0586     if (stateName.isEmpty()) {
0587         return;
0588     }
0589 
0590     for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
0591         QDomElement e = n.toElement();
0592         if (e.isNull()) {
0593             continue;
0594         }
0595 
0596         QString tagName = e.tagName().toLower();
0597 
0598         if (tagName != QLatin1String("enable") && tagName != QLatin1String("disable")) {
0599             continue;
0600         }
0601 
0602         const bool processingActionsToEnable = (tagName == QLatin1String("enable"));
0603 
0604         // process action names
0605         for (QDomNode n2 = n.firstChild(); !n2.isNull(); n2 = n2.nextSibling()) {
0606             QDomElement actionEl = n2.toElement();
0607             if (actionEl.tagName().compare(QLatin1String("action"), Qt::CaseInsensitive) != 0) {
0608                 continue;
0609             }
0610 
0611             QString actionName = actionEl.attribute(QStringLiteral("name"));
0612             if (actionName.isEmpty()) {
0613                 return;
0614             }
0615 
0616             if (processingActionsToEnable) {
0617                 m_state.guiClient->addStateActionEnabled(stateName, actionName);
0618             } else {
0619                 m_state.guiClient->addStateActionDisabled(stateName, actionName);
0620             }
0621         }
0622     }
0623 }
0624 
0625 void BuildHelper::processMergeElement(const QString &tag, const QString &name, const QDomElement &e)
0626 {
0627     const QLatin1String tagDefineGroup("definegroup");
0628     const QLatin1String tagActionList("actionlist");
0629     const QLatin1String defaultMergingName("<default>");
0630     const QLatin1String attrGroup("group");
0631 
0632     QString mergingName(name);
0633     if (mergingName.isEmpty()) {
0634         if (tag == tagDefineGroup) {
0635             qCCritical(DEBUG_KXMLGUI) << "cannot define group without name!";
0636             return;
0637         }
0638         if (tag == tagActionList) {
0639             qCCritical(DEBUG_KXMLGUI) << "cannot define actionlist without name!";
0640             return;
0641         }
0642         mergingName = defaultMergingName;
0643     }
0644 
0645     if (tag == tagDefineGroup) {
0646         mergingName.prepend(attrGroup); // avoid possible name clashes by prepending
0647                                         // "group" to group definitions
0648     } else if (tag == tagActionList) {
0649         mergingName.prepend(tagActionList);
0650     }
0651 
0652     if (parentNode->findIndex(mergingName) != parentNode->mergingIndices.end()) {
0653         return; // do not allow the redefinition of merging indices!
0654     }
0655 
0656     MergingIndexList::iterator mIt(parentNode->mergingIndices.end());
0657 
0658     QString group(e.attribute(attrGroup));
0659     if (!group.isEmpty()) {
0660         group.prepend(attrGroup);
0661     }
0662 
0663     // calculate the index of the new merging index. Usually this does not need any calculation,
0664     // we just want the last available index (i.e. append) . But in case the <Merge> tag appears
0665     // "inside" another <Merge> tag from a previously build client, then we have to use the
0666     // "parent's" index. That's why we call calcMergingIndex here.
0667     MergingIndex newIdx;
0668     newIdx.value = parentNode->calcMergingIndex(group, mIt, m_state, ignoreDefaultMergingIndex);
0669     newIdx.mergingName = mergingName;
0670     newIdx.clientName = m_state.clientName;
0671 
0672     // if that merging index is "inside" another one, then append it right after the "parent".
0673     if (mIt != parentNode->mergingIndices.end()) {
0674         parentNode->mergingIndices.insert(++mIt, newIdx);
0675     } else {
0676         parentNode->mergingIndices.append(newIdx);
0677     }
0678 
0679     if (mergingName == defaultMergingName) {
0680         ignoreDefaultMergingIndex = true;
0681     }
0682 
0683     // re-calculate the running default and client merging indices
0684     // (especially important in case the QList data got reallocated due to growing!)
0685     m_state.currentDefaultMergingIt = parentNode->findIndex(defaultMergingName);
0686     parentNode->calcMergingIndex(QString(), m_state.currentClientMergingIt, m_state, ignoreDefaultMergingIndex);
0687 }
0688 
0689 void BuildHelper::processContainerElement(const QDomElement &e, const QString &tag, const QString &name)
0690 {
0691     ContainerNode *containerNode = parentNode->findContainer(name, tag, &containerList, m_state.guiClient);
0692 
0693     if (!containerNode) {
0694         MergingIndexList::iterator it(m_state.currentClientMergingIt);
0695         QString group;
0696 
0697         int idx = calcMergingIndex(e, it, group);
0698 
0699         QAction *containerAction;
0700 
0701         KXMLGUIBuilder *builder;
0702 
0703         QWidget *container = createContainer(parentNode->container, idx, e, containerAction, &builder);
0704 
0705         // no container? (probably some <text> tag or so ;-)
0706         if (!container) {
0707             return;
0708         }
0709 
0710         parentNode->adjustMergingIndices(1, it, m_state.clientName);
0711 
0712         // Check that the container widget is not already in parentNode.
0713         Q_ASSERT(std::find_if(parentNode->children.constBegin(),
0714                               parentNode->children.constEnd(),
0715                               [container](ContainerNode *child) {
0716                                   return child->container == container;
0717                               })
0718                  == parentNode->children.constEnd());
0719 
0720         containerList.append(container);
0721 
0722         QString mergingName;
0723         if (it != parentNode->mergingIndices.end()) {
0724             mergingName = (*it).mergingName;
0725         }
0726 
0727         QStringList cusTags = m_state.builderCustomTags;
0728         QStringList conTags = m_state.builderContainerTags;
0729         if (builder != m_state.builder) {
0730             cusTags = m_state.clientBuilderCustomTags;
0731             conTags = m_state.clientBuilderContainerTags;
0732         }
0733 
0734         containerNode = new ContainerNode(container, tag, name, parentNode, m_state.guiClient, builder, containerAction, mergingName, group, cusTags, conTags);
0735     } else {
0736         if (tag == QLatin1String("toolbar")) {
0737             KToolBar *bar = qobject_cast<KToolBar *>(containerNode->container);
0738             if (bar) {
0739                 if (m_state.guiClient && !m_state.guiClient->xmlFile().isEmpty()) {
0740                     bar->addXMLGUIClient(m_state.guiClient);
0741                 }
0742             } else {
0743                 qCWarning(DEBUG_KXMLGUI) << "toolbar container is not a KToolBar";
0744             }
0745         }
0746     }
0747 
0748     BuildHelper(m_state, containerNode).build(e);
0749 
0750     // and re-calculate running values, for better performance
0751     m_state.currentDefaultMergingIt = parentNode->findIndex(QStringLiteral("<default>"));
0752     parentNode->calcMergingIndex(QString(), m_state.currentClientMergingIt, m_state, ignoreDefaultMergingIndex);
0753 }
0754 
0755 QWidget *BuildHelper::createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction, KXMLGUIBuilder **builder)
0756 {
0757     QWidget *res = nullptr;
0758 
0759     if (m_state.clientBuilder) {
0760         res = m_state.clientBuilder->createContainer(parent, index, element, containerAction);
0761 
0762         if (res) {
0763             *builder = m_state.clientBuilder;
0764             return res;
0765         }
0766     }
0767 
0768     KXMLGUIClient *oldClient = m_state.builder->builderClient();
0769 
0770     m_state.builder->setBuilderClient(m_state.guiClient);
0771 
0772     res = m_state.builder->createContainer(parent, index, element, containerAction);
0773 
0774     m_state.builder->setBuilderClient(oldClient);
0775 
0776     if (res) {
0777         *builder = m_state.builder;
0778     }
0779 
0780     return res;
0781 }
0782 
0783 void BuildState::reset()
0784 {
0785     clientName.clear();
0786     actionListName.clear();
0787     actionList.clear();
0788     guiClient = nullptr;
0789     clientBuilder = nullptr;
0790 
0791     currentDefaultMergingIt = currentClientMergingIt = MergingIndexList::iterator();
0792 }
0793 
0794 QDebug operator<<(QDebug stream, const MergingIndex &mi)
0795 {
0796     QDebugStateSaver saver(stream);
0797     stream.nospace() << "clientName=" << mi.clientName << " mergingName=" << mi.mergingName << " value=" << mi.value;
0798     return stream;
0799 }