KXmlGui

kxmlguiclient.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000 Simon Hausmann <hausmann@kde.org>
4 SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-only
7*/
8
9#include "kxmlguiclient.h"
10
11#include "debug.h"
12#include "kactioncollection.h"
13#include "kxmlguibuilder.h"
14#include "kxmlguifactory.h"
15#include "kxmlguiversionhandler_p.h"
16
17#include <QAction>
18#include <QCoreApplication>
19#include <QDir>
20#include <QDomDocument>
21#include <QFile>
22#include <QPointer>
23#include <QStandardPaths>
24
25#include <KAuthorized>
26#include <KLocalizedString>
27
28#include <cassert>
29
30class KXMLGUIClientPrivate
31{
32public:
33 KXMLGUIClientPrivate()
34 : m_componentName(QCoreApplication::applicationName())
35 , m_textTagNames({QStringLiteral("text"), QStringLiteral("Text"), QStringLiteral("title")})
36 {
37 }
38 ~KXMLGUIClientPrivate()
39 {
40 }
41
42 bool mergeXML(QDomElement &base, QDomElement &additive, KActionCollection *actionCollection);
43 bool isEmptyContainer(const QDomElement &base, KActionCollection *actionCollection) const;
44
45 QDomElement findMatchingElement(const QDomElement &base, const QDomElement &additive);
46
47 QString m_componentName;
48
49 QDomDocument m_doc;
50 KActionCollection *m_actionCollection = nullptr;
51 QDomDocument m_buildDocument;
53 KXMLGUIClient *m_parent = nullptr;
54 // QPtrList<KXMLGUIClient> m_supers;
55 QList<KXMLGUIClient *> m_children;
56 KXMLGUIBuilder *m_builder = nullptr;
57 QString m_xmlFile;
58 QString m_localXMLFile;
59 const QStringList m_textTagNames;
60
61 // Actions to enable/disable on a state change
63};
64
66 : d(new KXMLGUIClientPrivate)
67{
68}
69
71 : d(new KXMLGUIClientPrivate)
72{
73 Q_INIT_RESOURCE(kxmlgui);
74
75 parent->insertChildClient(this);
76}
77
79{
80 if (d->m_parent) {
81 d->m_parent->removeChildClient(this);
82 }
83
84 if (d->m_factory) {
85 qCWarning(DEBUG_KXMLGUI)
86 << this << "deleted without having been removed from the factory first. This will leak standalone popupmenus and could lead to crashes.";
87 d->m_factory->forgetClient(this);
88 }
89
90 for (KXMLGUIClient *client : std::as_const(d->m_children)) {
91 if (d->m_factory) {
92 d->m_factory->forgetClient(client);
93 }
94 assert(client->d->m_parent == this);
95 client->d->m_parent = nullptr;
96 }
97
98 delete d->m_actionCollection;
99}
100
102{
103 QAction *act = actionCollection()->action(name);
104 if (!act) {
105 for (KXMLGUIClient *client : std::as_const(d->m_children)) {
106 act = client->actionCollection()->action(name);
107 if (act) {
108 break;
109 }
110 }
111 }
112 return act;
113}
114
116{
117 if (!d->m_actionCollection) {
118 d->m_actionCollection = new KActionCollection(this);
119 d->m_actionCollection->setObjectName(QStringLiteral("KXMLGUIClient-KActionCollection"));
120 }
121 return d->m_actionCollection;
122}
123
125{
126 return actionCollection()->action(element.attribute(QStringLiteral("name")));
127}
128
130{
131 return d->m_componentName;
132}
133
135{
136 return d->m_doc;
137}
138
140{
141 return d->m_xmlFile;
142}
143
144QString KXMLGUIClient::localXMLFile() const
145{
146 if (!d->m_localXMLFile.isEmpty()) {
147 return d->m_localXMLFile;
148 }
149
150 if (!QDir::isRelativePath(d->m_xmlFile)) {
151 return QString(); // can't save anything here
152 }
153
154 if (d->m_xmlFile.isEmpty()) { // setXMLFile not called at all, can't save. Use case: ToolBarHandler
155 return QString();
156 }
157
159}
160
162{
163 // TODO: this method can't be used for the KXmlGuiWindow, since it doesn't merge in ui_standards.rc!
164 // -> KDE5: load ui_standards_rc in setXMLFile using a flag, and remember that flag?
165 // and then KEditToolBar can use reloadXML.
166 QString file(xmlFile());
167 if (!file.isEmpty()) {
168 setXMLFile(file);
169 }
170}
171
172void KXMLGUIClient::setComponentName(const QString &componentName, const QString &componentDisplayName)
173{
174 d->m_componentName = componentName;
176 actionCollection()->setComponentDisplayName(componentDisplayName);
177 if (d->m_builder) {
178 d->m_builder->setBuilderClient(this);
179 }
180}
181
183{
184 if (QStandardPaths::isTestModeEnabled()) {
185 return QStringLiteral(":/kxmlgui5/ui_standards.rc");
186 }
187 QString file = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QStringLiteral("kxmlgui5/ui_standards.rc"));
188 if (file.isEmpty()) {
189 // fallback to resource, to allow to use the rc file compiled into this framework, must exist!
190 file = QStringLiteral(":/kxmlgui5/ui_standards.rc");
191 Q_ASSERT(QFile::exists(file));
192 }
193 return file;
194}
195
200
201void KXMLGUIClient::setXMLFile(const QString &_file, bool merge, bool setXMLDoc)
202{
203 // store our xml file name
204 if (!_file.isNull()) {
205 d->m_xmlFile = _file;
206 }
207
208 if (!setXMLDoc) {
209 return;
210 }
211
212 QString file = _file;
213 QStringList allFiles;
214 if (!QDir::isRelativePath(file)) {
215 allFiles.append(file);
216 } else {
217 const QString filter = componentName() + QLatin1Char('/') + _file;
218
219 // files on filesystem
220 allFiles << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kxmlgui5/") + filter);
221
222 // built-in resource file
223 const QString qrcFile(QLatin1String(":/kxmlgui5/") + filter);
224 if (QFile::exists(qrcFile)) {
225 allFiles << qrcFile;
226 }
227 }
228 if (allFiles.isEmpty() && !_file.isEmpty()) {
229 // if a non-empty file gets passed and we can't find it,
230 // inform the developer using some debug output
231 qCWarning(DEBUG_KXMLGUI) << "cannot find .rc file" << _file << "for component" << componentName();
232 }
233
234 // make sure to merge the settings from any file specified by setLocalXMLFile()
235 if (!d->m_localXMLFile.isEmpty() && !file.endsWith(QLatin1String("ui_standards.rc"))) {
236 const bool exists = QDir::isRelativePath(d->m_localXMLFile) || QFile::exists(d->m_localXMLFile);
237 if (exists && !allFiles.contains(d->m_localXMLFile)) {
238 allFiles.prepend(d->m_localXMLFile);
239 }
240 }
241
242 QString doc;
243 if (!allFiles.isEmpty()) {
244 file = findMostRecentXMLFile(allFiles, doc);
245 }
246
247 // Always call setXML, even on error, so that we don't keep all ui_standards.rc menus.
248 setXML(doc, merge);
249}
250
252{
253 d->m_localXMLFile = file;
254}
255
256void KXMLGUIClient::replaceXMLFile(const QString &xmlfile, const QString &localxmlfile, bool merge)
257{
258 if (!QDir::isAbsolutePath(xmlfile)) {
259 qCWarning(DEBUG_KXMLGUI) << "xml file" << xmlfile << "is not an absolute path";
260 }
261
262 setLocalXMLFile(localxmlfile);
263 setXMLFile(xmlfile, merge);
264}
265
266// The top document element may have translation domain attribute set,
267// or the translation domain may be implicitly the application domain.
268// This domain must be used to fetch translations for all text elements
269// in the document that do not have their own domain attribute.
270// In order to preserve this semantics through document mergings,
271// the top or application domain must be propagated to all text elements
272// lacking their own domain attribute.
273static void propagateTranslationDomain(QDomDocument &doc, const QStringList &tagNames)
274{
275 const QLatin1String attrDomain("translationDomain");
276 QDomElement base = doc.documentElement();
277 QString domain = base.attribute(attrDomain);
278 if (domain.isEmpty()) {
280 if (domain.isEmpty()) {
281 return;
282 }
283 }
284 for (const QString &tagName : tagNames) {
285 QDomNodeList textNodes = base.elementsByTagName(tagName);
286 for (int i = 0; i < textNodes.length(); ++i) {
287 QDomElement e = textNodes.item(i).toElement();
288 QString localDomain = e.attribute(attrDomain);
289 if (localDomain.isEmpty()) {
290 e.setAttribute(attrDomain, domain);
291 }
292 }
293 }
294}
295
296void KXMLGUIClient::setXML(const QString &document, bool merge)
297{
298 QDomDocument doc;
299 // QDomDocument raises a parse error on empty document, but we accept no app-specific document,
300 // in which case you only get ui_standards.rc layout.
301 if (!document.isEmpty()) {
302 const QDomDocument::ParseResult result = doc.setContent(document);
303 if (!result) {
304 qCCritical(DEBUG_KXMLGUI) << "Error parsing XML document:" << result.errorMessage << "at line" << result.errorLine << "column"
305 << result.errorColumn;
306#ifdef NDEBUG
307 setDOMDocument(QDomDocument(), merge); // otherwise empty menus from ui_standards.rc stay around
308#else
309 abort();
310#endif
311 return;
312 }
313 }
314
315 propagateTranslationDomain(doc, d->m_textTagNames);
316 setDOMDocument(doc, merge);
317}
318
319void KXMLGUIClient::setDOMDocument(const QDomDocument &document, bool merge)
320{
321 if (merge && !d->m_doc.isNull()) {
322 QDomElement base = d->m_doc.documentElement();
323
324 QDomElement e = document.documentElement();
325
326 // merge our original (global) xml with our new one
327 d->mergeXML(base, e, actionCollection());
328
329 // reassign our pointer as mergeXML might have done something
330 // strange to it
331 base = d->m_doc.documentElement();
332
333 // qCDebug(DEBUG_KXMLGUI) << "Result of xmlgui merging:" << d->m_doc.toString();
334
335 // we want some sort of failsafe.. just in case
336 if (base.isNull()) {
337 d->m_doc = document;
338 }
339 } else {
340 d->m_doc = document;
341 }
342
344}
345
346// if (equals(a,b)) is more readable than if (a.compare(b, Qt::CaseInsensitive)==0)
347static inline bool equalstr(const QString &a, const QString &b)
348{
349 return a.compare(b, Qt::CaseInsensitive) == 0;
350}
351static inline bool equalstr(const QString &a, QLatin1String b)
352{
353 return a.compare(b, Qt::CaseInsensitive) == 0;
354}
355
356bool KXMLGUIClientPrivate::mergeXML(QDomElement &base, QDomElement &additive, KActionCollection *actionCollection)
357{
358 const QLatin1String tagAction("Action");
359 const QLatin1String tagMerge("Merge");
360 const QLatin1String tagSeparator("Separator");
361 const QLatin1String tagMergeLocal("MergeLocal");
362 const QLatin1String tagText("text");
363 const QLatin1String attrAppend("append");
364 const QString attrName(QStringLiteral("name"));
365 const QString attrWeakSeparator(QStringLiteral("weakSeparator"));
366 const QString attrAlreadyVisited(QStringLiteral("alreadyVisited"));
367 const QString attrNoMerge(QStringLiteral("noMerge"));
368 const QLatin1String attrOne("1");
369
370 // there is a possibility that we don't want to merge in the
371 // additive.. rather, we might want to *replace* the base with the
372 // additive. this can be for any container.. either at a file wide
373 // level or a simple container level. we look for the 'noMerge'
374 // tag, in any event and just replace the old with the new
375 if (additive.attribute(attrNoMerge) == attrOne) { // ### use toInt() instead? (Simon)
376 base.parentNode().replaceChild(additive, base);
377 return true;
378 } else {
379 // Merge attributes
380 {
381 const QDomNamedNodeMap attribs = additive.attributes();
382 const int attribcount = attribs.count();
383
384 for (int i = 0; i < attribcount; ++i) {
385 const QDomNode node = attribs.item(i);
386 base.setAttribute(node.nodeName(), node.nodeValue());
387 }
388 }
389
390 // iterate over all elements in the container (of the global DOM tree)
391 QDomNode n = base.firstChild();
392 while (!n.isNull()) {
393 QDomElement e = n.toElement();
394 n = n.nextSibling(); // Advance now so that we can safely delete e
395 if (e.isNull()) {
396 continue;
397 }
398
399 const QString tag = e.tagName();
400
401 // if there's an action tag in the global tree and the action is
402 // not implemented, then we remove the element
403 if (equalstr(tag, tagAction)) {
404 const QString name = e.attribute(attrName);
405 if (!actionCollection->action(name) || !KAuthorized::authorizeAction(name)) {
406 // remove this child as we aren't using it
407 base.removeChild(e);
408 continue;
409 }
410 }
411
412 // if there's a separator defined in the global tree, then add an
413 // attribute, specifying that this is a "weak" separator
414 else if (equalstr(tag, tagSeparator)) {
415 e.setAttribute(attrWeakSeparator, uint(1));
416
417 // okay, hack time. if the last item was a weak separator OR
418 // this is the first item in a container, then we nuke the
419 // current one
421 if (prev.isNull() //
422 || (equalstr(prev.tagName(), tagSeparator) && !prev.attribute(attrWeakSeparator).isNull()) //
423 || (equalstr(prev.tagName(), tagText))) {
424 // the previous element was a weak separator or didn't exist
425 base.removeChild(e);
426 continue;
427 }
428 }
429
430 // the MergeLocal tag lets us specify where non-standard elements
431 // of the local tree shall be merged in. After inserting the
432 // elements we delete this element
433 else if (equalstr(tag, tagMergeLocal)) {
434 QDomNode it = additive.firstChild();
435 while (!it.isNull()) {
436 QDomElement newChild = it.toElement();
437 it = it.nextSibling();
438 if (newChild.isNull()) {
439 continue;
440 }
441
442 if (equalstr(newChild.tagName(), tagText)) {
443 continue;
444 }
445
446 if (newChild.attribute(attrAlreadyVisited) == attrOne) {
447 continue;
448 }
449
450 QString itAppend(newChild.attribute(attrAppend));
451 QString elemName(e.attribute(attrName));
452
453 if ((itAppend.isNull() && elemName.isEmpty()) || (itAppend == elemName)) {
454 // first, see if this new element matches a standard one in
455 // the global file. if it does, then we skip it as it will
456 // be merged in, later
457 QDomElement matchingElement = findMatchingElement(newChild, base);
458 if (matchingElement.isNull() || equalstr(newChild.tagName(), tagSeparator)) {
459 base.insertBefore(newChild, e);
460 }
461 }
462 }
463
464 base.removeChild(e);
465 continue;
466 }
467
468 else if (equalstr(tag, tagText)) {
469 continue;
470 } else if (equalstr(tag, tagMerge)) {
471 continue;
472 }
473
474 // in this last case we check for a separator tag and, if not, we
475 // can be sure that it is a container --> proceed with child nodes
476 // recursively and delete the just proceeded container item in
477 // case it is empty (if the recursive call returns true)
478 else {
479 QDomElement matchingElement = findMatchingElement(e, additive);
480 if (!matchingElement.isNull()) {
481 matchingElement.setAttribute(attrAlreadyVisited, uint(1));
482
483 if (mergeXML(e, matchingElement, actionCollection)) {
484 base.removeChild(e);
485 additive.removeChild(matchingElement); // make sure we don't append it below
486 continue;
487 }
488
489 continue;
490 } else {
491 // this is an important case here! We reach this point if the
492 // "local" tree does not contain a container definition for
493 // this container. However we have to call mergeXML recursively
494 // and make it check if there are actions implemented for this
495 // container. *If* none, then we can remove this container now
496 QDomElement dummy;
497 if (mergeXML(e, dummy, actionCollection)) {
498 base.removeChild(e);
499 }
500 continue;
501 }
502 }
503 }
504
505 // here we append all child elements which were not inserted
506 // previously via the LocalMerge tag
507 n = additive.firstChild();
508 while (!n.isNull()) {
509 QDomElement e = n.toElement();
510 n = n.nextSibling(); // Advance now so that we can safely delete e
511 if (e.isNull()) {
512 continue;
513 }
514
515 QDomElement matchingElement = findMatchingElement(e, base);
516
517 if (matchingElement.isNull()) {
518 base.appendChild(e);
519 }
520 }
521
522 // do one quick check to make sure that the last element was not
523 // a weak separator
524 QDomElement last = base.lastChild().toElement();
525 if (equalstr(last.tagName(), tagSeparator) && (!last.attribute(attrWeakSeparator).isNull())) {
526 base.removeChild(last);
527 }
528 }
529
530 return isEmptyContainer(base, actionCollection);
531}
532
533bool KXMLGUIClientPrivate::isEmptyContainer(const QDomElement &base, KActionCollection *actionCollection) const
534{
535 // now we check if we are empty (in which case we return "true", to
536 // indicate the caller that it can delete "us" (the base element
537 // argument of "this" call)
538 QDomNode n = base.firstChild();
539 while (!n.isNull()) {
540 const QDomElement e = n.toElement();
541 n = n.nextSibling(); // Advance now so that we can safely delete e
542 if (e.isNull()) {
543 continue;
544 }
545
546 const QString tag = e.tagName();
547
548 if (equalstr(tag, QLatin1String("Action"))) {
549 // if base contains an implemented action, then we must not get
550 // deleted (note that the actionCollection contains both,
551 // "global" and "local" actions)
552 if (actionCollection->action(e.attribute(QStringLiteral("name")))) {
553 return false;
554 }
555 } else if (equalstr(tag, QLatin1String("Separator"))) {
556 // if we have a separator which has *not* the weak attribute
557 // set, then it must be owned by the "local" tree in which case
558 // we must not get deleted either
559 const QString weakAttr = e.attribute(QStringLiteral("weakSeparator"));
560 if (weakAttr.isEmpty() || weakAttr.toInt() != 1) {
561 return false;
562 }
563 }
564
565 else if (equalstr(tag, QLatin1String("merge"))) {
566 continue;
567 }
568
569 // a text tag is NOT enough to spare this container
570 else if (equalstr(tag, QLatin1String("text"))) {
571 continue;
572 }
573
574 // what's left are non-empty containers! *don't* delete us in this
575 // case (at this position we can be *sure* that the container is
576 // *not* empty, as the recursive call for it was in the first loop
577 // which deleted the element in case the call returned "true"
578 else {
579 return false;
580 }
581 }
582
583 return true; // I'm empty, please delete me.
584}
585
586QDomElement KXMLGUIClientPrivate::findMatchingElement(const QDomElement &base, const QDomElement &additive)
587{
588 const QString idAttribute(base.tagName() == QLatin1String("ActionProperties") ? QStringLiteral("scheme") : QStringLiteral("name"));
589
590 QDomNode n = additive.firstChild();
591 while (!n.isNull()) {
592 QDomElement e = n.toElement();
593 n = n.nextSibling(); // Advance now so that we can safely delete e -- TODO we don't, so simplify this
594 if (e.isNull()) {
595 continue;
596 }
597
598 const QString tag = e.tagName();
599 // skip all action and merge tags as we will never use them
600 if (equalstr(tag, QLatin1String("Action")) //
601 || equalstr(tag, QLatin1String("MergeLocal"))) {
602 continue;
603 }
604
605 // now see if our tags are equivalent
606 if (equalstr(tag, base.tagName()) //
607 && e.attribute(idAttribute) == base.attribute(idAttribute)) {
608 return e;
609 }
610 }
611
612 // nope, return a (now) null element
613 return QDomElement();
614}
615
617{
618 d->m_buildDocument = doc;
619}
620
622{
623 return d->m_buildDocument;
624}
625
627{
628 d->m_factory = factory;
629}
630
632{
633 return d->m_factory;
634}
635
637{
638 return d->m_parent;
639}
640
642{
643 if (child->d->m_parent) {
644 child->d->m_parent->removeChildClient(child);
645 }
646 d->m_children.append(child);
647 child->d->m_parent = this;
648}
649
651{
652 assert(d->m_children.contains(child));
653 d->m_children.removeAll(child);
654 child->d->m_parent = nullptr;
655}
656
657/*bool KXMLGUIClient::addSuperClient( KXMLGUIClient *super )
658{
659 if ( d->m_supers.contains( super ) )
660 return false;
661 d->m_supers.append( super );
662 return true;
663}*/
664
666{
667 return d->m_children;
668}
669
671{
672 d->m_builder = builder;
673}
674
676{
677 return d->m_builder;
678}
679
680void KXMLGUIClient::plugActionList(const QString &name, const QList<QAction *> &actionList)
681{
682 if (!d->m_factory) {
683 return;
684 }
685
686 d->m_factory->plugActionList(this, name, actionList);
687}
688
690{
691 if (!d->m_factory) {
692 return;
693 }
694
695 d->m_factory->unplugActionList(this, name);
696}
697
698QString KXMLGUIClient::findMostRecentXMLFile(const QStringList &files, QString &doc)
699{
700 KXmlGuiVersionHandler versionHandler(files);
701 doc = versionHandler.finalDocument();
702 return versionHandler.finalFile();
703}
704
705void KXMLGUIClient::addStateActionEnabled(const QString &state, const QString &action)
706{
707 StateChange stateChange = getActionsToChangeForState(state);
708
709 stateChange.actionsToEnable.append(action);
710 // qCDebug(DEBUG_KXMLGUI) << "KXMLGUIClient::addStateActionEnabled( " << state << ", " << action << ")";
711
712 d->m_actionsStateMap.insert(state, stateChange);
713}
714
715void KXMLGUIClient::addStateActionDisabled(const QString &state, const QString &action)
716{
717 StateChange stateChange = getActionsToChangeForState(state);
718
719 stateChange.actionsToDisable.append(action);
720 // qCDebug(DEBUG_KXMLGUI) << "KXMLGUIClient::addStateActionDisabled( " << state << ", " << action << ")";
721
722 d->m_actionsStateMap.insert(state, stateChange);
723}
724
725KXMLGUIClient::StateChange KXMLGUIClient::getActionsToChangeForState(const QString &state)
726{
727 return d->m_actionsStateMap[state];
728}
729
730void KXMLGUIClient::stateChanged(const QString &newstate, KXMLGUIClient::ReverseStateChange reverse)
731{
732 const StateChange stateChange = getActionsToChangeForState(newstate);
733
734 bool setTrue = (reverse == StateNoReverse);
735 bool setFalse = !setTrue;
736
737 // Enable actions which need to be enabled...
738 //
739 for (const auto &actionId : stateChange.actionsToEnable) {
740 QAction *action = actionCollection()->action(actionId);
741 if (action) {
742 action->setEnabled(setTrue);
743 }
744 }
745
746 // and disable actions which need to be disabled...
747 //
748 for (const auto &actionId : stateChange.actionsToDisable) {
749 QAction *action = actionCollection()->action(actionId);
750 if (action) {
751 action->setEnabled(setFalse);
752 }
753 }
754}
755
756void KXMLGUIClient::beginXMLPlug(QWidget *w)
757{
759 for (KXMLGUIClient *client : std::as_const(d->m_children)) {
760 client->beginXMLPlug(w);
761 }
762}
763
764void KXMLGUIClient::endXMLPlug()
765{
766}
767
768void KXMLGUIClient::prepareXMLUnplug(QWidget *w)
769{
771 for (KXMLGUIClient *client : std::as_const(d->m_children)) {
772 client->prepareXMLUnplug(w);
773 }
774}
775
776void KXMLGUIClient::virtual_hook(int, void *)
777{
778 /*BASE::virtual_hook( id, data );*/
779}
780
782{
783 enum {
784 ST_START,
785 ST_AFTER_OPEN,
786 ST_AFTER_GUI,
787 ST_EXPECT_VERSION,
788 ST_VERSION_NUM,
789 } state = ST_START;
790 const int length = xml.length();
791 for (int pos = 0; pos < length; pos++) {
792 switch (state) {
793 case ST_START:
794 if (xml[pos] == QLatin1Char('<')) {
795 state = ST_AFTER_OPEN;
796 }
797 break;
798 case ST_AFTER_OPEN: {
799 // Jump to gui..
800 const int guipos = xml.indexOf(QLatin1String("gui"), pos, Qt::CaseInsensitive);
801 if (guipos == -1) {
802 return QString(); // Reject
803 }
804
805 pos = guipos + 2; // Position at i, so we're moved ahead to the next character by the ++;
806 state = ST_AFTER_GUI;
807 break;
808 }
809 case ST_AFTER_GUI:
810 state = ST_EXPECT_VERSION;
811 break;
812 case ST_EXPECT_VERSION: {
813 const int verpos = xml.indexOf(QLatin1String("version"), pos, Qt::CaseInsensitive);
814 if (verpos == -1) {
815 return QString(); // Reject
816 }
817 pos = verpos + 7; // strlen("version") is 7
818 while (xml.at(pos).isSpace()) {
819 ++pos;
820 }
821 if (xml.at(pos++) != QLatin1Char('=')) {
822 return QString(); // Reject
823 }
824 while (xml.at(pos).isSpace()) {
825 ++pos;
826 }
827
828 state = ST_VERSION_NUM;
829 break;
830 }
831 case ST_VERSION_NUM: {
832 int endpos;
833 for (endpos = pos; endpos < length; endpos++) {
834 const ushort ch = xml[endpos].unicode();
835 if (ch >= QLatin1Char('0') && ch <= QLatin1Char('9')) {
836 continue; // Number..
837 }
838 if (ch == QLatin1Char('"')) { // End of parameter
839 break;
840 } else { // This shouldn't be here..
841 endpos = length;
842 }
843 }
844
845 if (endpos != pos && endpos < length) {
846 const QString matchCandidate = xml.mid(pos, endpos - pos); // Don't include " ".
847 return matchCandidate;
848 }
849
850 state = ST_EXPECT_VERSION; // Try to match a well-formed version..
851 break;
852 } // case..
853 } // switch
854 } // for
855
856 return QString();
857}
A container for a set of QAction objects.
void addAssociatedWidget(QWidget *widget)
Associate all actions in this collection to the given widget, including any actions added after this ...
void setComponentName(const QString &componentName)
Set the componentName associated with this action collection.
void setComponentDisplayName(const QString &displayName)
Set the component display name associated with this action collection.
void removeAssociatedWidget(QWidget *widget)
Remove an association between all actions in this collection and the given widget,...
QAction * action(int index) const
Return the QAction* at position index in the action collection.
static Q_INVOKABLE bool authorizeAction(const QString &action)
static QByteArray applicationDomain()
Implements the creation of the GUI (menubar, menus and toolbars) as requested by the GUI factory.
A KXMLGUIClient can be used with KXMLGUIFactory to create a GUI from actions and an XML document,...
KXMLGUIClient * parentClient() const
KXMLGUIClients can form a simple child/parent object tree.
virtual QString xmlFile() const
This will return the name of the XML file as set by setXMLFile().
virtual void setDOMDocument(const QDomDocument &document, bool merge=false)
Sets the Document for the part, describing the layout of the GUI.
static QString findVersionNumber(const QString &xml)
Returns the version number of the given xml data (belonging to an xml rc file)
void replaceXMLFile(const QString &xmlfile, const QString &localxmlfile, bool merge=false)
Sets a new xmlFile() and localXMLFile().
KXMLGUIClient()
Constructs a KXMLGUIClient which can be used with a KXMLGUIFactory to create a GUI from actions and a...
virtual QString componentName() const
void removeChildClient(KXMLGUIClient *child)
Removes the given child from the client's children list.
void reloadXML()
Forces this client to re-read its XML resource file.
virtual void stateChanged(const QString &newstate, ReverseStateChange reverse=StateNoReverse)
Actions can collectively be assigned a "State".
virtual QDomDocument domDocument() const
static QString standardsXmlFileLocation()
Return the full path to the ui_standards.rc, might return a resource path.
virtual KActionCollection * actionCollection() const
Retrieves the entire action collection for the GUI client.
void unplugActionList(const QString &name)
Unplugs the action list name from the XMLGUI.
void plugActionList(const QString &name, const QList< QAction * > &actionList)
ActionLists are a way for XMLGUI to support dynamic lists of actions.
virtual void setXMLFile(const QString &file, bool merge=false, bool setXMLDoc=true)
Sets the name of the rc file containing the XML for the part.
QList< KXMLGUIClient * > childClients()
Retrieves a list of all child clients.
KXMLGUIFactory * factory() const
Retrieves a pointer to the KXMLGUIFactory this client is associated with (will return nullptr if the ...
virtual ~KXMLGUIClient()
Destructs the KXMLGUIClient.
void setXMLGUIBuildDocument(const QDomDocument &doc)
void setClientBuilder(KXMLGUIBuilder *builder)
A client can have an own KXMLGUIBuilder.
virtual void setLocalXMLFile(const QString &file)
Set the full path to the "local" xml file, the one used for saving toolbar and shortcut changes.
void loadStandardsXmlFile()
Load the ui_standards.rc file.
QAction * action(const QString &name) const
Retrieves an action of the client by name.
void setFactory(KXMLGUIFactory *factory)
This method is called by the KXMLGUIFactory as soon as the client is added to the KXMLGUIFactory's GU...
virtual void setXML(const QString &document, bool merge=false)
Sets the XML for the part.
KXMLGUIBuilder * clientBuilder() const
Retrieves the client's GUI builder or nullptr if no client specific builder has been assigned via set...
void insertChildClient(KXMLGUIClient *child)
Use this method to make a client a child client of another client.
QDomDocument xmlguiBuildDocument() const
virtual void setComponentName(const QString &componentName, const QString &componentDisplayName)
Sets the component name for this part.
KXMLGUIFactory, together with KXMLGUIClient objects, can be used to create a GUI of container widgets...
static QString readConfigFile(const QString &filename, const QString &componentName=QString())
Q_SCRIPTABLE Q_NOREPLY void abort()
QString name(StandardAction id)
void setEnabled(bool)
bool isSpace(char32_t ucs4)
bool isAbsolutePath(const QString &path)
bool isRelativePath(const QString &path)
QDomElement documentElement() const const
ParseResult setContent(QAnyStringView text, ParseOptions options)
QString attribute(const QString &name, const QString &defValue) const const
QDomNamedNodeMap attributes() const const
QDomNodeList elementsByTagName(const QString &tagname) const const
void setAttribute(const QString &name, const QString &value)
QString tagName() const const
int count() const const
QDomNode item(int index) const const
QDomNode appendChild(const QDomNode &newChild)
QDomNode firstChild() const const
QDomNode insertBefore(const QDomNode &newChild, const QDomNode &refChild)
bool isNull() const const
QDomNode lastChild() const const
QDomNode nextSibling() const const
QString nodeName() const const
QString nodeValue() const const
QDomNode parentNode() const const
QDomNode previousSibling() const const
QDomNode removeChild(const QDomNode &oldChild)
QDomNode replaceChild(const QDomNode &newChild, const QDomNode &oldChild)
QDomElement toElement() const const
QDomNode item(int index) const const
int length() const const
bool exists() const const
void append(QList< T > &&value)
bool isEmpty() const const
void prepend(parameter_type value)
QString locate(StandardLocation type, const QString &fileName, LocateOptions options)
QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options)
QString writableLocation(StandardLocation type)
const QChar at(qsizetype position) const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString fromUtf8(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool isNull() const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
int toInt(bool *ok, int base) const const
const QChar * unicode() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
CaseInsensitive
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:19:32 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.