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 QMutableListIterator<ContainerClient *> clientIt(clients);
350 while (clientIt.hasNext()) {
351 // only unplug the actions of the client we want to remove, as the container might be owned
352 // by a different client
353 ContainerClient *cClient = clientIt.next();
354 if (cClient->client == state.guiClient) {
355 unplugClient(client: cClient);
356 delete cClient;
357 clientIt.remove();
358 }
359 }
360}
361
362void ContainerNode::removeActions(const QList<QAction *> &actions)
363{
364 for (QAction *action : actions) {
365 const int pos = container->actions().indexOf(t: action);
366 if (pos != -1) {
367 container->removeAction(action);
368 for (MergingIndex &idx : mergingIndices) {
369 if (idx.value > pos) {
370 --idx.value;
371 }
372 }
373 --index;
374 }
375 }
376}
377
378void ContainerNode::unplugClient(ContainerClient *client)
379{
380 assert(builder);
381
382 KToolBar *bar = qobject_cast<KToolBar *>(object: container);
383 if (bar) {
384 bar->removeXMLGUIClient(client: client->client);
385 }
386
387 // now quickly remove all custom elements (i.e. separators) and unplug all actions
388 removeActions(actions: client->customElements);
389 removeActions(actions: client->actions);
390
391 // unplug all actionslists
392
393 for (const auto &actionList : std::as_const(t&: client->actionLists)) {
394 removeActions(actions: actionList);
395 }
396}
397
398void ContainerNode::reset()
399{
400 for (ContainerNode *child : std::as_const(t&: children)) {
401 child->reset();
402 }
403
404 if (client) {
405 client->setFactory(nullptr);
406 }
407}
408
409int ContainerNode::calcMergingIndex(const QString &mergingName, MergingIndexList::iterator &it, BuildState &state, bool ignoreDefaultMergingIndex)
410{
411 const MergingIndexList::iterator mergingIt = findIndex(name: mergingName.isEmpty() ? state.clientName : mergingName);
412 const MergingIndexList::iterator mergingEnd = mergingIndices.end();
413
414 if (ignoreDefaultMergingIndex || (mergingIt == mergingEnd && state.currentDefaultMergingIt == mergingEnd)) {
415 it = mergingEnd;
416 return index;
417 }
418
419 if (mergingIt != mergingEnd) {
420 it = mergingIt;
421 } else {
422 it = state.currentDefaultMergingIt;
423 }
424
425 return (*it).value;
426}
427
428void ContainerNode::dump(int offset)
429{
430 QString indent;
431 indent.fill(c: QLatin1Char(' '), size: offset);
432 qCDebug(DEBUG_KXMLGUI) << qPrintable(indent) << name << tagName << groupName << mergingName << mergingIndices;
433 for (ContainerNode *child : std::as_const(t&: children)) {
434 child->dump(offset: offset + 2);
435 }
436}
437
438// TODO: return a struct with 3 members rather than 1 ret val + 2 out params
439int BuildHelper::calcMergingIndex(const QDomElement &element, MergingIndexList::iterator &it, QString &group)
440{
441 const QLatin1String attrGroup("group");
442
443 bool haveGroup = false;
444 group = element.attribute(name: attrGroup);
445 if (!group.isEmpty()) {
446 group.prepend(s: attrGroup);
447 haveGroup = true;
448 }
449
450 int idx;
451 if (haveGroup) {
452 idx = parentNode->calcMergingIndex(mergingName: group, it, state&: m_state, ignoreDefaultMergingIndex);
453 } else {
454 it = m_state.currentClientMergingIt;
455 if (it == parentNode->mergingIndices.end()) {
456 idx = parentNode->index;
457 } else {
458 idx = (*it).value;
459 }
460 }
461
462 return idx;
463}
464
465BuildHelper::BuildHelper(BuildState &state, ContainerNode *node)
466 : containerClient(nullptr)
467 , ignoreDefaultMergingIndex(false)
468 , m_state(state)
469 , parentNode(node)
470{
471 // create a list of supported container and custom tags
472 customTags = m_state.builderCustomTags;
473 containerTags = m_state.builderContainerTags;
474
475 if (parentNode->builder != m_state.builder) {
476 customTags += parentNode->builderCustomTags;
477 containerTags += parentNode->builderContainerTags;
478 }
479
480 if (m_state.clientBuilder) {
481 customTags = m_state.clientBuilderCustomTags + customTags;
482 containerTags = m_state.clientBuilderContainerTags + containerTags;
483 }
484
485 m_state.currentDefaultMergingIt = parentNode->findIndex(QStringLiteral("<default>"));
486 parentNode->calcMergingIndex(mergingName: QString(), it&: m_state.currentClientMergingIt, state&: m_state, /*ignoreDefaultMergingIndex*/ false);
487}
488
489void BuildHelper::build(const QDomElement &element)
490{
491 for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
492 QDomElement e = n.toElement();
493 if (e.isNull()) {
494 continue;
495 }
496 processElement(element: e);
497 }
498}
499
500void BuildHelper::processElement(const QDomElement &e)
501{
502 QString tag(e.tagName().toLower());
503 QString currName(e.attribute(QStringLiteral("name")));
504
505 const bool isActionTag = (tag == QLatin1String("action"));
506
507 if (isActionTag || customTags.indexOf(str: tag) != -1) {
508 processActionOrCustomElement(e, isActionTag);
509 } else if (containerTags.indexOf(str: tag) != -1) {
510 processContainerElement(e, tag, name: currName);
511 } else if (tag == QLatin1String("merge") || tag == QLatin1String("definegroup") || tag == QLatin1String("actionlist")) {
512 processMergeElement(tag, name: currName, e);
513 } else if (tag == QLatin1String("state")) {
514 processStateElement(element: e);
515 }
516}
517
518void BuildHelper::processActionOrCustomElement(const QDomElement &e, bool isActionTag)
519{
520 if (!parentNode->container) {
521 return;
522 }
523
524 MergingIndexList::iterator it(m_state.currentClientMergingIt);
525
526 QString group;
527 int idx = calcMergingIndex(element: e, it, group);
528
529 containerClient = parentNode->findChildContainerClient(currentGUIClient: m_state.guiClient, groupName: group, mergingIdx: it);
530
531 bool guiElementCreated = false;
532 if (isActionTag) {
533 guiElementCreated = processActionElement(e, idx);
534 } else {
535 guiElementCreated = processCustomElement(e, idx);
536 }
537
538 if (guiElementCreated) {
539 // adjust any following merging indices and the current running index for the container
540 parentNode->adjustMergingIndices(offset: 1, it, currentClientName: m_state.clientName);
541 }
542}
543
544bool BuildHelper::processActionElement(const QDomElement &e, int idx)
545{
546 assert(m_state.guiClient);
547
548 // look up the action and plug it in
549 QAction *action = m_state.guiClient->action(element: e);
550
551 if (!action) {
552 return false;
553 }
554
555 // qCDebug(DEBUG_KXMLGUI) << e.attribute(QStringLiteral("name")) << "->" << action << "inserting at idx=" << idx;
556
557 QAction *before = nullptr;
558 if (idx >= 0 && idx < parentNode->container->actions().count()) {
559 before = parentNode->container->actions().at(i: idx);
560 }
561
562 parentNode->container->insertAction(before, action);
563 if (QToolBar *toolBarContainer = qobject_cast<QToolBar *>(object: parentNode->container)) {
564 QWidget *widget = toolBarContainer->widgetForAction(action);
565 const Qt::FocusPolicy oldPolicy = widget->focusPolicy();
566 if (!(oldPolicy & Qt::TabFocus)) {
567 widget->setFocusPolicy(oldPolicy == Qt::NoFocus ? Qt::TabFocus : Qt::StrongFocus);
568 }
569 }
570
571 // save a reference to the plugged action, in order to properly unplug it afterwards.
572 containerClient->actions.append(t: action);
573
574 return true;
575}
576
577bool BuildHelper::processCustomElement(const QDomElement &e, int idx)
578{
579 assert(parentNode->builder);
580
581 QAction *action = parentNode->builder->createCustomElement(parent: parentNode->container, index: idx, element: e);
582 if (!action) {
583 return false;
584 }
585
586 containerClient->customElements.append(t: action);
587 return true;
588}
589
590void BuildHelper::processStateElement(const QDomElement &element)
591{
592 QString stateName = element.attribute(QStringLiteral("name"));
593
594 if (stateName.isEmpty()) {
595 return;
596 }
597
598 for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
599 QDomElement e = n.toElement();
600 if (e.isNull()) {
601 continue;
602 }
603
604 QString tagName = e.tagName().toLower();
605
606 if (tagName != QLatin1String("enable") && tagName != QLatin1String("disable")) {
607 continue;
608 }
609
610 const bool processingActionsToEnable = (tagName == QLatin1String("enable"));
611
612 // process action names
613 for (QDomNode n2 = n.firstChild(); !n2.isNull(); n2 = n2.nextSibling()) {
614 QDomElement actionEl = n2.toElement();
615 if (actionEl.tagName().compare(other: QLatin1String("action"), cs: Qt::CaseInsensitive) != 0) {
616 continue;
617 }
618
619 QString actionName = actionEl.attribute(QStringLiteral("name"));
620 if (actionName.isEmpty()) {
621 return;
622 }
623
624 if (processingActionsToEnable) {
625 m_state.guiClient->addStateActionEnabled(state: stateName, action: actionName);
626 } else {
627 m_state.guiClient->addStateActionDisabled(state: stateName, action: actionName);
628 }
629 }
630 }
631}
632
633void BuildHelper::processMergeElement(const QString &tag, const QString &name, const QDomElement &e)
634{
635 const QLatin1String tagDefineGroup("definegroup");
636 const QLatin1String tagActionList("actionlist");
637 const QLatin1String defaultMergingName("<default>");
638 const QLatin1String attrGroup("group");
639
640 QString mergingName(name);
641 if (mergingName.isEmpty()) {
642 if (tag == tagDefineGroup) {
643 qCCritical(DEBUG_KXMLGUI) << "cannot define group without name!";
644 return;
645 }
646 if (tag == tagActionList) {
647 qCCritical(DEBUG_KXMLGUI) << "cannot define actionlist without name!";
648 return;
649 }
650 mergingName = defaultMergingName;
651 }
652
653 if (tag == tagDefineGroup) {
654 mergingName.prepend(s: attrGroup); // avoid possible name clashes by prepending
655 // "group" to group definitions
656 } else if (tag == tagActionList) {
657 mergingName.prepend(s: tagActionList);
658 }
659
660 if (parentNode->findIndex(name: mergingName) != parentNode->mergingIndices.end()) {
661 return; // do not allow the redefinition of merging indices!
662 }
663
664 MergingIndexList::iterator mIt(parentNode->mergingIndices.end());
665
666 QString group(e.attribute(name: attrGroup));
667 if (!group.isEmpty()) {
668 group.prepend(s: attrGroup);
669 }
670
671 // calculate the index of the new merging index. Usually this does not need any calculation,
672 // we just want the last available index (i.e. append) . But in case the <Merge> tag appears
673 // "inside" another <Merge> tag from a previously build client, then we have to use the
674 // "parent's" index. That's why we call calcMergingIndex here.
675 MergingIndex newIdx;
676 newIdx.value = parentNode->calcMergingIndex(mergingName: group, it&: mIt, state&: m_state, ignoreDefaultMergingIndex);
677 newIdx.mergingName = mergingName;
678 newIdx.clientName = m_state.clientName;
679
680 // if that merging index is "inside" another one, then append it right after the "parent".
681 if (mIt != parentNode->mergingIndices.end()) {
682 parentNode->mergingIndices.insert(before: ++mIt, t: newIdx);
683 } else {
684 parentNode->mergingIndices.append(t: newIdx);
685 }
686
687 if (mergingName == defaultMergingName) {
688 ignoreDefaultMergingIndex = true;
689 }
690
691 // re-calculate the running default and client merging indices
692 // (especially important in case the QList data got reallocated due to growing!)
693 m_state.currentDefaultMergingIt = parentNode->findIndex(name: defaultMergingName);
694 parentNode->calcMergingIndex(mergingName: QString(), it&: m_state.currentClientMergingIt, state&: m_state, ignoreDefaultMergingIndex);
695}
696
697void BuildHelper::processContainerElement(const QDomElement &e, const QString &tag, const QString &name)
698{
699 ContainerNode *containerNode = parentNode->findContainer(name, tagName: tag, excludeList: &containerList, m_state.guiClient);
700
701 if (!containerNode) {
702 MergingIndexList::iterator it(m_state.currentClientMergingIt);
703 QString group;
704
705 int idx = calcMergingIndex(element: e, it, group);
706
707 QAction *containerAction;
708
709 KXMLGUIBuilder *builder;
710
711 QWidget *container = createContainer(parent: parentNode->container, index: idx, element: e, containerAction, builder: &builder);
712
713 // no container? (probably some <text> tag or so ;-)
714 if (!container) {
715 return;
716 }
717
718 parentNode->adjustMergingIndices(offset: 1, it, currentClientName: m_state.clientName);
719
720 // Check that the container widget is not already in parentNode.
721 Q_ASSERT(std::find_if(parentNode->children.constBegin(),
722 parentNode->children.constEnd(),
723 [container](ContainerNode *child) {
724 return child->container == container;
725 })
726 == parentNode->children.constEnd());
727
728 containerList.append(t: container);
729
730 QString mergingName;
731 if (it != parentNode->mergingIndices.end()) {
732 mergingName = (*it).mergingName;
733 }
734
735 QStringList cusTags = m_state.builderCustomTags;
736 QStringList conTags = m_state.builderContainerTags;
737 if (builder != m_state.builder) {
738 cusTags = m_state.clientBuilderCustomTags;
739 conTags = m_state.clientBuilderContainerTags;
740 }
741
742 containerNode = new ContainerNode(container, tag, name, parentNode, m_state.guiClient, builder, containerAction, mergingName, group, cusTags, conTags);
743 } else {
744 if (tag == QLatin1String("toolbar")) {
745 KToolBar *bar = qobject_cast<KToolBar *>(object: containerNode->container);
746 if (bar) {
747 if (m_state.guiClient && !m_state.guiClient->xmlFile().isEmpty()) {
748 bar->addXMLGUIClient(client: m_state.guiClient);
749 }
750 } else {
751 qCWarning(DEBUG_KXMLGUI) << "toolbar container is not a KToolBar";
752 }
753 }
754 }
755
756 BuildHelper(m_state, containerNode).build(element: e);
757
758 // and re-calculate running values, for better performance
759 m_state.currentDefaultMergingIt = parentNode->findIndex(QStringLiteral("<default>"));
760 parentNode->calcMergingIndex(mergingName: QString(), it&: m_state.currentClientMergingIt, state&: m_state, ignoreDefaultMergingIndex);
761}
762
763QWidget *BuildHelper::createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction, KXMLGUIBuilder **builder)
764{
765 QWidget *res = nullptr;
766
767 if (m_state.clientBuilder) {
768 res = m_state.clientBuilder->createContainer(parent, index, element, containerAction);
769
770 if (res) {
771 *builder = m_state.clientBuilder;
772 return res;
773 }
774 }
775
776 KXMLGUIClient *oldClient = m_state.builder->builderClient();
777
778 m_state.builder->setBuilderClient(m_state.guiClient);
779
780 res = m_state.builder->createContainer(parent, index, element, containerAction);
781
782 m_state.builder->setBuilderClient(oldClient);
783
784 if (res) {
785 *builder = m_state.builder;
786 }
787
788 return res;
789}
790
791void BuildState::reset()
792{
793 clientName.clear();
794 actionListName.clear();
795 actionList.clear();
796 guiClient = nullptr;
797 clientBuilder = nullptr;
798
799 currentDefaultMergingIt = currentClientMergingIt = MergingIndexList::iterator();
800}
801
802QDebug operator<<(QDebug stream, const MergingIndex &mi)
803{
804 QDebugStateSaver saver(stream);
805 stream.nospace() << "clientName=" << mi.clientName << " mergingName=" << mi.mergingName << " value=" << mi.value;
806 return stream;
807}
808

source code of kxmlgui/src/kxmlguifactory_p.cpp