KXmlGui

kxmlguifactory.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 1999, 2000 Simon Hausmann <[email protected]>
4  SPDX-FileCopyrightText: 2000 Kurt Granroth <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "config-xmlgui.h"
10 
11 #include "kxmlguifactory.h"
12 
13 #include "debug.h"
14 #include "kactioncollection.h"
15 #include "kshortcutschemeshelper_p.h"
16 #include "kshortcutsdialog.h"
17 #include "kxmlguibuilder.h"
18 #include "kxmlguiclient.h"
19 #include "kxmlguifactory_p.h"
20 
21 #include <QAction>
22 #include <QCoreApplication>
23 #include <QDir>
24 #include <QDomDocument>
25 #include <QFile>
26 #include <QStandardPaths>
27 #include <QTextCodec>
28 #include <QTextStream>
29 #include <QVariant>
30 #include <QWidget>
31 
32 #include <KConfigGroup>
33 #include <KSharedConfig>
34 #if HAVE_GLOBALACCEL
35 #include <KGlobalAccel>
36 #endif
37 
38 Q_DECLARE_METATYPE(QList<QKeySequence>)
39 
40 using namespace KXMLGUI;
41 
42 class KXMLGUIFactoryPrivate : public BuildState
43 {
44 public:
45  enum ShortcutOption { SetActiveShortcut = 1, SetDefaultShortcut = 2 };
46 
47  KXMLGUIFactoryPrivate()
48  {
49  m_rootNode = new ContainerNode(nullptr, QString(), QString());
50  attrName = QStringLiteral("name");
51  }
52  ~KXMLGUIFactoryPrivate()
53  {
54  delete m_rootNode;
55  }
56 
57  void pushState()
58  {
59  m_stateStack.push(*this);
60  }
61 
62  void popState()
63  {
64  BuildState::operator=(m_stateStack.pop());
65  }
66 
67  bool emptyState() const
68  {
69  return m_stateStack.isEmpty();
70  }
71 
72  QWidget *findRecursive(KXMLGUI::ContainerNode *node, bool tag);
73  QList<QWidget *> findRecursive(KXMLGUI::ContainerNode *node, const QString &tagName);
74  void applyActionProperties(const QDomElement &element, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut);
75  void configureAction(QAction *action, const QDomNamedNodeMap &attributes, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut);
76  void configureAction(QAction *action, const QDomAttr &attribute, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut);
77 
78  void applyShortcutScheme(const QString &schemeName, KXMLGUIClient *client, const QList<QAction *> &actions);
79  void refreshActionProperties(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &doc);
80  void saveDefaultActionProperties(const QList<QAction *> &actions);
81 
82  ContainerNode *m_rootNode;
83 
84  /*
85  * Contains the container which is searched for in ::container .
86  */
87  QString m_containerName;
88 
89  /*
90  * List of all clients
91  */
92  QList<KXMLGUIClient *> m_clients;
93 
94  QString attrName;
95 
96  BuildStateStack m_stateStack;
97 };
98 
99 QString KXMLGUIFactory::readConfigFile(const QString &filename, const QString &_componentName)
100 {
101  QString componentName = _componentName.isEmpty() ? QCoreApplication::applicationName() : _componentName;
102  QString xml_file;
103 
104  if (!QDir::isRelativePath(filename)) {
105  xml_file = filename;
106  } else {
107  // KF >= 5.1 (KDE_INSTALL_KXMLGUI5DIR)
108  xml_file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kxmlgui5/") + componentName + QLatin1Char('/') + filename);
109  if (!QFile::exists(xml_file)) {
110  // KF >= 5.4 (resource file)
111  xml_file = QLatin1String(":/kxmlgui5/") + componentName + QLatin1Char('/') + filename;
112  }
113 
114  bool warn = false;
115  if (!QFile::exists(xml_file)) {
116  // kdelibs4 / KF 5.0 solution
117  xml_file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, componentName + QLatin1Char('/') + filename);
118  warn = true;
119  }
120 
121  if (!QFile::exists(xml_file)) {
122  // kdelibs4 / KF 5.0 solution, and the caller includes the component name
123  // This was broken (lead to component/component/ in kdehome) and unnecessary
124  // (they can specify it with setComponentName instead)
126  warn = true;
127  }
128 
129  if (warn && !xml_file.isEmpty()) {
130  qCWarning(DEBUG_KXMLGUI) << "KXMLGUI file found at deprecated location" << xml_file
131  << "-- please use ${KDE_INSTALL_KXMLGUI5DIR} to install these files instead.";
132  }
133  }
134 
135  QFile file(xml_file);
136  if (xml_file.isEmpty() || !file.open(QIODevice::ReadOnly)) {
137  qCCritical(DEBUG_KXMLGUI) << "No such XML file" << filename;
138  return QString();
139  }
140 
141  QByteArray buffer(file.readAll());
142  return QString::fromUtf8(buffer.constData(), buffer.size());
143 }
144 
145 bool KXMLGUIFactory::saveConfigFile(const QDomDocument &doc, const QString &filename, const QString &_componentName)
146 {
147  QString componentName = _componentName.isEmpty() ? QCoreApplication::applicationName() : _componentName;
148  QString xml_file(filename);
149 
150  if (QDir::isRelativePath(xml_file))
151  xml_file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kxmlgui5/%1/%2").arg(componentName, filename);
152 
153  QFileInfo fileInfo(xml_file);
154  QDir().mkpath(fileInfo.absolutePath());
155  QFile file(xml_file);
156  if (xml_file.isEmpty() || !file.open(QIODevice::WriteOnly)) {
157  qCCritical(DEBUG_KXMLGUI) << "Could not write to" << filename;
158  return false;
159  }
160 
161  // write out our document
162  QTextStream ts(&file);
163  ts.setCodec(QTextCodec::codecForName("UTF-8"));
164  ts << doc;
165 
166  file.close();
167  return true;
168 }
169 
171  : QObject(parent)
172  , d(new KXMLGUIFactoryPrivate)
173 {
174  d->builder = builder;
175  d->guiClient = nullptr;
176  if (d->builder) {
177  d->builderContainerTags = d->builder->containerTags();
178  d->builderCustomTags = d->builder->customTags();
179  }
180 }
181 
183 {
184  for (KXMLGUIClient *client : qAsConst(d->m_clients)) {
185  client->setFactory(nullptr);
186  }
187 }
188 
190 {
191  // qCDebug(DEBUG_KXMLGUI) << client;
192  if (client->factory()) {
193  if (client->factory() == this) {
194  return;
195  } else {
196  client->factory()->removeClient(client); // just in case someone does stupid things ;-)
197  }
198  }
199 
200  if (d->emptyState()) {
201  Q_EMIT makingChanges(true);
202  }
203  d->pushState();
204 
205  // QTime dt; dt.start();
206 
207  d->guiClient = client;
208 
209  // add this client to our client list
210  if (!d->m_clients.contains(client)) {
211  d->m_clients.append(client);
212  }
213  // else
214  // qCDebug(DEBUG_KXMLGUI) << "XMLGUI client already added " << client;
215 
216  // Tell the client that plugging in is process and
217  // let it know what builder widget its mainwindow shortcuts
218  // should be attached to.
219  client->beginXMLPlug(d->builder->widget());
220 
221  // try to use the build document for building the client's GUI, as the build document
222  // contains the correct container state information (like toolbar positions, sizes, etc.) .
223  // if there is non available, then use the "real" document.
224  QDomDocument doc = client->xmlguiBuildDocument();
225  if (doc.documentElement().isNull()) {
226  doc = client->domDocument();
227  }
228 
229  QDomElement docElement = doc.documentElement();
230 
231  d->m_rootNode->index = -1;
232 
233  // cache some variables
234 
235  d->clientName = docElement.attribute(d->attrName);
236  d->clientBuilder = client->clientBuilder();
237 
238  if (d->clientBuilder) {
239  d->clientBuilderContainerTags = d->clientBuilder->containerTags();
240  d->clientBuilderCustomTags = d->clientBuilder->customTags();
241  } else {
242  d->clientBuilderContainerTags.clear();
243  d->clientBuilderCustomTags.clear();
244  }
245 
246  // load shortcut schemes, user-defined shortcuts and other action properties
247  d->saveDefaultActionProperties(client->actionCollection()->actions());
248  if (!doc.isNull()) {
249  d->refreshActionProperties(client, client->actionCollection()->actions(), doc);
250  }
251 
252  BuildHelper(*d, d->m_rootNode).build(docElement);
253 
254  // let the client know that we built its GUI.
255  client->setFactory(this);
256 
257  // call the finalizeGUI method, to fix up the positions of toolbars for example.
258  // Note: the client argument is ignored
259  d->builder->finalizeGUI(d->guiClient);
260 
261  // reset some variables, for safety
262  d->BuildState::reset();
263 
264  client->endXMLPlug();
265 
266  d->popState();
267 
268  Q_EMIT clientAdded(client);
269 
270  // build child clients
271  const auto children = client->childClients();
272  for (KXMLGUIClient *child : children) {
273  addClient(child);
274  }
275 
276  if (d->emptyState()) {
277  Q_EMIT makingChanges(false);
278  }
279  /*
280  QString unaddedActions;
281  Q_FOREACH (KActionCollection* ac, KActionCollection::allCollections())
282  Q_FOREACH (QAction* action, ac->actions())
283  if (action->associatedWidgets().isEmpty())
284  unaddedActions += action->objectName() + ' ';
285 
286  if (!unaddedActions.isEmpty())
287  qCWarning(DEBUG_KXMLGUI) << "The following actions are not plugged into the gui (shortcuts will not work): " << unaddedActions;
288  */
289 
290  // qCDebug(DEBUG_KXMLGUI) << "addClient took " << dt.elapsed();
291 }
292 
294 {
295  for (KXMLGUIClient *client : qAsConst(d->m_clients)) {
296  d->guiClient = client;
297  QDomDocument doc = client->xmlguiBuildDocument();
298  if (doc.documentElement().isNull()) {
299  client->reloadXML();
300  doc = client->domDocument();
301  }
302  d->refreshActionProperties(client, client->actionCollection()->actions(), doc);
303  }
304  d->guiClient = nullptr;
305 }
306 
307 static QString currentShortcutScheme()
308 {
309  const KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes");
310  return cg.readEntry("Current Scheme", "Default");
311 }
312 
313 // Find the right ActionProperties element, otherwise return null element
314 static QDomElement findActionPropertiesElement(const QDomDocument &doc)
315 {
316  const QLatin1String tagActionProp("ActionProperties");
317  const QString schemeName = currentShortcutScheme();
319  for (; !e.isNull(); e = e.nextSiblingElement()) {
320  if (QString::compare(e.tagName(), tagActionProp, Qt::CaseInsensitive) == 0
321  && (e.attribute(QStringLiteral("scheme"), QStringLiteral("Default")) == schemeName)) {
322  return e;
323  }
324  }
325  return QDomElement();
326 }
327 
328 void KXMLGUIFactoryPrivate::refreshActionProperties(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &doc)
329 {
330  // try to find and apply shortcuts schemes
331  const QString schemeName = KShortcutSchemesHelper::currentShortcutSchemeName();
332  // qCDebug(DEBUG_KXMLGUI) << client->componentName() << ": applying shortcut scheme" << schemeName;
333 
334  if (schemeName != QLatin1String("Default")) {
335  applyShortcutScheme(schemeName, client, actions);
336  } else {
337  // apply saved default shortcuts
338  for (QAction *action : actions) {
339  QVariant savedDefaultShortcut = action->property("_k_DefaultShortcut");
340  if (savedDefaultShortcut.isValid()) {
341  QList<QKeySequence> shortcut = savedDefaultShortcut.value<QList<QKeySequence>>();
342  action->setShortcuts(shortcut);
343  action->setProperty("defaultShortcuts", QVariant::fromValue(shortcut));
344  // qCDebug(DEBUG_KXMLGUI) << "scheme said" << action->shortcut().toString() << "for action" << action->objectName();
345  } else {
346  action->setShortcuts(QList<QKeySequence>());
347  }
348  }
349  }
350 
351  // try to find and apply user-defined shortcuts
352  const QDomElement actionPropElement = findActionPropertiesElement(doc);
353  if (!actionPropElement.isNull()) {
354  applyActionProperties(actionPropElement);
355  }
356 }
357 
358 void KXMLGUIFactoryPrivate::saveDefaultActionProperties(const QList<QAction *> &actions)
359 {
360  // This method is called every time the user activated a new
361  // kxmlguiclient. We only want to execute the following code only once in
362  // the lifetime of an action.
363  for (QAction *action : actions) {
364  // Skip nullptr actions or those we have seen already.
365  if (!action || action->property("_k_DefaultShortcut").isValid()) {
366  continue;
367  }
368 
369  // Check if the default shortcut is set
370  QList<QKeySequence> defaultShortcut = action->property("defaultShortcuts").value<QList<QKeySequence>>();
371  QList<QKeySequence> activeShortcut = action->shortcuts();
372  // qCDebug(DEBUG_KXMLGUI) << action->objectName() << "default=" << defaultShortcut.toString() << "active=" << activeShortcut.toString();
373 
374  // Check if we have an empty default shortcut and an non empty
375  // custom shortcut. Print out a warning and correct the mistake.
376  if ((!activeShortcut.isEmpty()) && defaultShortcut.isEmpty()) {
377  qCCritical(DEBUG_KXMLGUI) << "Shortcut for action " << action->objectName() << action->text()
378  << "set with QAction::setShortcut()! Use KActionCollection::setDefaultShortcut(s) instead.";
379  action->setProperty("_k_DefaultShortcut", QVariant::fromValue(activeShortcut));
380  } else {
381  action->setProperty("_k_DefaultShortcut", QVariant::fromValue(defaultShortcut));
382  }
383  }
384 }
385 
386 void KXMLGUIFactory::changeShortcutScheme(const QString &scheme)
387 {
388  qCDebug(DEBUG_KXMLGUI) << "Changing shortcut scheme to" << scheme;
389  KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes");
390  cg.writeEntry("Current Scheme", scheme);
391 
393 }
394 
395 void KXMLGUIFactory::forgetClient(KXMLGUIClient *client)
396 {
397  d->m_clients.removeAll(client);
398 }
399 
401 {
402  // qCDebug(DEBUG_KXMLGUI) << client;
403 
404  // don't try to remove the client's GUI if we didn't build it
405  if (!client || client->factory() != this) {
406  return;
407  }
408 
409  if (d->emptyState()) {
410  Q_EMIT makingChanges(true);
411  }
412 
413  // remove this client from our client list
414  d->m_clients.removeAll(client);
415 
416  // remove child clients first (create a copy of the list just in case the
417  // original list is modified directly or indirectly in removeClient())
418  const QList<KXMLGUIClient *> childClients(client->childClients());
419  for (KXMLGUIClient *child : childClients) {
420  removeClient(child);
421  }
422 
423  // qCDebug(DEBUG_KXMLGUI) << "calling removeRecursive";
424 
425  d->pushState();
426 
427  // cache some variables
428 
429  d->guiClient = client;
430  d->clientName = client->domDocument().documentElement().attribute(d->attrName);
431  d->clientBuilder = client->clientBuilder();
432 
433  client->setFactory(nullptr);
434 
435  // if we don't have a build document for that client, yet, then create one by
436  // cloning the original document, so that saving container information in the
437  // DOM tree does not touch the original document.
438  QDomDocument doc = client->xmlguiBuildDocument();
439  if (doc.documentElement().isNull()) {
440  doc = client->domDocument().cloneNode(true).toDocument();
441  client->setXMLGUIBuildDocument(doc);
442  }
443 
444  d->m_rootNode->destruct(doc.documentElement(), *d);
445 
446  // reset some variables
447  d->BuildState::reset();
448 
449  // This will destruct the KAccel object built around the given widget.
450  client->prepareXMLUnplug(d->builder->widget());
451 
452  d->popState();
453 
454  if (d->emptyState()) {
455  Q_EMIT makingChanges(false);
456  }
457 
458  Q_EMIT clientRemoved(client);
459 }
460 
462 {
463  return d->m_clients;
464 }
465 
466 QWidget *KXMLGUIFactory::container(const QString &containerName, KXMLGUIClient *client, bool useTagName)
467 {
468  d->pushState();
469  d->m_containerName = containerName;
470  d->guiClient = client;
471 
472  QWidget *result = d->findRecursive(d->m_rootNode, useTagName);
473 
474  d->guiClient = nullptr;
475  d->m_containerName.clear();
476 
477  d->popState();
478 
479  return result;
480 }
481 
482 QList<QWidget *> KXMLGUIFactory::containers(const QString &tagName)
483 {
484  return d->findRecursive(d->m_rootNode, tagName);
485 }
486 
488 {
489  d->m_rootNode->reset();
490 
491  d->m_rootNode->clearChildren();
492 }
493 
494 void KXMLGUIFactory::resetContainer(const QString &containerName, bool useTagName)
495 {
496  if (containerName.isEmpty()) {
497  return;
498  }
499 
500  ContainerNode *container = d->m_rootNode->findContainer(containerName, useTagName);
501  if (container && container->parent) {
502  container->parent->removeChild(container);
503  }
504 }
505 
506 QWidget *KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node, bool tag)
507 {
508  if (((!tag && node->name == m_containerName) || (tag && node->tagName == m_containerName)) //
509  && (!guiClient || node->client == guiClient)) {
510  return node->container;
511  }
512 
513  for (ContainerNode *child : qAsConst(node->children)) {
514  QWidget *cont = findRecursive(child, tag);
515  if (cont) {
516  return cont;
517  }
518  }
519 
520  return nullptr;
521 }
522 
523 // Case insensitive equality without calling toLower which allocates a new string
524 static inline bool equals(const QString &str1, const char *str2)
525 {
526  return str1.compare(QLatin1String(str2), Qt::CaseInsensitive) == 0;
527 }
528 static inline bool equals(const QString &str1, const QString &str2)
529 {
530  return str1.compare(str2, Qt::CaseInsensitive) == 0;
531 }
532 
533 QList<QWidget *> KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node, const QString &tagName)
534 {
535  QList<QWidget *> res;
536 
537  if (equals(node->tagName, tagName)) {
538  res.append(node->container);
539  }
540 
541  for (KXMLGUI::ContainerNode *child : qAsConst(node->children)) {
542  res << findRecursive(child, tagName);
543  }
544 
545  return res;
546 }
547 
548 void KXMLGUIFactory::plugActionList(KXMLGUIClient *client, const QString &name, const QList<QAction *> &actionList)
549 {
550  d->pushState();
551  d->guiClient = client;
552  d->actionListName = name;
553  d->actionList = actionList;
554  d->clientName = client->domDocument().documentElement().attribute(d->attrName);
555 
556  d->m_rootNode->plugActionList(*d);
557 
558  // Load shortcuts for these new actions
559  d->saveDefaultActionProperties(actionList);
560  d->refreshActionProperties(client, actionList, client->domDocument());
561 
562  d->BuildState::reset();
563  d->popState();
564 }
565 
566 void KXMLGUIFactory::unplugActionList(KXMLGUIClient *client, const QString &name)
567 {
568  d->pushState();
569  d->guiClient = client;
570  d->actionListName = name;
571  d->clientName = client->domDocument().documentElement().attribute(d->attrName);
572 
573  d->m_rootNode->unplugActionList(*d);
574 
575  d->BuildState::reset();
576  d->popState();
577 }
578 
579 void KXMLGUIFactoryPrivate::applyActionProperties(const QDomElement &actionPropElement, ShortcutOption shortcutOption)
580 {
581  for (QDomElement e = actionPropElement.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
582  if (!equals(e.tagName(), "action")) {
583  continue;
584  }
585 
586  QAction *action = guiClient->action(e);
587  if (!action) {
588  continue;
589  }
590 
591  configureAction(action, e.attributes(), shortcutOption);
592  }
593 }
594 
595 void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomNamedNodeMap &attributes, ShortcutOption shortcutOption)
596 {
597  for (int i = 0; i < attributes.length(); i++) {
598  QDomAttr attr = attributes.item(i).toAttr();
599  if (attr.isNull()) {
600  continue;
601  }
602 
603  configureAction(action, attr, shortcutOption);
604  }
605 }
606 
607 void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomAttr &attribute, ShortcutOption shortcutOption)
608 {
609  QString attrName = attribute.name();
610  // If the attribute is a deprecated "accel", change to "shortcut".
611  if (equals(attrName, "accel")) {
612  attrName = QStringLiteral("shortcut");
613  }
614 
615  // No need to re-set name, particularly since it's "objectName" in Qt4
616  if (equals(attrName, "name")) {
617  return;
618  }
619 
620  if (equals(attrName, "icon")) {
621  action->setIcon(QIcon::fromTheme(attribute.value()));
622  return;
623  }
624 
625  QVariant propertyValue;
626 
627  QVariant::Type propertyType = action->property(attrName.toLatin1().constData()).type();
628  bool isShortcut = (propertyType == QVariant::KeySequence);
629 
630  if (propertyType == QVariant::Int) {
631  propertyValue = QVariant(attribute.value().toInt());
632  } else if (propertyType == QVariant::UInt) {
633  propertyValue = QVariant(attribute.value().toUInt());
634  } else if (isShortcut) {
635  // Setting the shortcut by property also sets the default shortcut (which is incorrect), so we have to do it directly
636  if (attrName == QLatin1String("globalShortcut")) {
637 #if HAVE_GLOBALACCEL
639 #endif
640  } else {
641  action->setShortcuts(QKeySequence::listFromString(attribute.value()));
642  }
643  if (shortcutOption & KXMLGUIFactoryPrivate::SetDefaultShortcut) {
644  action->setProperty("defaultShortcuts", QVariant::fromValue(QKeySequence::listFromString(attribute.value())));
645  }
646  } else {
647  propertyValue = QVariant(attribute.value());
648  }
649  if (!isShortcut && !action->setProperty(attrName.toLatin1().constData(), propertyValue)) {
650  qCWarning(DEBUG_KXMLGUI) << "Error: Unknown action property " << attrName << " will be ignored!";
651  }
652 }
653 
654 void KXMLGUIFactoryPrivate::applyShortcutScheme(const QString &schemeName, KXMLGUIClient *client, const QList<QAction *> &actions)
655 {
656  // First clear all existing shortcuts
657  for (QAction *action : actions) {
659  // We clear the default shortcut as well because the shortcut scheme will set its own defaults
660  action->setProperty("defaultShortcuts", QVariant::fromValue(QList<QKeySequence>()));
661  }
662 
663  // Find the document for the shortcut scheme using the current application path.
664  // This allows to install a single XML file for a shortcut scheme for kdevelop
665  // rather than 10.
666  // Also look for the current xmlguiclient path.
667  // Per component xml files make sense for making kmail shortcuts available in kontact.
668  QString schemeFileName = KShortcutSchemesHelper::shortcutSchemeFileName(client->componentName(), schemeName);
669  if (schemeFileName.isEmpty()) {
670  schemeFileName = KShortcutSchemesHelper::applicationShortcutSchemeFileName(schemeName);
671  }
672  if (schemeFileName.isEmpty()) {
673  qCWarning(DEBUG_KXMLGUI) << client->componentName() << ": shortcut scheme file not found:" << schemeName << "after trying"
674  << QCoreApplication::applicationName() << "and" << client->componentName();
675  return;
676  }
677 
678  QDomDocument scheme;
679  QFile schemeFile(schemeFileName);
680  if (schemeFile.open(QIODevice::ReadOnly)) {
681  qCDebug(DEBUG_KXMLGUI) << client->componentName() << ": found shortcut scheme XML" << schemeFileName;
682  scheme.setContent(&schemeFile);
683  }
684 
685  if (scheme.isNull()) {
686  return;
687  }
688 
689  QDomElement docElement = scheme.documentElement();
690  QDomElement actionPropElement = docElement.namedItem(QStringLiteral("ActionProperties")).toElement();
691 
692  // Check if we really have the shortcut configuration here
693  if (!actionPropElement.isNull()) {
694  // qCDebug(DEBUG_KXMLGUI) << "Applying shortcut scheme for XMLGUI client" << client->componentName();
695 
696  // Apply all shortcuts we have
697  applyActionProperties(actionPropElement, KXMLGUIFactoryPrivate::SetDefaultShortcut);
698  //} else {
699  // qCDebug(DEBUG_KXMLGUI) << "Invalid shortcut scheme file";
700  }
701 }
702 
703 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 84)
704 int KXMLGUIFactory::configureShortcuts(bool letterCutsOk, bool bSaveSettings)
705 {
708  qobject_cast<QWidget *>(parent()));
709  for (KXMLGUIClient *client : qAsConst(d->m_clients)) {
710  if (client) {
711  qCDebug(DEBUG_KXMLGUI) << "Adding collection from client" << client->componentName() << "with" << client->actionCollection()->count() << "actions";
712  dlg->addCollection(client->actionCollection(), client->componentName());
713  }
714  }
716  return dlg->configure(bSaveSettings);
717 }
718 #endif
719 
721 {
722  auto *dlg = new KShortcutsDialog(qobject_cast<QWidget *>(parent()));
723  dlg->setAttribute(Qt::WA_DeleteOnClose);
724 
725  for (KXMLGUIClient *client : qAsConst(d->m_clients)) {
726  if (client) {
727  qCDebug(DEBUG_KXMLGUI) << "Adding collection from client" << client->componentName() << "with" << client->actionCollection()->count() << "actions";
728 
729  dlg->addCollection(client->actionCollection(), client->componentName());
730  }
731  }
732 
734  dlg->configure(true /*save settings on accept*/);
735 }
736 
737 // Find or create
739 {
740  // first, lets see if we have existing properties
741  QDomElement elem = findActionPropertiesElement(doc);
742 
743  // if there was none, create one
744  if (elem.isNull()) {
745  elem = doc.createElement(QStringLiteral("ActionProperties"));
746  elem.setAttribute(QStringLiteral("scheme"), currentShortcutScheme());
747  doc.documentElement().appendChild(elem);
748  }
749  return elem;
750 }
751 
753 {
754  const QLatin1String attrName("name");
755  for (QDomNode it = elem.firstChild(); !it.isNull(); it = it.nextSibling()) {
756  QDomElement e = it.toElement();
757  if (e.attribute(attrName) == sName) {
758  return e;
759  }
760  }
761 
762  if (create) {
763  QDomElement act_elem = elem.ownerDocument().createElement(QStringLiteral("Action"));
764  act_elem.setAttribute(attrName, sName);
765  elem.appendChild(act_elem);
766  return act_elem;
767  }
768  return QDomElement();
769 }
void setCodec(QTextCodec *codec)
static QString readConfigFile(const QString &filename, const QString &componentName=QString())
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
QString name() const const
void showConfigureShortcutsDialog()
Shows a dialog (KShortcutsDialog) that lists every action in this factory, and which can be used to c...
QDomNode appendChild(const QDomNode &newChild)
QList< QKeySequence > listFromString(const QString &str, QKeySequence::SequenceFormat format)
QString attribute(const QString &name, const QString &defValue) const const
void removeClient(KXMLGUIClient *client)
Removes the GUI described by the client, by unplugging all provided actions and removing all owned co...
void addClient(KXMLGUIClient *client)
Creates the GUI described by the QDomDocument of the client, using the client&#39;s actions, and merges it with the previously created GUI.
void setIcon(const QIcon &icon)
const QObjectList & children() const const
QDomElement nextSiblingElement(const QString &tagName) const const
T value() const const
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
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
bool exists() const const
void reset()
Use this method to free all memory allocated by the KXMLGUIFactory.
int count() const
Returns the number of actions in the collection.
T value(int i) const const
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
QList< KXMLGUIClient * > childClients()
Retrieves a list of all child clients.
void append(const T &value)
void setShortcuts(const QList< QKeySequence > &shortcuts)
QString fromUtf8(const char *str, int size)
void shortcutsSaved()
Emitted when the shortcuts have been saved (i.e.
QVariant property(const char *name) const const
QDomDocument ownerDocument() const const
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
bool isEmpty() const const
QList< QAction * > actions() const
Returns the list of QActions which belong to this action collection.
const char * constData() const const
QByteArray readAll()
void makingChanges(bool)
Emitted when the factory is currently making changes to the GUI, i.e.
void resetContainer(const QString &containerName, bool useTagName=false)
Use this method to free all memory allocated by the KXMLGUIFactory for a specific container...
virtual bool open(QIODevice::OpenMode mode) override
QDomNode namedItem(const QString &name) const const
bool isRelativePath(const QString &path)
QVariant fromValue(const T &value)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QString value() const const
bool isNull() const const
Letter shortcuts are allowed.
static bool saveConfigFile(const QDomDocument &doc, const QString &filename, const QString &componentName=QString())
WA_DeleteOnClose
static KGlobalAccel * self()
QDomNode firstChild() const const
QByteArray toLatin1() const const
void saved()
Emitted after the dialog is accepted (by clicking the OK button) and settings are saved...
QDomNode cloneNode(bool deep) const const
int length() const const
virtual void close() override
QDomElement firstChildElement(const QString &tagName) const const
QTextCodec * codecForName(const QByteArray &name)
void refreshActionProperties()
Use this method to reset and reread action properties (shortcuts, etc.) for all actions.
QDomAttr toAttr() const const
bool isValid() const const
virtual QDomDocument domDocument() const
QWidget * container(const QString &containerName, KXMLGUIClient *client, bool useTagName=false)
Use this method to get access to a container widget with the name specified with containerName and wh...
QIcon fromTheme(const QString &name)
bool setProperty(const char *name, const QVariant &value)
QString tagName() const const
QDomElement createElement(const QString &tagName)
QList< KXMLGUIClient * > clients() const
Returns a list of all clients currently added to this factory.
int configureShortcuts(bool bAllowLetterShortcuts=true, bool bSaveSettings=true)
Shows a dialog (KShortcutsDialog) that lists every action in this factory, and which can be used to c...
QDomNode item(int index) const const
QString absolutePath() const const
Shortcuts without a modifier are not allowed, so &#39;A&#39; would not be valid, whereas &#39;Ctrl+A&#39; would be...
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
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...
T readEntry(const QString &key, const T &aDefault) const
bool setShortcut(QAction *action, const QList< QKeySequence > &shortcut, GlobalShortcutLoading loadFlag=Autoloading)
~KXMLGUIFactory()
Destructor.
KXMLGUIFactory(KXMLGUIBuilder *builder, QObject *parent=nullptr)
Constructs a KXMLGUIFactory.
static QDomElement actionPropertiesElement(QDomDocument &doc)
QDomDocument toDocument() const const
Dialog for configuration of KActionCollection and KGlobalAccel.
Q_EMITQ_EMIT
static QDomElement findActionByName(QDomElement &elem, const QString &sName, bool create)
QString arg(Args &&...args) const const
bool mkpath(const QString &dirPath) const const
QString applicationName()
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)
uint toUInt(bool *ok, int base) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Aug 3 2021 22:51:11 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.