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

KDE's Doxygen guidelines are available online.