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

KDE's Doxygen guidelines are available online.