KXmlGui

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

KDE's Doxygen guidelines are available online.