File indexing completed on 2025-04-20 03:43:58
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 1999, 2000 Simon Hausmann <hausmann@kde.org> 0004 SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "config-xmlgui.h" 0010 0011 #include "kxmlguifactory.h" 0012 0013 #include "debug.h" 0014 #include "kactioncollection.h" 0015 #include "kshortcutschemeshelper_p.h" 0016 #include "kshortcutsdialog.h" 0017 #include "kxmlguibuilder.h" 0018 #include "kxmlguiclient.h" 0019 #include "kxmlguifactory_p.h" 0020 0021 #include <QAction> 0022 #include <QCoreApplication> 0023 #include <QDir> 0024 #include <QDomDocument> 0025 #include <QFile> 0026 #include <QStandardPaths> 0027 #include <QTextStream> 0028 #include <QVariant> 0029 #include <QWidget> 0030 0031 #include <KConfigGroup> 0032 #include <KSharedConfig> 0033 #if HAVE_GLOBALACCEL 0034 #include <KGlobalAccel> 0035 #endif 0036 0037 using namespace KXMLGUI; 0038 0039 class KXMLGUIFactoryPrivate : public BuildState 0040 { 0041 public: 0042 enum ShortcutOption { SetActiveShortcut = 1, SetDefaultShortcut = 2 }; 0043 0044 KXMLGUIFactoryPrivate() 0045 { 0046 m_rootNode = new ContainerNode(nullptr, QString(), QString()); 0047 attrName = QStringLiteral("name"); 0048 } 0049 ~KXMLGUIFactoryPrivate() 0050 { 0051 delete m_rootNode; 0052 } 0053 0054 void pushState() 0055 { 0056 m_stateStack.push(*this); 0057 } 0058 0059 void popState() 0060 { 0061 BuildState::operator=(m_stateStack.pop()); 0062 } 0063 0064 bool emptyState() const 0065 { 0066 return m_stateStack.isEmpty(); 0067 } 0068 0069 QWidget *findRecursive(KXMLGUI::ContainerNode *node, bool tag); 0070 QList<QWidget *> findRecursive(KXMLGUI::ContainerNode *node, const QString &tagName); 0071 void applyActionProperties(const QDomElement &element, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut); 0072 void configureAction(QAction *action, const QDomNamedNodeMap &attributes, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut); 0073 void configureAction(QAction *action, const QDomAttr &attribute, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut); 0074 0075 void applyShortcutScheme(const QString &schemeName, KXMLGUIClient *client, const QList<QAction *> &actions); 0076 void refreshActionProperties(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &doc); 0077 void saveDefaultActionProperties(const QList<QAction *> &actions); 0078 0079 ContainerNode *m_rootNode; 0080 0081 /* 0082 * Contains the container which is searched for in ::container . 0083 */ 0084 QString m_containerName; 0085 0086 /* 0087 * List of all clients 0088 */ 0089 QList<KXMLGUIClient *> m_clients; 0090 0091 QString attrName; 0092 0093 BuildStateStack m_stateStack; 0094 }; 0095 0096 QString KXMLGUIFactory::readConfigFile(const QString &filename, const QString &_componentName) 0097 { 0098 QString componentName = _componentName.isEmpty() ? QCoreApplication::applicationName() : _componentName; 0099 QString xml_file; 0100 0101 if (!QDir::isRelativePath(filename)) { 0102 xml_file = filename; 0103 } else { 0104 // first look for any custom user config, admin config or the default deployed as file 0105 xml_file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kxmlgui5/") + componentName + QLatin1Char('/') + filename); 0106 if (!QFile::exists(xml_file)) { 0107 // fall-back to any built-in resource file 0108 xml_file = QLatin1String(":/kxmlgui5/") + componentName + QLatin1Char('/') + filename; 0109 } 0110 } 0111 0112 QFile file(xml_file); 0113 if (xml_file.isEmpty() || !file.open(QIODevice::ReadOnly)) { 0114 qCCritical(DEBUG_KXMLGUI) << "No such XML file" << filename; 0115 return QString(); 0116 } 0117 0118 QByteArray buffer(file.readAll()); 0119 return QString::fromUtf8(buffer.constData(), buffer.size()); 0120 } 0121 0122 bool KXMLGUIFactory::saveConfigFile(const QDomDocument &doc, const QString &filename, const QString &_componentName) 0123 { 0124 QString componentName = _componentName.isEmpty() ? QCoreApplication::applicationName() : _componentName; 0125 QString xml_file(filename); 0126 0127 if (QDir::isRelativePath(xml_file)) { 0128 xml_file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kxmlgui5/%1/%2").arg(componentName, filename); 0129 } 0130 0131 QFileInfo fileInfo(xml_file); 0132 QDir().mkpath(fileInfo.absolutePath()); 0133 QFile file(xml_file); 0134 if (xml_file.isEmpty() || !file.open(QIODevice::WriteOnly)) { 0135 qCCritical(DEBUG_KXMLGUI) << "Could not write to" << filename; 0136 return false; 0137 } 0138 0139 // write out our document 0140 QTextStream ts(&file); 0141 ts << doc; 0142 0143 file.close(); 0144 return true; 0145 } 0146 0147 KXMLGUIFactory::KXMLGUIFactory(KXMLGUIBuilder *builder, QObject *parent) 0148 : QObject(parent) 0149 , d(new KXMLGUIFactoryPrivate) 0150 { 0151 Q_INIT_RESOURCE(kxmlgui); 0152 0153 d->builder = builder; 0154 d->guiClient = nullptr; 0155 if (d->builder) { 0156 d->builderContainerTags = d->builder->containerTags(); 0157 d->builderCustomTags = d->builder->customTags(); 0158 } 0159 } 0160 0161 KXMLGUIFactory::~KXMLGUIFactory() 0162 { 0163 for (KXMLGUIClient *client : std::as_const(d->m_clients)) { 0164 client->setFactory(nullptr); 0165 } 0166 } 0167 0168 void KXMLGUIFactory::addClient(KXMLGUIClient *client) 0169 { 0170 // qCDebug(DEBUG_KXMLGUI) << client; 0171 if (client->factory()) { 0172 if (client->factory() == this) { 0173 return; 0174 } else { 0175 client->factory()->removeClient(client); // just in case someone does stupid things ;-) 0176 } 0177 } 0178 0179 if (d->emptyState()) { 0180 Q_EMIT makingChanges(true); 0181 } 0182 d->pushState(); 0183 0184 // QTime dt; dt.start(); 0185 0186 d->guiClient = client; 0187 0188 // add this client to our client list 0189 if (!d->m_clients.contains(client)) { 0190 d->m_clients.append(client); 0191 } 0192 // else 0193 // qCDebug(DEBUG_KXMLGUI) << "XMLGUI client already added " << client; 0194 0195 // Tell the client that plugging in is process and 0196 // let it know what builder widget its mainwindow shortcuts 0197 // should be attached to. 0198 client->beginXMLPlug(d->builder->widget()); 0199 0200 // try to use the build document for building the client's GUI, as the build document 0201 // contains the correct container state information (like toolbar positions, sizes, etc.) . 0202 // if there is non available, then use the "real" document. 0203 QDomDocument doc = client->xmlguiBuildDocument(); 0204 if (doc.documentElement().isNull()) { 0205 doc = client->domDocument(); 0206 } 0207 0208 QDomElement docElement = doc.documentElement(); 0209 0210 d->m_rootNode->index = -1; 0211 0212 // cache some variables 0213 0214 d->clientName = docElement.attribute(d->attrName); 0215 d->clientBuilder = client->clientBuilder(); 0216 0217 if (d->clientBuilder) { 0218 d->clientBuilderContainerTags = d->clientBuilder->containerTags(); 0219 d->clientBuilderCustomTags = d->clientBuilder->customTags(); 0220 } else { 0221 d->clientBuilderContainerTags.clear(); 0222 d->clientBuilderCustomTags.clear(); 0223 } 0224 0225 // load shortcut schemes, user-defined shortcuts and other action properties 0226 d->saveDefaultActionProperties(client->actionCollection()->actions()); 0227 if (!doc.isNull()) { 0228 d->refreshActionProperties(client, client->actionCollection()->actions(), doc); 0229 } 0230 0231 BuildHelper(*d, d->m_rootNode).build(docElement); 0232 0233 // let the client know that we built its GUI. 0234 client->setFactory(this); 0235 0236 // call the finalizeGUI method, to fix up the positions of toolbars for example. 0237 // Note: the client argument is ignored 0238 d->builder->finalizeGUI(d->guiClient); 0239 0240 // reset some variables, for safety 0241 d->BuildState::reset(); 0242 0243 client->endXMLPlug(); 0244 0245 d->popState(); 0246 0247 Q_EMIT clientAdded(client); 0248 0249 // build child clients 0250 const auto children = client->childClients(); 0251 for (KXMLGUIClient *child : children) { 0252 addClient(child); 0253 } 0254 0255 if (d->emptyState()) { 0256 Q_EMIT makingChanges(false); 0257 } 0258 /* 0259 QString unaddedActions; 0260 Q_FOREACH (KActionCollection* ac, KActionCollection::allCollections()) 0261 Q_FOREACH (QAction* action, ac->actions()) 0262 if (action->associatedWidgets().isEmpty()) 0263 unaddedActions += action->objectName() + ' '; 0264 0265 if (!unaddedActions.isEmpty()) 0266 qCWarning(DEBUG_KXMLGUI) << "The following actions are not plugged into the gui (shortcuts will not work): " << unaddedActions; 0267 */ 0268 0269 // qCDebug(DEBUG_KXMLGUI) << "addClient took " << dt.elapsed(); 0270 } 0271 0272 void KXMLGUIFactory::refreshActionProperties() 0273 { 0274 for (KXMLGUIClient *client : std::as_const(d->m_clients)) { 0275 d->guiClient = client; 0276 QDomDocument doc = client->xmlguiBuildDocument(); 0277 if (doc.documentElement().isNull()) { 0278 client->reloadXML(); 0279 doc = client->domDocument(); 0280 } 0281 d->refreshActionProperties(client, client->actionCollection()->actions(), doc); 0282 } 0283 d->guiClient = nullptr; 0284 } 0285 0286 static QString currentShortcutScheme() 0287 { 0288 const KConfigGroup cg = KSharedConfig::openConfig()->group(QStringLiteral("Shortcut Schemes")); 0289 return cg.readEntry("Current Scheme", "Default"); 0290 } 0291 0292 // Find the right ActionProperties element, otherwise return null element 0293 static QDomElement findActionPropertiesElement(const QDomDocument &doc) 0294 { 0295 const QLatin1String tagActionProp("ActionProperties"); 0296 const QString schemeName = currentShortcutScheme(); 0297 QDomElement e = doc.documentElement().firstChildElement(); 0298 for (; !e.isNull(); e = e.nextSiblingElement()) { 0299 if (QString::compare(e.tagName(), tagActionProp, Qt::CaseInsensitive) == 0 0300 && (e.attribute(QStringLiteral("scheme"), QStringLiteral("Default")) == schemeName)) { 0301 return e; 0302 } 0303 } 0304 return QDomElement(); 0305 } 0306 0307 void KXMLGUIFactoryPrivate::refreshActionProperties(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &doc) 0308 { 0309 // try to find and apply shortcuts schemes 0310 const QString schemeName = KShortcutSchemesHelper::currentShortcutSchemeName(); 0311 // qCDebug(DEBUG_KXMLGUI) << client->componentName() << ": applying shortcut scheme" << schemeName; 0312 0313 if (schemeName != QLatin1String("Default")) { 0314 applyShortcutScheme(schemeName, client, actions); 0315 } else { 0316 // apply saved default shortcuts 0317 for (QAction *action : actions) { 0318 QVariant savedDefaultShortcut = action->property("_k_DefaultShortcut"); 0319 if (savedDefaultShortcut.isValid()) { 0320 QList<QKeySequence> shortcut = savedDefaultShortcut.value<QList<QKeySequence>>(); 0321 action->setShortcuts(shortcut); 0322 action->setProperty("defaultShortcuts", QVariant::fromValue(shortcut)); 0323 // qCDebug(DEBUG_KXMLGUI) << "scheme said" << action->shortcut().toString() << "for action" << action->objectName(); 0324 } else { 0325 action->setShortcuts(QList<QKeySequence>()); 0326 } 0327 } 0328 } 0329 0330 // try to find and apply user-defined shortcuts 0331 const QDomElement actionPropElement = findActionPropertiesElement(doc); 0332 if (!actionPropElement.isNull()) { 0333 applyActionProperties(actionPropElement); 0334 } 0335 } 0336 0337 void KXMLGUIFactoryPrivate::saveDefaultActionProperties(const QList<QAction *> &actions) 0338 { 0339 // This method is called every time the user activated a new 0340 // kxmlguiclient. We only want to execute the following code only once in 0341 // the lifetime of an action. 0342 for (QAction *action : actions) { 0343 // Skip nullptr actions or those we have seen already. 0344 if (!action || action->property("_k_DefaultShortcut").isValid()) { 0345 continue; 0346 } 0347 0348 // Check if the default shortcut is set 0349 QList<QKeySequence> defaultShortcut = action->property("defaultShortcuts").value<QList<QKeySequence>>(); 0350 QList<QKeySequence> activeShortcut = action->shortcuts(); 0351 // qCDebug(DEBUG_KXMLGUI) << action->objectName() << "default=" << defaultShortcut.toString() << "active=" << activeShortcut.toString(); 0352 0353 // Check if we have an empty default shortcut and an non empty 0354 // custom shortcut. Print out a warning and correct the mistake. 0355 if ((!activeShortcut.isEmpty()) && defaultShortcut.isEmpty()) { 0356 qCCritical(DEBUG_KXMLGUI) << "Shortcut for action " << action->objectName() << action->text() 0357 << "set with QAction::setShortcut()! Use KActionCollection::setDefaultShortcut(s) instead."; 0358 action->setProperty("_k_DefaultShortcut", QVariant::fromValue(activeShortcut)); 0359 } else { 0360 action->setProperty("_k_DefaultShortcut", QVariant::fromValue(defaultShortcut)); 0361 } 0362 } 0363 } 0364 0365 void KXMLGUIFactory::changeShortcutScheme(const QString &scheme) 0366 { 0367 qCDebug(DEBUG_KXMLGUI) << "Changing shortcut scheme to" << scheme; 0368 KConfigGroup cg = KSharedConfig::openConfig()->group(QStringLiteral("Shortcut Schemes")); 0369 cg.writeEntry("Current Scheme", scheme); 0370 0371 refreshActionProperties(); 0372 } 0373 0374 void KXMLGUIFactory::forgetClient(KXMLGUIClient *client) 0375 { 0376 d->m_clients.erase(std::remove(d->m_clients.begin(), d->m_clients.end(), client), d->m_clients.end()); 0377 } 0378 0379 void KXMLGUIFactory::removeClient(KXMLGUIClient *client) 0380 { 0381 // qCDebug(DEBUG_KXMLGUI) << client; 0382 0383 // don't try to remove the client's GUI if we didn't build it 0384 if (!client || client->factory() != this) { 0385 return; 0386 } 0387 0388 if (d->emptyState()) { 0389 Q_EMIT makingChanges(true); 0390 } 0391 0392 // remove this client from our client list 0393 forgetClient(client); 0394 0395 // remove child clients first (create a copy of the list just in case the 0396 // original list is modified directly or indirectly in removeClient()) 0397 const QList<KXMLGUIClient *> childClients(client->childClients()); 0398 for (KXMLGUIClient *child : childClients) { 0399 removeClient(child); 0400 } 0401 0402 // qCDebug(DEBUG_KXMLGUI) << "calling removeRecursive"; 0403 0404 d->pushState(); 0405 0406 // cache some variables 0407 0408 d->guiClient = client; 0409 d->clientName = client->domDocument().documentElement().attribute(d->attrName); 0410 d->clientBuilder = client->clientBuilder(); 0411 0412 client->setFactory(nullptr); 0413 0414 // if we don't have a build document for that client, yet, then create one by 0415 // cloning the original document, so that saving container information in the 0416 // DOM tree does not touch the original document. 0417 QDomDocument doc = client->xmlguiBuildDocument(); 0418 if (doc.documentElement().isNull()) { 0419 doc = client->domDocument().cloneNode(true).toDocument(); 0420 client->setXMLGUIBuildDocument(doc); 0421 } 0422 0423 d->m_rootNode->destruct(doc.documentElement(), *d); 0424 0425 // reset some variables 0426 d->BuildState::reset(); 0427 0428 // This will destruct the KAccel object built around the given widget. 0429 client->prepareXMLUnplug(d->builder->widget()); 0430 0431 d->popState(); 0432 0433 if (d->emptyState()) { 0434 Q_EMIT makingChanges(false); 0435 } 0436 0437 Q_EMIT clientRemoved(client); 0438 } 0439 0440 QList<KXMLGUIClient *> KXMLGUIFactory::clients() const 0441 { 0442 return d->m_clients; 0443 } 0444 0445 QWidget *KXMLGUIFactory::container(const QString &containerName, KXMLGUIClient *client, bool useTagName) 0446 { 0447 d->pushState(); 0448 d->m_containerName = containerName; 0449 d->guiClient = client; 0450 0451 QWidget *result = d->findRecursive(d->m_rootNode, useTagName); 0452 0453 d->guiClient = nullptr; 0454 d->m_containerName.clear(); 0455 0456 d->popState(); 0457 0458 return result; 0459 } 0460 0461 QList<QWidget *> KXMLGUIFactory::containers(const QString &tagName) 0462 { 0463 return d->findRecursive(d->m_rootNode, tagName); 0464 } 0465 0466 void KXMLGUIFactory::reset() 0467 { 0468 d->m_rootNode->reset(); 0469 0470 d->m_rootNode->clearChildren(); 0471 } 0472 0473 void KXMLGUIFactory::resetContainer(const QString &containerName, bool useTagName) 0474 { 0475 if (containerName.isEmpty()) { 0476 return; 0477 } 0478 0479 ContainerNode *container = d->m_rootNode->findContainer(containerName, useTagName); 0480 if (container && container->parent) { 0481 container->parent->removeChild(container); 0482 } 0483 } 0484 0485 QWidget *KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node, bool tag) 0486 { 0487 if (((!tag && node->name == m_containerName) || (tag && node->tagName == m_containerName)) // 0488 && (!guiClient || node->client == guiClient)) { 0489 return node->container; 0490 } 0491 0492 for (ContainerNode *child : std::as_const(node->children)) { 0493 QWidget *cont = findRecursive(child, tag); 0494 if (cont) { 0495 return cont; 0496 } 0497 } 0498 0499 return nullptr; 0500 } 0501 0502 // Case insensitive equality without calling toLower which allocates a new string 0503 static inline bool equals(const QString &str1, const char *str2) 0504 { 0505 return str1.compare(QLatin1String(str2), Qt::CaseInsensitive) == 0; 0506 } 0507 static inline bool equals(const QString &str1, const QString &str2) 0508 { 0509 return str1.compare(str2, Qt::CaseInsensitive) == 0; 0510 } 0511 0512 QList<QWidget *> KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node, const QString &tagName) 0513 { 0514 QList<QWidget *> res; 0515 0516 if (equals(node->tagName, tagName)) { 0517 res.append(node->container); 0518 } 0519 0520 for (KXMLGUI::ContainerNode *child : std::as_const(node->children)) { 0521 res << findRecursive(child, tagName); 0522 } 0523 0524 return res; 0525 } 0526 0527 void KXMLGUIFactory::plugActionList(KXMLGUIClient *client, const QString &name, const QList<QAction *> &actionList) 0528 { 0529 d->pushState(); 0530 d->guiClient = client; 0531 d->actionListName = name; 0532 d->actionList = actionList; 0533 d->clientName = client->domDocument().documentElement().attribute(d->attrName); 0534 0535 d->m_rootNode->plugActionList(*d); 0536 0537 // Load shortcuts for these new actions 0538 d->saveDefaultActionProperties(actionList); 0539 d->refreshActionProperties(client, actionList, client->domDocument()); 0540 0541 d->BuildState::reset(); 0542 d->popState(); 0543 } 0544 0545 void KXMLGUIFactory::unplugActionList(KXMLGUIClient *client, const QString &name) 0546 { 0547 d->pushState(); 0548 d->guiClient = client; 0549 d->actionListName = name; 0550 d->clientName = client->domDocument().documentElement().attribute(d->attrName); 0551 0552 d->m_rootNode->unplugActionList(*d); 0553 0554 d->BuildState::reset(); 0555 d->popState(); 0556 } 0557 0558 void KXMLGUIFactoryPrivate::applyActionProperties(const QDomElement &actionPropElement, ShortcutOption shortcutOption) 0559 { 0560 for (QDomElement e = actionPropElement.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { 0561 if (!equals(e.tagName(), "action")) { 0562 continue; 0563 } 0564 0565 QAction *action = guiClient->action(e); 0566 if (!action) { 0567 continue; 0568 } 0569 0570 configureAction(action, e.attributes(), shortcutOption); 0571 } 0572 } 0573 0574 void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomNamedNodeMap &attributes, ShortcutOption shortcutOption) 0575 { 0576 for (int i = 0; i < attributes.length(); i++) { 0577 QDomAttr attr = attributes.item(i).toAttr(); 0578 if (attr.isNull()) { 0579 continue; 0580 } 0581 0582 configureAction(action, attr, shortcutOption); 0583 } 0584 } 0585 0586 void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomAttr &attribute, ShortcutOption shortcutOption) 0587 { 0588 QString attrName = attribute.name(); 0589 // If the attribute is a deprecated "accel", change to "shortcut". 0590 if (equals(attrName, "accel")) { 0591 attrName = QStringLiteral("shortcut"); 0592 } 0593 0594 // No need to re-set name, particularly since it's "objectName" in Qt4 0595 if (equals(attrName, "name")) { 0596 return; 0597 } 0598 0599 if (equals(attrName, "icon")) { 0600 action->setIcon(QIcon::fromTheme(attribute.value())); 0601 return; 0602 } 0603 0604 QVariant propertyValue; 0605 0606 const int propertyType = action->property(attrName.toLatin1().constData()).typeId(); 0607 bool isShortcut = (propertyType == QMetaType::QKeySequence); 0608 0609 if (propertyType == QMetaType::Int) { 0610 propertyValue = QVariant(attribute.value().toInt()); 0611 } else if (propertyType == QMetaType::UInt) { 0612 propertyValue = QVariant(attribute.value().toUInt()); 0613 } else if (isShortcut) { 0614 // Setting the shortcut by property also sets the default shortcut (which is incorrect), so we have to do it directly 0615 if (attrName == QLatin1String("globalShortcut")) { 0616 #if HAVE_GLOBALACCEL 0617 KGlobalAccel::self()->setShortcut(action, QKeySequence::listFromString(attribute.value())); 0618 #endif 0619 } else { 0620 action->setShortcuts(QKeySequence::listFromString(attribute.value())); 0621 } 0622 if (shortcutOption & KXMLGUIFactoryPrivate::SetDefaultShortcut) { 0623 action->setProperty("defaultShortcuts", QVariant::fromValue(QKeySequence::listFromString(attribute.value()))); 0624 } 0625 } else { 0626 propertyValue = QVariant(attribute.value()); 0627 } 0628 if (!isShortcut && !action->setProperty(attrName.toLatin1().constData(), propertyValue)) { 0629 qCWarning(DEBUG_KXMLGUI) << "Error: Unknown action property " << attrName << " will be ignored!"; 0630 } 0631 } 0632 0633 void KXMLGUIFactoryPrivate::applyShortcutScheme(const QString &schemeName, KXMLGUIClient *client, const QList<QAction *> &actions) 0634 { 0635 // First clear all existing shortcuts 0636 for (QAction *action : actions) { 0637 action->setShortcuts(QList<QKeySequence>()); 0638 // We clear the default shortcut as well because the shortcut scheme will set its own defaults 0639 action->setProperty("defaultShortcuts", QVariant::fromValue(QList<QKeySequence>())); 0640 } 0641 0642 // Find the document for the shortcut scheme using the current application path. 0643 // This allows to install a single XML file for a shortcut scheme for kdevelop 0644 // rather than 10. 0645 // Also look for the current xmlguiclient path. 0646 // Per component xml files make sense for making kmail shortcuts available in kontact. 0647 QString schemeFileName = KShortcutSchemesHelper::shortcutSchemeFileName(client->componentName(), schemeName); 0648 if (schemeFileName.isEmpty()) { 0649 schemeFileName = KShortcutSchemesHelper::applicationShortcutSchemeFileName(schemeName); 0650 } 0651 if (schemeFileName.isEmpty()) { 0652 qCWarning(DEBUG_KXMLGUI) << client->componentName() << ": shortcut scheme file not found:" << schemeName << "after trying" 0653 << QCoreApplication::applicationName() << "and" << client->componentName(); 0654 return; 0655 } 0656 0657 QDomDocument scheme; 0658 QFile schemeFile(schemeFileName); 0659 if (schemeFile.open(QIODevice::ReadOnly)) { 0660 qCDebug(DEBUG_KXMLGUI) << client->componentName() << ": found shortcut scheme XML" << schemeFileName; 0661 scheme.setContent(&schemeFile); 0662 } 0663 0664 if (scheme.isNull()) { 0665 return; 0666 } 0667 0668 QDomElement docElement = scheme.documentElement(); 0669 QDomElement actionPropElement = docElement.namedItem(QStringLiteral("ActionProperties")).toElement(); 0670 0671 // Check if we really have the shortcut configuration here 0672 if (!actionPropElement.isNull()) { 0673 // qCDebug(DEBUG_KXMLGUI) << "Applying shortcut scheme for XMLGUI client" << client->componentName(); 0674 0675 // Apply all shortcuts we have 0676 applyActionProperties(actionPropElement, KXMLGUIFactoryPrivate::SetDefaultShortcut); 0677 //} else { 0678 // qCDebug(DEBUG_KXMLGUI) << "Invalid shortcut scheme file"; 0679 } 0680 } 0681 0682 void KXMLGUIFactory::showConfigureShortcutsDialog() 0683 { 0684 auto *dlg = new KShortcutsDialog(qobject_cast<QWidget *>(parent())); 0685 dlg->setAttribute(Qt::WA_DeleteOnClose); 0686 0687 for (KXMLGUIClient *client : std::as_const(d->m_clients)) { 0688 if (client) { 0689 qCDebug(DEBUG_KXMLGUI) << "Adding collection from client" << client->componentName() << "with" << client->actionCollection()->count() << "actions"; 0690 0691 dlg->addCollection(client->actionCollection(), client->componentName()); 0692 } 0693 } 0694 0695 connect(dlg, &KShortcutsDialog::saved, this, &KXMLGUIFactory::shortcutsSaved); 0696 dlg->configure(true /*save settings on accept*/); 0697 } 0698 0699 // Find or create 0700 QDomElement KXMLGUIFactory::actionPropertiesElement(QDomDocument &doc) 0701 { 0702 // first, lets see if we have existing properties 0703 QDomElement elem = findActionPropertiesElement(doc); 0704 0705 // if there was none, create one 0706 if (elem.isNull()) { 0707 elem = doc.createElement(QStringLiteral("ActionProperties")); 0708 elem.setAttribute(QStringLiteral("scheme"), currentShortcutScheme()); 0709 doc.documentElement().appendChild(elem); 0710 } 0711 return elem; 0712 } 0713 0714 QDomElement KXMLGUIFactory::findActionByName(QDomElement &elem, const QString &sName, bool create) 0715 { 0716 const QLatin1String attrName("name"); 0717 for (QDomNode it = elem.firstChild(); !it.isNull(); it = it.nextSibling()) { 0718 QDomElement e = it.toElement(); 0719 if (e.attribute(attrName) == sName) { 0720 return e; 0721 } 0722 } 0723 0724 if (create) { 0725 QDomElement act_elem = elem.ownerDocument().createElement(QStringLiteral("Action")); 0726 act_elem.setAttribute(attrName, sName); 0727 elem.appendChild(act_elem); 0728 return act_elem; 0729 } 0730 return QDomElement(); 0731 } 0732 0733 #include "moc_kxmlguifactory.cpp"