1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2001 Simon Hausmann <hausmann@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kxmlguifactory_p.h"
9
10#include "ktoolbar.h"
11#include "kxmlguibuilder.h"
12#include "kxmlguiclient.h"
13
14#include <QList>
15#include <QWidget>
16
17#include "debug.h"
18#include <cassert>
19#include <qnamespace.h>
20
21using namespace KXMLGUI;
22
23void ActionList::plug(QWidget *container, int index) const
24{
25 QAction *before = nullptr; // Insert after end of widget's current actions (default).
26
27 if ((index < 0) || (index > container->actions().count())) {
28 qCWarning(DEBUG_KXMLGUI) << "Index " << index << " is not within range (0 - " << container->actions().count() << ")";
29 } else if (index != container->actions().count()) {
30 before = container->actions().at(i: index); // Insert before indexed action.
31 }
32
33 for (QAction *action : *this) {
34 container->insertAction(before, action);
35 // before = action; // BUG FIX: do not insert actions in reverse order.
36 }
37}
38
39ContainerNode::ContainerNode(QWidget *_container,
40 const QString &_tagName,
41 const QString &_name,
42 ContainerNode *_parent,
43 KXMLGUIClient *_client,
44 KXMLGUIBuilder *_builder,
45 QAction *_containerAction,
46 const QString &_mergingName,
47 const QString &_groupName,
48 const QStringList &customTags,
49 const QStringList &containerTags)
50 : parent(_parent)
51 , client(_client)
52 , builder(_builder)
53 , builderCustomTags(customTags)
54 , builderContainerTags(containerTags)
55 , container(_container)
56 , containerAction(_containerAction)
57 , tagName(_tagName)
58 , name(_name)
59 , groupName(_groupName)
60 , index(0)
61 , mergingName(_mergingName)
62{
63 if (parent) {
64 parent->children.append(t: this);
65 }
66}
67
68ContainerNode::~ContainerNode()
69{
70 qDeleteAll(c: children);
71 qDeleteAll(c: clients);
72}
73
74void ContainerNode::removeChild(ContainerNode *child)
75{
76 children.removeAll(t: child);
77 deleteChild(child);
78}
79
80void ContainerNode::deleteChild(ContainerNode *child)
81{
82 MergingIndexList::iterator mergingIt = findIndex(name: child->mergingName);
83 adjustMergingIndices(offset: -1, it: mergingIt, currentClientName: QString());
84 delete child;
85}
86
87/*
88 * Find a merging index with the given name. Used to find an index defined by <Merge name="blah"/>
89 * or by a <DefineGroup name="foo" /> tag.
90 */
91MergingIndexList::iterator ContainerNode::findIndex(const QString &name)
92{
93 return std::find_if(first: mergingIndices.begin(), last: mergingIndices.end(), pred: [&name](const MergingIndex &idx) {
94 return idx.mergingName == name;
95 });
96}
97
98/*
99 * Find a container recursively with the given name. Either compares _name with the
100 * container's tag name or the value of the container's name attribute. Specified by
101 * the tag bool .
102 */
103ContainerNode *ContainerNode::findContainer(const QString &_name, bool tag)
104{
105 if ((tag && tagName == _name) || (!tag && name == _name)) {
106 return this;
107 }
108
109 for (ContainerNode *child : std::as_const(t&: children)) {
110 ContainerNode *res = child->findContainer(_name, tag);
111 if (res) {
112 return res;
113 }
114 }
115
116 return nullptr;
117}
118
119/*
120 * Finds a child container node (not recursively) with the given name and tagname. Explicitly
121 * leaves out container widgets specified in the exludeList . Also ensures that the containers
122 * belongs to currClient.
123 */
124ContainerNode *ContainerNode::findContainer(const QString &name, const QString &tagName, const QList<QWidget *> *excludeList, KXMLGUIClient * /*currClient*/)
125{
126 if (!name.isEmpty()) {
127 auto it = std::find_if(first: children.cbegin(), last: children.cend(), pred: [&name, excludeList](ContainerNode *node) {
128 return node->name == name && !excludeList->contains(t: node->container);
129 });
130 return it != children.cend() ? *it : nullptr;
131 }
132
133 if (!tagName.isEmpty()) {
134 // It is a bad idea to also compare the client (node->client == currClient),
135 // because we don't want to do so in situations like these:
136 //
137 // <MenuBar>
138 // <Menu>
139 // ...
140 //
141 // other client:
142 // <MenuBar>
143 // <Menu>
144 // ...
145 auto it = std::find_if(first: children.cbegin(), last: children.cend(), pred: [&tagName, excludeList](ContainerNode *node) {
146 return node->tagName == tagName && !excludeList->contains(t: node->container);
147 });
148 return it != children.cend() ? *it : nullptr;
149 };
150
151 return {};
152}
153
154ContainerClient *
155ContainerNode::findChildContainerClient(KXMLGUIClient *currentGUIClient, const QString &groupName, const MergingIndexList::iterator &mergingIdx)
156{
157 auto it = std::find_if(first: clients.cbegin(), last: clients.cend(), pred: [&groupName, currentGUIClient](ContainerClient *cl) {
158 return cl->client == currentGUIClient && (groupName.isEmpty() || groupName == cl->groupName);
159 });
160 if (it != clients.cend()) {
161 return *it;
162 }
163
164 ContainerClient *client = new ContainerClient;
165 client->client = currentGUIClient;
166 client->groupName = groupName;
167
168 if (mergingIdx != mergingIndices.end()) {
169 client->mergingName = (*mergingIdx).mergingName;
170 }
171
172 clients.append(t: client);
173
174 return client;
175}
176
177void ContainerNode::plugActionList(BuildState &state)
178{
179 MergingIndexList::iterator mIt(mergingIndices.begin());
180 MergingIndexList::iterator mEnd(mergingIndices.end());
181 for (; mIt != mEnd; ++mIt) {
182 plugActionList(state, mergingIdxIt: mIt);
183 }
184
185 for (ContainerNode *child : std::as_const(t&: children)) {
186 child->plugActionList(state);
187 }
188}
189
190void ContainerNode::plugActionList(BuildState &state, const MergingIndexList::iterator &mergingIdxIt)
191{
192 const QLatin1String tagActionList("actionlist");
193
194 const MergingIndex &mergingIdx = *mergingIdxIt;
195 if (mergingIdx.clientName != state.clientName) {
196 return;
197 }
198 if (!mergingIdx.mergingName.startsWith(s: tagActionList)) {
199 return;
200 }
201 const QString k = mergingIdx.mergingName.mid(position: tagActionList.size());
202 if (k != state.actionListName) {
203 return;
204 }
205
206 ContainerClient *client = findChildContainerClient(currentGUIClient: state.guiClient, groupName: QString(), mergingIdx: mergingIndices.end());
207
208 client->actionLists.insert(key: k, value: state.actionList);
209
210 state.actionList.plug(container, index: mergingIdx.value);
211
212 adjustMergingIndices(offset: state.actionList.count(), it: mergingIdxIt, currentClientName: QString());
213}
214
215void ContainerNode::unplugActionList(BuildState &state)
216{
217 MergingIndexList::iterator mIt(mergingIndices.begin());
218 MergingIndexList::iterator mEnd(mergingIndices.end());
219 for (; mIt != mEnd; ++mIt) {
220 unplugActionList(state, mergingIdxIt: mIt);
221 }
222
223 for (ContainerNode *child : std::as_const(t&: children)) {
224 child->unplugActionList(state);
225 }
226}
227
228void ContainerNode::unplugActionList(BuildState &state, const MergingIndexList::iterator &mergingIdxIt)
229{
230 const QLatin1String tagActionList("actionlist");
231
232 MergingIndex mergingIdx = *mergingIdxIt;
233
234 QString k = mergingIdx.mergingName;
235
236 if (k.indexOf(s: tagActionList) == -1) {
237 return;
238 }
239
240 k.remove(i: 0, len: tagActionList.size());
241
242 if (mergingIdx.clientName != state.clientName) {
243 return;
244 }
245
246 if (k != state.actionListName) {
247 return;
248 }
249
250 ContainerClient *client = findChildContainerClient(currentGUIClient: state.guiClient, groupName: QString(), mergingIdx: mergingIndices.end());
251
252 ActionListMap::Iterator lIt(client->actionLists.find(key: k));
253 if (lIt == client->actionLists.end()) {
254 return;
255 }
256
257 removeActions(actions: lIt.value());
258
259 client->actionLists.erase(it: lIt);
260}
261
262void ContainerNode::adjustMergingIndices(int offset, const MergingIndexList::iterator &it, const QString &currentClientName)
263{
264 MergingIndexList::iterator mergingIt = it;
265 MergingIndexList::iterator mergingEnd = mergingIndices.end();
266
267 for (; mergingIt != mergingEnd; ++mergingIt) {
268 if ((*mergingIt).clientName != currentClientName) {
269 (*mergingIt).value += offset;
270 }
271 }
272
273 index += offset;
274}
275
276bool ContainerNode::destruct(
277 QDomElement element,
278 BuildState &state) // krazy:exclude=passbyvalue (this is correct QDom usage, and a ref wouldn't allow passing doc.documentElement() as argument)
279{
280 destructChildren(element, state);
281
282 unplugActions(state);
283
284 // remove all merging indices the client defined
285 QMutableListIterator<MergingIndex> cmIt = mergingIndices;
286 while (cmIt.hasNext()) {
287 if (cmIt.next().clientName == state.clientName) {
288 cmIt.remove();
289 }
290 }
291
292 // ### check for merging index count, too?
293 if (clients.isEmpty() && children.isEmpty() && container && client == state.guiClient) {
294 QWidget *parentContainer = nullptr;
295 if (parent && parent->container) {
296 parentContainer = parent->container;
297 }
298
299 Q_ASSERT(builder);
300 builder->removeContainer(container, parent: parentContainer, element, containerAction);
301
302 client = nullptr;
303 return true;
304 }
305
306 if (client == state.guiClient) {
307 client = nullptr;
308 }
309
310 return false;
311}
312
313void ContainerNode::destructChildren(const QDomElement &element, BuildState &state)
314{
315 QMutableListIterator<ContainerNode *> childIt = children;
316 while (childIt.hasNext()) {
317 ContainerNode *childNode = childIt.next();
318
319 QDomElement childElement = findElementForChild(baseElement: element, childNode);
320
321 // destruct returns true in case the container really got deleted
322 if (childNode->destruct(element: childElement, state)) {
323 deleteChild(child: childNode);
324 childIt.remove();
325 }
326 }
327}
328
329QDomElement ContainerNode::findElementForChild(const QDomElement &baseElement, ContainerNode *childNode)
330{
331 // ### slow
332 for (QDomNode n = baseElement.firstChild(); !n.isNull(); n = n.nextSibling()) {
333 QDomElement e = n.toElement();
334 if (e.tagName().toLower() == childNode->tagName //
335 && e.attribute(QStringLiteral("name")) == childNode->name) {
336 return e;
337 }
338 }
339
340 return QDomElement();
341}
342
343void ContainerNode::unplugActions(BuildState &state)
344{
345 if (!container) {
346 return;
347 }
348
349 // This is done in 2 steps as "clients" can get modified during iteration
350
351 // Collect the elements that need to be removed and remove them from the clients list
352 ContainerClientList toRemove;
353 for (auto it = clients.begin(); it != clients.end();) {
354 if ((*it)->client == state.guiClient) {
355 auto container = *it;
356 it = clients.erase(pos: it);
357 toRemove.push_back(t: container);
358 } else {
359 ++it;
360 }
361 }
362
363 // Do the actual remove
364 for (auto *c : toRemove) {
365 unplugClient(client: c);
366 delete c;
367 }
368}
369
370void ContainerNode::removeActions(const QList<QAction *> &actions)
371{
372 for (QAction *action : actions) {
373 const int pos = container->actions().indexOf(t: action);
374 if (pos != -1) {
375 container->removeAction(action);
376 for (MergingIndex &idx : mergingIndices) {
377 if (idx.value > pos) {
378 --idx.value;
379 }
380 }
381 --index;
382 }
383 }
384}
385
386void ContainerNode::unplugClient(ContainerClient *client)
387{
388 assert(builder);
389
390 KToolBar *bar = qobject_cast<KToolBar *>(object: container);
391 if (bar) {
392 bar->removeXMLGUIClient(client: client->client);
393 }
394
395 // now quickly remove all custom elements (i.e. separators) and unplug all actions
396 removeActions(actions: client->customElements);
397 removeActions(actions: client->actions);
398
399 // unplug all actionslists
400
401 for (const auto &actionList : std::as_const(t&: client->actionLists)) {
402 removeActions(actions: actionList);
403 }
404}
405
406void ContainerNode::reset()
407{
408 for (ContainerNode *child : std::as_const(t&: children)) {
409 child->reset();
410 }
411
412 if (client) {
413 client->setFactory(nullptr);
414 }
415}
416
417int ContainerNode::calcMergingIndex(const QString &mergingName, MergingIndexList::iterator &it, BuildState &state, bool ignoreDefaultMergingIndex)
418{
419 const MergingIndexList::iterator mergingIt = findIndex(name: mergingName.isEmpty() ? state.clientName : mergingName);
420 const MergingIndexList::iterator mergingEnd = mergingIndices.end();
421
422 if (ignoreDefaultMergingIndex || (mergingIt == mergingEnd && state.currentDefaultMergingIt == mergingEnd)) {
423 it = mergingEnd;
424 return index;
425 }
426
427 if (mergingIt != mergingEnd) {
428 it = mergingIt;
429 } else {
430 it = state.currentDefaultMergingIt;
431 }
432
433 return (*it).value;
434}
435
436void ContainerNode::dump(int offset)
437{
438 QString indent;
439 indent.fill(c: QLatin1Char(' '), size: offset);
440 qCDebug(DEBUG_KXMLGUI) << qPrintable(indent) << name << tagName << groupName << mergingName << mergingIndices;
441 for (ContainerNode *child : std::as_const(t&: children)) {
442 child->dump(offset: offset + 2);
443 }
444}
445
446// TODO: return a struct with 3 members rather than 1 ret val + 2 out params
447int BuildHelper::calcMergingIndex(const QDomElement &element, MergingIndexList::iterator &it, QString &group)
448{
449 const QLatin1String attrGroup("group");
450
451 bool haveGroup = false;
452 group = element.attribute(name: attrGroup);
453 if (!group.isEmpty()) {
454 group.prepend(s: attrGroup);
455 haveGroup = true;
456 }
457
458 int idx;
459 if (haveGroup) {
460 idx = parentNode->calcMergingIndex(mergingName: group, it, state&: m_state, ignoreDefaultMergingIndex);
461 } else {
462 it = m_state.currentClientMergingIt;
463 if (it == parentNode->mergingIndices.end()) {
464 idx = parentNode->index;
465 } else {
466 idx = (*it).value;
467 }
468 }
469
470 return idx;
471}
472
473BuildHelper::BuildHelper(BuildState &state, ContainerNode *node)
474 : containerClient(nullptr)
475 , ignoreDefaultMergingIndex(false)
476 , m_state(state)
477 , parentNode(node)
478{
479 // create a list of supported container and custom tags
480 customTags = m_state.builderCustomTags;
481 containerTags = m_state.builderContainerTags;
482
483 if (parentNode->builder != m_state.builder) {
484 customTags += parentNode->builderCustomTags;
485 containerTags += parentNode->builderContainerTags;
486 }
487
488 if (m_state.clientBuilder) {
489 customTags = m_state.clientBuilderCustomTags + customTags;
490 containerTags = m_state.clientBuilderContainerTags + containerTags;
491 }
492
493 m_state.currentDefaultMergingIt = parentNode->findIndex(QStringLiteral("<default>"));
494 parentNode->calcMergingIndex(mergingName: QString(), it&: m_state.currentClientMergingIt, state&: m_state, /*ignoreDefaultMergingIndex*/ false);
495}
496
497void BuildHelper::build(const QDomElement &element)
498{
499 for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
500 QDomElement e = n.toElement();
501 if (e.isNull()) {
502 continue;
503 }
504 processElement(element: e);
505 }
506}
507
508void BuildHelper::processElement(const QDomElement &e)
509{
510 QString tag(e.tagName().toLower());
511 QString currName(e.attribute(QStringLiteral("name")));
512
513 const bool isActionTag = (tag == QLatin1String("action"));
514
515 if (isActionTag || customTags.indexOf(str: tag) != -1) {
516 processActionOrCustomElement(e, isActionTag);
517 } else if (containerTags.indexOf(str: tag) != -1) {
518 processContainerElement(e, tag, name: currName);
519 } else if (tag == QLatin1String("merge") || tag == QLatin1String("definegroup") || tag == QLatin1String("actionlist")) {
520 processMergeElement(tag, name: currName, e);
521 } else if (tag == QLatin1String("state")) {
522 processStateElement(element: e);
523 }
524}
525
526void BuildHelper::processActionOrCustomElement(const QDomElement &e, bool isActionTag)
527{
528 if (!parentNode->container) {
529 return;
530 }
531
532 MergingIndexList::iterator it(m_state.currentClientMergingIt);
533
534 QString group;
535 int idx = calcMergingIndex(element: e, it, group);
536
537 containerClient = parentNode->findChildContainerClient(currentGUIClient: m_state.guiClient, groupName: group, mergingIdx: it);
538
539 bool guiElementCreated = false;
540 if (isActionTag) {
541 guiElementCreated = processActionElement(e, idx);
542 } else {
543 guiElementCreated = processCustomElement(e, idx);
544 }
545
546 if (guiElementCreated) {
547 // adjust any following merging indices and the current running index for the container
548 parentNode->adjustMergingIndices(offset: 1, it, currentClientName: m_state.clientName);
549 }
550}
551
552bool BuildHelper::processActionElement(const QDomElement &e, int idx)
553{
554 assert(m_state.guiClient);
555
556 // look up the action and plug it in
557 QAction *action = m_state.guiClient->action(element: e);
558
559 if (!action) {
560 return false;
561 }
562
563 // qCDebug(DEBUG_KXMLGUI) << e.attribute(QStringLiteral("name")) << "->" << action << "inserting at idx=" << idx;
564
565 QAction *before = nullptr;
566 if (idx >= 0 && idx < parentNode->container->actions().count()) {
567 before = parentNode->container->actions().at(i: idx);
568 }
569
570 parentNode->container->insertAction(before, action);
571 if (QToolBar *toolBarContainer = qobject_cast<QToolBar *>(object: parentNode->container)) {
572 QWidget *widget = toolBarContainer->widgetForAction(action);
573 const Qt::FocusPolicy oldPolicy = widget->focusPolicy();
574 if (!(oldPolicy & Qt::TabFocus)) {
575 widget->setFocusPolicy(oldPolicy == Qt::NoFocus ? Qt::TabFocus : Qt::StrongFocus);
576 }
577 }
578
579 // save a reference to the plugged action, in order to properly unplug it afterwards.
580 containerClient->actions.append(t: action);
581
582 return true;
583}
584
585bool BuildHelper::processCustomElement(const QDomElement &e, int idx)
586{
587 assert(parentNode->builder);
588
589 QAction *action = parentNode->builder->createCustomElement(parent: parentNode->container, index: idx, element: e);
590 if (!action) {
591 return false;
592 }
593
594 containerClient->customElements.append(t: action);
595 return true;
596}
597
598void BuildHelper::processStateElement(const QDomElement &element)
599{
600 QString stateName = element.attribute(QStringLiteral("name"));
601
602 if (stateName.isEmpty()) {
603 return;
604 }
605
606 for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
607 QDomElement e = n.toElement();
608 if (e.isNull()) {
609 continue;
610 }
611
612 QString tagName = e.tagName().toLower();
613
614 if (tagName != QLatin1String("enable") && tagName != QLatin1String("disable")) {
615 continue;
616 }
617
618 const bool processingActionsToEnable = (tagName == QLatin1String("enable"));
619
620 // process action names
621 for (QDomNode n2 = n.firstChild(); !n2.isNull(); n2 = n2.nextSibling()) {
622 QDomElement actionEl = n2.toElement();
623 if (actionEl.tagName().compare(other: QLatin1String("action"), cs: Qt::CaseInsensitive) != 0) {
624 continue;
625 }
626
627 QString actionName = actionEl.attribute(QStringLiteral("name"));
628 if (actionName.isEmpty()) {
629 return;
630 }
631
632 if (processingActionsToEnable) {
633 m_state.guiClient->addStateActionEnabled(state: stateName, action: actionName);
634 } else {
635 m_state.guiClient->addStateActionDisabled(state: stateName, action: actionName);
636 }
637 }
638 }
639}
640
641void BuildHelper::processMergeElement(const QString &tag, const QString &name, const QDomElement &e)
642{
643 const QLatin1String tagDefineGroup("definegroup");
644 const QLatin1String tagActionList("actionlist");
645 const QLatin1String defaultMergingName("<default>");
646 const QLatin1String attrGroup("group");
647
648 QString mergingName(name);
649 if (mergingName.isEmpty()) {
650 if (tag == tagDefineGroup) {
651 qCCritical(DEBUG_KXMLGUI) << "cannot define group without name!";
652 return;
653 }
654 if (tag == tagActionList) {
655 qCCritical(DEBUG_KXMLGUI) << "cannot define actionlist without name!";
656 return;
657 }
658 mergingName = defaultMergingName;
659 }
660
661 if (tag == tagDefineGroup) {
662 mergingName.prepend(s: attrGroup); // avoid possible name clashes by prepending
663 // "group" to group definitions
664 } else if (tag == tagActionList) {
665 mergingName.prepend(s: tagActionList);
666 }
667
668 if (parentNode->findIndex(name: mergingName) != parentNode->mergingIndices.end()) {
669 return; // do not allow the redefinition of merging indices!
670 }
671
672 MergingIndexList::iterator mIt(parentNode->mergingIndices.end());
673
674 QString group(e.attribute(name: attrGroup));
675 if (!group.isEmpty()) {
676 group.prepend(s: attrGroup);
677 }
678
679 // calculate the index of the new merging index. Usually this does not need any calculation,
680 // we just want the last available index (i.e. append) . But in case the <Merge> tag appears
681 // "inside" another <Merge> tag from a previously build client, then we have to use the
682 // "parent's" index. That's why we call calcMergingIndex here.
683 MergingIndex newIdx;
684 newIdx.value = parentNode->calcMergingIndex(mergingName: group, it&: mIt, state&: m_state, ignoreDefaultMergingIndex);
685 newIdx.mergingName = mergingName;
686 newIdx.clientName = m_state.clientName;
687
688 // if that merging index is "inside" another one, then append it right after the "parent".
689 if (mIt != parentNode->mergingIndices.end()) {
690 parentNode->mergingIndices.insert(before: ++mIt, t: newIdx);
691 } else {
692 parentNode->mergingIndices.append(t: newIdx);
693 }
694
695 if (mergingName == defaultMergingName) {
696 ignoreDefaultMergingIndex = true;
697 }
698
699 // re-calculate the running default and client merging indices
700 // (especially important in case the QList data got reallocated due to growing!)
701 m_state.currentDefaultMergingIt = parentNode->findIndex(name: defaultMergingName);
702 parentNode->calcMergingIndex(mergingName: QString(), it&: m_state.currentClientMergingIt, state&: m_state, ignoreDefaultMergingIndex);
703}
704
705void BuildHelper::processContainerElement(const QDomElement &e, const QString &tag, const QString &name)
706{
707 ContainerNode *containerNode = parentNode->findContainer(name, tagName: tag, excludeList: &containerList, m_state.guiClient);
708
709 if (!containerNode) {
710 MergingIndexList::iterator it(m_state.currentClientMergingIt);
711 QString group;
712
713 int idx = calcMergingIndex(element: e, it, group);
714
715 QAction *containerAction;
716
717 KXMLGUIBuilder *builder;
718
719 QWidget *container = createContainer(parent: parentNode->container, index: idx, element: e, containerAction, builder: &builder);
720
721 // no container? (probably some <text> tag or so ;-)
722 if (!container) {
723 return;
724 }
725
726 parentNode->adjustMergingIndices(offset: 1, it, currentClientName: m_state.clientName);
727
728 // Check that the container widget is not already in parentNode.
729 Q_ASSERT(std::find_if(parentNode->children.constBegin(),
730 parentNode->children.constEnd(),
731 [container](ContainerNode *child) {
732 return child->container == container;
733 })
734 == parentNode->children.constEnd());
735
736 containerList.append(t: container);
737
738 QString mergingName;
739 if (it != parentNode->mergingIndices.end()) {
740 mergingName = (*it).mergingName;
741 }
742
743 QStringList cusTags = m_state.builderCustomTags;
744 QStringList conTags = m_state.builderContainerTags;
745 if (builder != m_state.builder) {
746 cusTags = m_state.clientBuilderCustomTags;
747 conTags = m_state.clientBuilderContainerTags;
748 }
749
750 containerNode = new ContainerNode(container, tag, name, parentNode, m_state.guiClient, builder, containerAction, mergingName, group, cusTags, conTags);
751 } else {
752 if (tag == QLatin1String("toolbar")) {
753 KToolBar *bar = qobject_cast<KToolBar *>(object: containerNode->container);
754 if (bar) {
755 if (m_state.guiClient && !m_state.guiClient->xmlFile().isEmpty()) {
756 bar->addXMLGUIClient(client: m_state.guiClient);
757 }
758 } else {
759 qCWarning(DEBUG_KXMLGUI) << "toolbar container is not a KToolBar";
760 }
761 }
762 }
763
764 BuildHelper(m_state, containerNode).build(element: e);
765
766 // and re-calculate running values, for better performance
767 m_state.currentDefaultMergingIt = parentNode->findIndex(QStringLiteral("<default>"));
768 parentNode->calcMergingIndex(mergingName: QString(), it&: m_state.currentClientMergingIt, state&: m_state, ignoreDefaultMergingIndex);
769}
770
771QWidget *BuildHelper::createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction, KXMLGUIBuilder **builder)
772{
773 QWidget *res = nullptr;
774
775 if (m_state.clientBuilder) {
776 res = m_state.clientBuilder->createContainer(parent, index, element, containerAction);
777
778 if (res) {
779 *builder = m_state.clientBuilder;
780 return res;
781 }
782 }
783
784 KXMLGUIClient *oldClient = m_state.builder->builderClient();
785
786 m_state.builder->setBuilderClient(m_state.guiClient);
787
788 res = m_state.builder->createContainer(parent, index, element, containerAction);
789
790 m_state.builder->setBuilderClient(oldClient);
791
792 if (res) {
793 *builder = m_state.builder;
794 }
795
796 return res;
797}
798
799void BuildState::reset()
800{
801 clientName.clear();
802 actionListName.clear();
803 actionList.clear();
804 guiClient = nullptr;
805 clientBuilder = nullptr;
806
807 currentDefaultMergingIt = currentClientMergingIt = MergingIndexList::iterator();
808}
809
810QDebug operator<<(QDebug stream, const MergingIndex &mi)
811{
812 QDebugStateSaver saver(stream);
813 stream.nospace() << "clientName=" << mi.clientName << " mergingName=" << mi.mergingName << " value=" << mi.value;
814 return stream;
815}
816

source code of kxmlgui/src/kxmlguifactory_p.cpp