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 delete d;
101}
102
104{
105 QAction *act = actionCollection()->action(name);
106 if (!act) {
107 for (KXMLGUIClient *client : std::as_const(d->m_children)) {
108 act = client->actionCollection()->action(name);
109 if (act) {
110 break;
111 }
112 }
113 }
114 return act;
115}
116
118{
119 if (!d->m_actionCollection) {
120 d->m_actionCollection = new KActionCollection(this);
121 d->m_actionCollection->setObjectName(QStringLiteral("KXMLGUIClient-KActionCollection"));
122 }
123 return d->m_actionCollection;
124}
125
127{
128 return actionCollection()->action(element.attribute(QStringLiteral("name")));
129}
130
132{
133 return d->m_componentName;
134}
135
137{
138 return d->m_doc;
139}
140
142{
143 return d->m_xmlFile;
144}
145
146QString KXMLGUIClient::localXMLFile() const
147{
148 if (!d->m_localXMLFile.isEmpty()) {
149 return d->m_localXMLFile;
150 }
151
152 if (!QDir::isRelativePath(d->m_xmlFile)) {
153 return QString(); // can't save anything here
154 }
155
156 if (d->m_xmlFile.isEmpty()) { // setXMLFile not called at all, can't save. Use case: ToolBarHandler
157 return QString();
158 }
159
161}
162
164{
165 // TODO: this method can't be used for the KXmlGuiWindow, since it doesn't merge in ui_standards.rc!
166 // -> KDE5: load ui_standards_rc in setXMLFile using a flag, and remember that flag?
167 // and then KEditToolBar can use reloadXML.
168 QString file(xmlFile());
169 if (!file.isEmpty()) {
170 setXMLFile(file);
171 }
172}
173
174void KXMLGUIClient::setComponentName(const QString &componentName, const QString &componentDisplayName)
175{
176 d->m_componentName = componentName;
178 actionCollection()->setComponentDisplayName(componentDisplayName);
179 if (d->m_builder) {
180 d->m_builder->setBuilderClient(this);
181 }
182}
183
185{
186 if (QStandardPaths::isTestModeEnabled()) {
187 return QStringLiteral(":/kxmlgui5/ui_standards.rc");
188 }
189 QString file = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QStringLiteral("kxmlgui5/ui_standards.rc"));
190 if (file.isEmpty()) {
191 // fallback to resource, to allow to use the rc file compiled into this framework, must exist!
192 file = QStringLiteral(":/kxmlgui5/ui_standards.rc");
193 Q_ASSERT(QFile::exists(file));
194 }
195 return file;
196}
197
202
203void KXMLGUIClient::setXMLFile(const QString &_file, bool merge, bool setXMLDoc)
204{
205 // store our xml file name
206 if (!_file.isNull()) {
207 d->m_xmlFile = _file;
208 }
209
210 if (!setXMLDoc) {
211 return;
212 }
213
214 QString file = _file;
215 QStringList allFiles;
216 if (!QDir::isRelativePath(file)) {
217 allFiles.append(file);
218 } else {
219 const QString filter = componentName() + QLatin1Char('/') + _file;
220
221 // files on filesystem
222 allFiles << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kxmlgui5/") + filter);
223
224 // built-in resource file
225 const QString qrcFile(QLatin1String(":/kxmlgui5/") + filter);
226 if (QFile::exists(qrcFile)) {
227 allFiles << qrcFile;
228 }
229 }
230 if (allFiles.isEmpty() && !_file.isEmpty()) {
231 // if a non-empty file gets passed and we can't find it,
232 // inform the developer using some debug output
233 qCWarning(DEBUG_KXMLGUI) << "cannot find .rc file" << _file << "for component" << componentName();
234 }
235
236 // make sure to merge the settings from any file specified by setLocalXMLFile()
237 if (!d->m_localXMLFile.isEmpty() && !file.endsWith(QLatin1String("ui_standards.rc"))) {
238 const bool exists = QDir::isRelativePath(d->m_localXMLFile) || QFile::exists(d->m_localXMLFile);
239 if (exists && !allFiles.contains(d->m_localXMLFile)) {
240 allFiles.prepend(d->m_localXMLFile);
241 }
242 }
243
244 QString doc;
245 if (!allFiles.isEmpty()) {
246 file = findMostRecentXMLFile(allFiles, doc);
247 }
248
249 // Always call setXML, even on error, so that we don't keep all ui_standards.rc menus.
250 setXML(doc, merge);
251}
252
254{
255 d->m_localXMLFile = file;
256}
257
258void KXMLGUIClient::replaceXMLFile(const QString &xmlfile, const QString &localxmlfile, bool merge)
259{
260 if (!QDir::isAbsolutePath(xmlfile)) {
261 qCWarning(DEBUG_KXMLGUI) << "xml file" << xmlfile << "is not an absolute path";
262 }
263
264 setLocalXMLFile(localxmlfile);
265 setXMLFile(xmlfile, merge);
266}
267
268// The top document element may have translation domain attribute set,
269// or the translation domain may be implicitly the application domain.
270// This domain must be used to fetch translations for all text elements
271// in the document that do not have their own domain attribute.
272// In order to preserve this semantics through document mergings,
273// the top or application domain must be propagated to all text elements
274// lacking their own domain attribute.
275static void propagateTranslationDomain(QDomDocument &doc, const QStringList &tagNames)
276{
277 const QLatin1String attrDomain("translationDomain");
278 QDomElement base = doc.documentElement();
279 QString domain = base.attribute(attrDomain);
280 if (domain.isEmpty()) {
282 if (domain.isEmpty()) {
283 return;
284 }
285 }
286 for (const QString &tagName : tagNames) {
287 QDomNodeList textNodes = base.elementsByTagName(tagName);
288 for (int i = 0; i < textNodes.length(); ++i) {
289 QDomElement e = textNodes.item(i).toElement();
290 QString localDomain = e.attribute(attrDomain);
291 if (localDomain.isEmpty()) {
292 e.setAttribute(attrDomain, domain);
293 }
294 }
295 }
296}
297
298void KXMLGUIClient::setXML(const QString &document, bool merge)
299{
300 QDomDocument doc;
301 // QDomDocument raises a parse error on empty document, but we accept no app-specific document,
302 // in which case you only get ui_standards.rc layout.
303 if (!document.isEmpty()) {
304 const QDomDocument::ParseResult result = doc.setContent(document);
305 if (!result) {
306 qCCritical(DEBUG_KXMLGUI) << "Error parsing XML document:" << result.errorMessage << "at line" << result.errorLine << "column"
307 << result.errorColumn;
308#ifdef NDEBUG
309 setDOMDocument(QDomDocument(), merge); // otherwise empty menus from ui_standards.rc stay around
310#else
311 abort();
312#endif
313 return;
314 }
315 }
316
317 propagateTranslationDomain(doc, d->m_textTagNames);
318 setDOMDocument(doc, merge);
319}
320
321void KXMLGUIClient::setDOMDocument(const QDomDocument &document, bool merge)
322{
323 if (merge && !d->m_doc.isNull()) {
324 QDomElement base = d->m_doc.documentElement();
325
326 QDomElement e = document.documentElement();
327
328 // merge our original (global) xml with our new one
329 d->mergeXML(base, e, actionCollection());
330
331 // reassign our pointer as mergeXML might have done something
332 // strange to it
333 base = d->m_doc.documentElement();
334
335 // qCDebug(DEBUG_KXMLGUI) << "Result of xmlgui merging:" << d->m_doc.toString();
336
337 // we want some sort of failsafe.. just in case
338 if (base.isNull()) {
339 d->m_doc = document;
340 }
341 } else {
342 d->m_doc = document;
343 }
344
346}
347
348// if (equals(a,b)) is more readable than if (a.compare(b, Qt::CaseInsensitive)==0)
349static inline bool equalstr(const QString &a, const QString &b)
350{
351 return a.compare(b, Qt::CaseInsensitive) == 0;
352}
353static inline bool equalstr(const QString &a, QLatin1String b)
354{
355 return a.compare(b, Qt::CaseInsensitive) == 0;
356}
357
358bool KXMLGUIClientPrivate::mergeXML(QDomElement &base, QDomElement &additive, KActionCollection *actionCollection)
359{
360 const QLatin1String tagAction("Action");
361 const QLatin1String tagMerge("Merge");
362 const QLatin1String tagSeparator("Separator");
363 const QLatin1String tagMergeLocal("MergeLocal");
364 const QLatin1String tagText("text");
365 const QLatin1String attrAppend("append");
366 const QString attrName(QStringLiteral("name"));
367 const QString attrWeakSeparator(QStringLiteral("weakSeparator"));
368 const QString attrAlreadyVisited(QStringLiteral("alreadyVisited"));
369 const QString attrNoMerge(QStringLiteral("noMerge"));
370 const QLatin1String attrOne("1");
371
372 // there is a possibility that we don't want to merge in the
373 // additive.. rather, we might want to *replace* the base with the
374 // additive. this can be for any container.. either at a file wide
375 // level or a simple container level. we look for the 'noMerge'
376 // tag, in any event and just replace the old with the new
377 if (additive.attribute(attrNoMerge) == attrOne) { // ### use toInt() instead? (Simon)
378 base.parentNode().replaceChild(additive, base);
379 return true;
380 } else {
381 // Merge attributes
382 {
383 const QDomNamedNodeMap attribs = additive.attributes();
384 const int attribcount = attribs.count();
385
386 for (int i = 0; i < attribcount; ++i) {
387 const QDomNode node = attribs.item(i);
388 base.setAttribute(node.nodeName(), node.nodeValue());
389 }
390 }
391
392 // iterate over all elements in the container (of the global DOM tree)
393 QDomNode n = base.firstChild();
394 while (!n.isNull()) {
395 QDomElement e = n.toElement();
396 n = n.nextSibling(); // Advance now so that we can safely delete e
397 if (e.isNull()) {
398 continue;
399 }
400
401 const QString tag = e.tagName();
402
403 // if there's an action tag in the global tree and the action is
404 // not implemented, then we remove the element
405 if (equalstr(tag, tagAction)) {
406 const QString name = e.attribute(attrName);
407 if (!actionCollection->action(name) || !KAuthorized::authorizeAction(name)) {
408 // remove this child as we aren't using it
409 base.removeChild(e);
410 continue;
411 }
412 }
413
414 // if there's a separator defined in the global tree, then add an
415 // attribute, specifying that this is a "weak" separator
416 else if (equalstr(tag, tagSeparator)) {
417 e.setAttribute(attrWeakSeparator, uint(1));
418
419 // okay, hack time. if the last item was a weak separator OR
420 // this is the first item in a container, then we nuke the
421 // current one
423 if (prev.isNull() //
424 || (equalstr(prev.tagName(), tagSeparator) && !prev.attribute(attrWeakSeparator).isNull()) //
425 || (equalstr(prev.tagName(), tagText))) {
426 // the previous element was a weak separator or didn't exist
427 base.removeChild(e);
428 continue;
429 }
430 }
431
432 // the MergeLocal tag lets us specify where non-standard elements
433 // of the local tree shall be merged in. After inserting the
434 // elements we delete this element
435 else if (equalstr(tag, tagMergeLocal)) {
436 QDomNode it = additive.firstChild();
437 while (!it.isNull()) {
438 QDomElement newChild = it.toElement();
439 it = it.nextSibling();
440 if (newChild.isNull()) {
441 continue;
442 }
443
444 if (equalstr(newChild.tagName(), tagText)) {
445 continue;
446 }
447
448 if (newChild.attribute(attrAlreadyVisited) == attrOne) {
449 continue;
450 }
451
452 QString itAppend(newChild.attribute(attrAppend));
453 QString elemName(e.attribute(attrName));
454
455 if ((itAppend.isNull() && elemName.isEmpty()) || (itAppend == elemName)) {
456 // first, see if this new element matches a standard one in
457 // the global file. if it does, then we skip it as it will
458 // be merged in, later
459 QDomElement matchingElement = findMatchingElement(newChild, base);
460 if (matchingElement.isNull() || equalstr(newChild.tagName(), tagSeparator)) {
461 base.insertBefore(newChild, e);
462 }
463 }
464 }
465
466 base.removeChild(e);
467 continue;
468 }
469
470 else if (equalstr(tag, tagText)) {
471 continue;
472 } else if (equalstr(tag, tagMerge)) {
473 continue;
474 }
475
476 // in this last case we check for a separator tag and, if not, we
477 // can be sure that it is a container --> proceed with child nodes
478 // recursively and delete the just proceeded container item in
479 // case it is empty (if the recursive call returns true)
480 else {
481 QDomElement matchingElement = findMatchingElement(e, additive);
482 if (!matchingElement.isNull()) {
483 matchingElement.setAttribute(attrAlreadyVisited, uint(1));
484
485 if (mergeXML(e, matchingElement, actionCollection)) {
486 base.removeChild(e);
487 additive.removeChild(matchingElement); // make sure we don't append it below
488 continue;
489 }
490
491 continue;
492 } else {
493 // this is an important case here! We reach this point if the
494 // "local" tree does not contain a container definition for
495 // this container. However we have to call mergeXML recursively
496 // and make it check if there are actions implemented for this
497 // container. *If* none, then we can remove this container now
498 QDomElement dummy;
499 if (mergeXML(e, dummy, actionCollection)) {
500 base.removeChild(e);
501 }
502 continue;
503 }
504 }
505 }
506
507 // here we append all child elements which were not inserted
508 // previously via the LocalMerge tag
509 n = additive.firstChild();
510 while (!n.isNull()) {
511 QDomElement e = n.toElement();
512 n = n.nextSibling(); // Advance now so that we can safely delete e
513 if (e.isNull()) {
514 continue;
515 }
516
517 QDomElement matchingElement = findMatchingElement(e, base);
518
519 if (matchingElement.isNull()) {
520 base.appendChild(e);
521 }
522 }
523
524 // do one quick check to make sure that the last element was not
525 // a weak separator
526 QDomElement last = base.lastChild().toElement();
527 if (equalstr(last.tagName(), tagSeparator) && (!last.attribute(attrWeakSeparator).isNull())) {
528 base.removeChild(last);
529 }
530 }
531
532 return isEmptyContainer(base, actionCollection);
533}
534
535bool KXMLGUIClientPrivate::isEmptyContainer(const QDomElement &base, KActionCollection *actionCollection) const
536{
537 // now we check if we are empty (in which case we return "true", to
538 // indicate the caller that it can delete "us" (the base element
539 // argument of "this" call)
540 QDomNode n = base.firstChild();
541 while (!n.isNull()) {
542 const QDomElement e = n.toElement();
543 n = n.nextSibling(); // Advance now so that we can safely delete e
544 if (e.isNull()) {
545 continue;
546 }
547
548 const QString tag = e.tagName();
549
550 if (equalstr(tag, QLatin1String("Action"))) {
551 // if base contains an implemented action, then we must not get
552 // deleted (note that the actionCollection contains both,
553 // "global" and "local" actions)
554 if (actionCollection->action(e.attribute(QStringLiteral("name")))) {
555 return false;
556 }
557 } else if (equalstr(tag, QLatin1String("Separator"))) {
558 // if we have a separator which has *not* the weak attribute
559 // set, then it must be owned by the "local" tree in which case
560 // we must not get deleted either
561 const QString weakAttr = e.attribute(QStringLiteral("weakSeparator"));
562 if (weakAttr.isEmpty() || weakAttr.toInt() != 1) {
563 return false;
564 }
565 }
566
567 else if (equalstr(tag, QLatin1String("merge"))) {
568 continue;
569 }
570
571 // a text tag is NOT enough to spare this container
572 else if (equalstr(tag, QLatin1String("text"))) {
573 continue;
574 }
575
576 // what's left are non-empty containers! *don't* delete us in this
577 // case (at this position we can be *sure* that the container is
578 // *not* empty, as the recursive call for it was in the first loop
579 // which deleted the element in case the call returned "true"
580 else {
581 return false;
582 }
583 }
584
585 return true; // I'm empty, please delete me.
586}
587
588QDomElement KXMLGUIClientPrivate::findMatchingElement(const QDomElement &base, const QDomElement &additive)
589{
590 const QString idAttribute(base.tagName() == QLatin1String("ActionProperties") ? QStringLiteral("scheme") : QStringLiteral("name"));
591
592 QDomNode n = additive.firstChild();
593 while (!n.isNull()) {
594 QDomElement e = n.toElement();
595 n = n.nextSibling(); // Advance now so that we can safely delete e -- TODO we don't, so simplify this
596 if (e.isNull()) {
597 continue;
598 }
599
600 const QString tag = e.tagName();
601 // skip all action and merge tags as we will never use them
602 if (equalstr(tag, QLatin1String("Action")) //
603 || equalstr(tag, QLatin1String("MergeLocal"))) {
604 continue;
605 }
606
607 // now see if our tags are equivalent
608 if (equalstr(tag, base.tagName()) //
609 && e.attribute(idAttribute) == base.attribute(idAttribute)) {
610 return e;
611 }
612 }
613
614 // nope, return a (now) null element
615 return QDomElement();
616}
617
619{
620 d->m_buildDocument = doc;
621}
622
624{
625 return d->m_buildDocument;
626}
627
629{
630 d->m_factory = factory;
631}
632
634{
635 return d->m_factory;
636}
637
639{
640 return d->m_parent;
641}
642
644{
645 if (child->d->m_parent) {
646 child->d->m_parent->removeChildClient(child);
647 }
648 d->m_children.append(child);
649 child->d->m_parent = this;
650}
651
653{
654 assert(d->m_children.contains(child));
655 d->m_children.removeAll(child);
656 child->d->m_parent = nullptr;
657}
658
659/*bool KXMLGUIClient::addSuperClient( KXMLGUIClient *super )
660{
661 if ( d->m_supers.contains( super ) )
662 return false;
663 d->m_supers.append( super );
664 return true;
665}*/
666
668{
669 return d->m_children;
670}
671
673{
674 d->m_builder = builder;
675}
676
678{
679 return d->m_builder;
680}
681
682void KXMLGUIClient::plugActionList(const QString &name, const QList<QAction *> &actionList)
683{
684 if (!d->m_factory) {
685 return;
686 }
687
688 d->m_factory->plugActionList(this, name, actionList);
689}
690
692{
693 if (!d->m_factory) {
694 return;
695 }
696
697 d->m_factory->unplugActionList(this, name);
698}
699
700QString KXMLGUIClient::findMostRecentXMLFile(const QStringList &files, QString &doc)
701{
702 KXmlGuiVersionHandler versionHandler(files);
703 doc = versionHandler.finalDocument();
704 return versionHandler.finalFile();
705}
706
707void KXMLGUIClient::addStateActionEnabled(const QString &state, const QString &action)
708{
709 StateChange stateChange = getActionsToChangeForState(state);
710
711 stateChange.actionsToEnable.append(action);
712 // qCDebug(DEBUG_KXMLGUI) << "KXMLGUIClient::addStateActionEnabled( " << state << ", " << action << ")";
713
714 d->m_actionsStateMap.insert(state, stateChange);
715}
716
717void KXMLGUIClient::addStateActionDisabled(const QString &state, const QString &action)
718{
719 StateChange stateChange = getActionsToChangeForState(state);
720
721 stateChange.actionsToDisable.append(action);
722 // qCDebug(DEBUG_KXMLGUI) << "KXMLGUIClient::addStateActionDisabled( " << state << ", " << action << ")";
723
724 d->m_actionsStateMap.insert(state, stateChange);
725}
726
727KXMLGUIClient::StateChange KXMLGUIClient::getActionsToChangeForState(const QString &state)
728{
729 return d->m_actionsStateMap[state];
730}
731
732void KXMLGUIClient::stateChanged(const QString &newstate, KXMLGUIClient::ReverseStateChange reverse)
733{
734 const StateChange stateChange = getActionsToChangeForState(newstate);
735
736 bool setTrue = (reverse == StateNoReverse);
737 bool setFalse = !setTrue;
738
739 // Enable actions which need to be enabled...
740 //
741 for (const auto &actionId : stateChange.actionsToEnable) {
742 QAction *action = actionCollection()->action(actionId);
743 if (action) {
744 action->setEnabled(setTrue);
745 }
746 }
747
748 // and disable actions which need to be disabled...
749 //
750 for (const auto &actionId : stateChange.actionsToDisable) {
751 QAction *action = actionCollection()->action(actionId);
752 if (action) {
753 action->setEnabled(setFalse);
754 }
755 }
756}
757
758void KXMLGUIClient::beginXMLPlug(QWidget *w)
759{
761 for (KXMLGUIClient *client : std::as_const(d->m_children)) {
762 client->beginXMLPlug(w);
763 }
764}
765
766void KXMLGUIClient::endXMLPlug()
767{
768}
769
770void KXMLGUIClient::prepareXMLUnplug(QWidget *w)
771{
773 for (KXMLGUIClient *client : std::as_const(d->m_children)) {
774 client->prepareXMLUnplug(w);
775 }
776}
777
778void KXMLGUIClient::virtual_hook(int, void *)
779{
780 /*BASE::virtual_hook( id, data );*/
781}
782
784{
785 enum {
786 ST_START,
787 ST_AFTER_OPEN,
788 ST_AFTER_GUI,
789 ST_EXPECT_VERSION,
790 ST_VERSION_NUM,
791 } state = ST_START;
792 const int length = xml.length();
793 for (int pos = 0; pos < length; pos++) {
794 switch (state) {
795 case ST_START:
796 if (xml[pos] == QLatin1Char('<')) {
797 state = ST_AFTER_OPEN;
798 }
799 break;
800 case ST_AFTER_OPEN: {
801 // Jump to gui..
802 const int guipos = xml.indexOf(QLatin1String("gui"), pos, Qt::CaseInsensitive);
803 if (guipos == -1) {
804 return QString(); // Reject
805 }
806
807 pos = guipos + 2; // Position at i, so we're moved ahead to the next character by the ++;
808 state = ST_AFTER_GUI;
809 break;
810 }
811 case ST_AFTER_GUI:
812 state = ST_EXPECT_VERSION;
813 break;
814 case ST_EXPECT_VERSION: {
815 const int verpos = xml.indexOf(QLatin1String("version"), pos, Qt::CaseInsensitive);
816 if (verpos == -1) {
817 return QString(); // Reject
818 }
819 pos = verpos + 7; // strlen("version") is 7
820 while (xml.at(pos).isSpace()) {
821 ++pos;
822 }
823 if (xml.at(pos++) != QLatin1Char('=')) {
824 return QString(); // Reject
825 }
826 while (xml.at(pos).isSpace()) {
827 ++pos;
828 }
829
830 state = ST_VERSION_NUM;
831 break;
832 }
833 case ST_VERSION_NUM: {
834 int endpos;
835 for (endpos = pos; endpos < length; endpos++) {
836 const ushort ch = xml[endpos].unicode();
837 if (ch >= QLatin1Char('0') && ch <= QLatin1Char('9')) {
838 continue; // Number..
839 }
840 if (ch == QLatin1Char('"')) { // End of parameter
841 break;
842 } else { // This shouldn't be here..
843 endpos = length;
844 }
845 }
846
847 if (endpos != pos && endpos < length) {
848 const QString matchCandidate = xml.mid(pos, endpos - pos); // Don't include " ".
849 return matchCandidate;
850 }
851
852 state = ST_EXPECT_VERSION; // Try to match a well-formed version..
853 break;
854 } // case..
855 } // switch
856 } // for
857
858 return QString();
859}
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(StandardShortcut 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 contains(const AT &value) const const
bool isEmpty() const const
void prepend(parameter_type value)
qsizetype removeAll(const AT &t)
iterator insert(const Key &key, const T &value)
void setObjectName(QAnyStringView name)
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 Tue Mar 26 2024 11:21:12 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.