File indexing completed on 2024-06-16 04:15:42

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