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

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