File indexing completed on 2024-04-21 03:54:10

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2001, 2002 Ellis Whitehead <ellis@kde.org>
0004     SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
0005     SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
0006     SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
0007 
0008     SPDX-License-Identifier: LGPL-2.0-or-later
0009 */
0010 
0011 #include "kglobalaccel.h"
0012 #include "kglobalaccel_debug.h"
0013 #include "kglobalaccel_p.h"
0014 
0015 #include <memory>
0016 
0017 #include <QAction>
0018 #include <QDBusMessage>
0019 #include <QDBusMetaType>
0020 #include <QGuiApplication>
0021 #include <QMessageBox>
0022 #include <QPushButton>
0023 #include <config-kglobalaccel.h>
0024 
0025 #if HAVE_X11
0026 #include <private/qtx11extras_p.h>
0027 #endif
0028 
0029 org::kde::kglobalaccel::Component *KGlobalAccelPrivate::getComponent(const QString &componentUnique, bool remember = false)
0030 {
0031     // Check if we already have this component
0032     {
0033         auto component = components.value(componentUnique);
0034         if (component) {
0035             return component;
0036         }
0037     }
0038 
0039     // Get the path for our component. We have to do that because
0040     // componentUnique is probably not a valid dbus object path
0041     QDBusReply<QDBusObjectPath> reply = iface()->getComponent(componentUnique);
0042     if (!reply.isValid()) {
0043         if (reply.error().name() == QLatin1String("org.kde.kglobalaccel.NoSuchComponent")) {
0044             // No problem. The component doesn't exists. That's normal
0045             return nullptr;
0046         }
0047 
0048         // An unknown error.
0049         qCDebug(KGLOBALACCEL_LOG) << "Failed to get dbus path for component " << componentUnique << reply.error();
0050         return nullptr;
0051     }
0052 
0053     // Now get the component
0054     org::kde::kglobalaccel::Component *component =
0055         new org::kde::kglobalaccel::Component(QStringLiteral("org.kde.kglobalaccel"), reply.value().path(), QDBusConnection::sessionBus(), q);
0056 
0057     // No component no cleaning
0058     if (!component->isValid()) {
0059         qCDebug(KGLOBALACCEL_LOG) << "Failed to get component" << componentUnique << QDBusConnection::sessionBus().lastError();
0060         return nullptr;
0061     }
0062 
0063     if (remember) {
0064         // Connect to the signals we are interested in.
0065         q->connect(component,
0066                    &org::kde::kglobalaccel::Component::globalShortcutPressed,
0067                    q,
0068                    [this](const QString &componentUnique, const QString &shortcutUnique, qlonglong timestamp) {
0069                        invokeAction(componentUnique, shortcutUnique, timestamp);
0070                    });
0071 
0072         q->connect(component,
0073                    &org::kde::kglobalaccel::Component::globalShortcutReleased,
0074                    q,
0075                    [this](const QString &componentUnique, const QString &shortcutUnique, qlonglong) {
0076                        invokeDeactivate(componentUnique, shortcutUnique);
0077                    });
0078 
0079         components[componentUnique] = component;
0080     }
0081 
0082     return component;
0083 }
0084 
0085 namespace
0086 {
0087 QString serviceName()
0088 {
0089     return QStringLiteral("org.kde.kglobalaccel");
0090 }
0091 }
0092 
0093 void KGlobalAccelPrivate::cleanup()
0094 {
0095     qDeleteAll(components);
0096     delete m_iface;
0097     m_iface = nullptr;
0098     delete m_watcher;
0099     m_watcher = nullptr;
0100 }
0101 
0102 KGlobalAccelPrivate::KGlobalAccelPrivate(KGlobalAccel *qq)
0103     : q(qq)
0104 {
0105     m_watcher = new QDBusServiceWatcher(serviceName(), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, q);
0106     q->connect(m_watcher, &QDBusServiceWatcher::serviceOwnerChanged, q, [this](const QString &serviceName, const QString &oldOwner, const QString &newOwner) {
0107         serviceOwnerChanged(serviceName, oldOwner, newOwner);
0108     });
0109 }
0110 
0111 org::kde::KGlobalAccel *KGlobalAccelPrivate::iface()
0112 {
0113     if (!m_iface) {
0114         m_iface = new org::kde::KGlobalAccel(serviceName(), QStringLiteral("/kglobalaccel"), QDBusConnection::sessionBus());
0115         // Make sure kglobalaccel is running. The iface declaration above somehow works anyway.
0116         QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface();
0117         if (bus && !bus->isServiceRegistered(serviceName())) {
0118             QDBusReply<void> reply = bus->startService(serviceName());
0119             if (!reply.isValid()) {
0120                 qCritical() << "Couldn't start kglobalaccel from org.kde.kglobalaccel.service:" << reply.error();
0121             }
0122         }
0123 
0124         q->connect(m_iface, &org::kde::KGlobalAccel::yourShortcutsChanged, q, [this](const QStringList &actionId, const QList<QKeySequence> &newKeys) {
0125             shortcutsChanged(actionId, newKeys);
0126         });
0127     }
0128     return m_iface;
0129 }
0130 
0131 KGlobalAccel::KGlobalAccel()
0132     : d(new KGlobalAccelPrivate(this))
0133 {
0134     qDBusRegisterMetaType<QList<int>>();
0135     qDBusRegisterMetaType<QKeySequence>();
0136     qDBusRegisterMetaType<QList<QKeySequence>>();
0137     qDBusRegisterMetaType<QList<QStringList>>();
0138     qDBusRegisterMetaType<KGlobalShortcutInfo>();
0139     qDBusRegisterMetaType<QList<KGlobalShortcutInfo>>();
0140     qDBusRegisterMetaType<KGlobalAccel::MatchType>();
0141 }
0142 
0143 KGlobalAccel::~KGlobalAccel()
0144 {
0145     delete d;
0146 }
0147 
0148 // static
0149 bool KGlobalAccel::cleanComponent(const QString &componentUnique)
0150 {
0151     org::kde::kglobalaccel::Component *component = self()->getComponent(componentUnique);
0152     if (!component) {
0153         return false;
0154     }
0155 
0156     return component->cleanUp();
0157 }
0158 
0159 // static
0160 bool KGlobalAccel::isComponentActive(const QString &componentUnique)
0161 {
0162     org::kde::kglobalaccel::Component *component = self()->getComponent(componentUnique);
0163     if (!component) {
0164         return false;
0165     }
0166 
0167     return component->isActive();
0168 }
0169 
0170 org::kde::kglobalaccel::Component *KGlobalAccel::getComponent(const QString &componentUnique)
0171 {
0172     return d->getComponent(componentUnique);
0173 }
0174 
0175 class KGlobalAccelSingleton
0176 {
0177 public:
0178     KGlobalAccelSingleton();
0179 
0180     KGlobalAccel instance;
0181 };
0182 
0183 Q_GLOBAL_STATIC(KGlobalAccelSingleton, s_instance)
0184 
0185 KGlobalAccelSingleton::KGlobalAccelSingleton()
0186 {
0187     qAddPostRoutine([]() {
0188         s_instance->instance.d->cleanup();
0189     });
0190 }
0191 
0192 KGlobalAccel *KGlobalAccel::self()
0193 {
0194     return &s_instance()->instance;
0195 }
0196 
0197 bool KGlobalAccelPrivate::doRegister(QAction *action)
0198 {
0199     if (!action || action->objectName().isEmpty() || action->objectName().startsWith(QLatin1String("unnamed-"))) {
0200         qWarning() << "Attempt to set global shortcut for action without objectName()."
0201                       " Read the setGlobalShortcut() documentation.";
0202         return false;
0203     }
0204 
0205     const bool isRegistered = actions.contains(action);
0206     if (isRegistered) {
0207         return true;
0208     }
0209 
0210     QStringList actionId = makeActionId(action);
0211 
0212     nameToAction.insert(actionId.at(KGlobalAccel::ActionUnique), action);
0213     actions.insert(action);
0214     iface()->doRegister(actionId);
0215 
0216     QObject::connect(action, &QObject::destroyed, [this, action](QObject *) {
0217         if (actions.contains(action) && (actionShortcuts.contains(action) || actionDefaultShortcuts.contains(action))) {
0218             remove(action, KGlobalAccelPrivate::SetInactive);
0219         }
0220     });
0221 
0222     return true;
0223 }
0224 
0225 void KGlobalAccelPrivate::remove(QAction *action, Removal removal)
0226 {
0227     if (!action || action->objectName().isEmpty()) {
0228         return;
0229     }
0230 
0231     const bool isRegistered = actions.contains(action);
0232     if (!isRegistered) {
0233         return;
0234     }
0235 
0236     QStringList actionId = makeActionId(action);
0237 
0238     nameToAction.remove(actionId.at(KGlobalAccel::ActionUnique), action);
0239     actions.remove(action);
0240 
0241     if (removal == UnRegister) {
0242         // Complete removal of the shortcut is requested
0243         // (forgetGlobalShortcut)
0244         unregister(actionId);
0245     } else {
0246         // If the action is a configurationAction wen only remove it from our
0247         // internal registry. That happened above.
0248 
0249         // If we are merely marking a callback as inactive there is nothing for kglobalaccel to do if kglobalaccel is not running
0250         // this can happen on shutdown where all apps and kglobalaccel are all torn down at once
0251         // For this reason we turn off the autostart flag in the DBus message call
0252 
0253         if (!action->property("isConfigurationAction").toBool()) {
0254             // If it's a session shortcut unregister it.
0255             if (action->objectName().startsWith(QLatin1String("_k_session:"))) {
0256                 unregister(actionId);
0257             } else {
0258                 setInactive(actionId);
0259             }
0260         }
0261     }
0262 
0263     actionDefaultShortcuts.remove(action);
0264     actionShortcuts.remove(action);
0265 }
0266 
0267 void KGlobalAccelPrivate::unregister(const QStringList &actionId)
0268 {
0269     const auto component = actionId.at(KGlobalAccel::ComponentUnique);
0270     const auto action = actionId.at(KGlobalAccel::ActionUnique);
0271 
0272     auto message = QDBusMessage::createMethodCall(iface()->service(), iface()->path(), iface()->interface(), QStringLiteral("unregister"));
0273     message.setArguments({component, action});
0274     message.setAutoStartService(false);
0275     QDBusConnection::sessionBus().call(message);
0276 }
0277 
0278 void KGlobalAccelPrivate::setInactive(const QStringList &actionId)
0279 {
0280     auto message = QDBusMessage::createMethodCall(iface()->service(), iface()->path(), iface()->interface(), QStringLiteral("setInactive"));
0281     message.setArguments({actionId});
0282     message.setAutoStartService(false);
0283     QDBusConnection::sessionBus().call(message);
0284 }
0285 
0286 void KGlobalAccelPrivate::updateGlobalShortcut(/*const would be better*/ QAction *action,
0287                                                ShortcutTypes actionFlags,
0288                                                KGlobalAccel::GlobalShortcutLoading globalFlags)
0289 {
0290     // No action or no objectname -> Do nothing
0291     if (!action || action->objectName().isEmpty()) {
0292         return;
0293     }
0294 
0295     QStringList actionId = makeActionId(action);
0296 
0297     uint setterFlags = 0;
0298     if (globalFlags & NoAutoloading) {
0299         setterFlags |= NoAutoloading;
0300     }
0301 
0302     if (actionFlags & ActiveShortcut) {
0303         const QList<QKeySequence> activeShortcut = actionShortcuts.value(action);
0304         bool isConfigurationAction = action->property("isConfigurationAction").toBool();
0305         uint activeSetterFlags = setterFlags;
0306 
0307         // setPresent tells kglobalaccel that the shortcut is active
0308         if (!isConfigurationAction) {
0309             activeSetterFlags |= SetPresent;
0310         }
0311 
0312         // Sets the shortcut, returns the active/real keys
0313         const auto result = iface()->setShortcutKeys(actionId, activeShortcut, activeSetterFlags);
0314 
0315         // Make sure we get informed about changes in the component by kglobalaccel
0316         getComponent(componentUniqueForAction(action), true);
0317 
0318         // Create a shortcut from the result
0319         const QList<QKeySequence> scResult(result);
0320 
0321         if (isConfigurationAction && (globalFlags & NoAutoloading)) {
0322             // If this is a configuration action and we have set the shortcut,
0323             // inform the real owner of the change.
0324             // Note that setForeignShortcut will cause a signal to be sent to applications
0325             // even if it did not "see" that the shortcut has changed. This is Good because
0326             // at the time of comparison (now) the action *already has* the new shortcut.
0327             // We called setShortcut(), remember?
0328             // Also note that we will see our own signal so we may not need to call
0329             // setActiveGlobalShortcutNoEnable - shortcutGotChanged() does it.
0330             // In practice it's probably better to get the change propagated here without
0331             // DBus delay as we do below.
0332             iface()->setForeignShortcutKeys(actionId, result);
0333         }
0334         if (scResult != activeShortcut) {
0335             // If kglobalaccel returned a shortcut that differs from the one we
0336             // sent, use that one. There must have been clashes or some other problem.
0337             actionShortcuts.insert(action, scResult);
0338             Q_EMIT q->globalShortcutChanged(action, scResult.isEmpty() ? QKeySequence() : scResult.first());
0339         }
0340     }
0341 
0342     if (actionFlags & DefaultShortcut) {
0343         const QList<QKeySequence> defaultShortcut = actionDefaultShortcuts.value(action);
0344         iface()->setShortcutKeys(actionId, defaultShortcut, setterFlags | IsDefault);
0345     }
0346 }
0347 
0348 QStringList KGlobalAccelPrivate::makeActionId(const QAction *action)
0349 {
0350     QStringList ret(componentUniqueForAction(action)); // Component Unique Id ( see actionIdFields )
0351     Q_ASSERT(!ret.at(KGlobalAccel::ComponentUnique).isEmpty());
0352     Q_ASSERT(!action->objectName().isEmpty());
0353     ret.append(action->objectName()); // Action Unique Name
0354     ret.append(componentFriendlyForAction(action)); // Component Friendly name
0355     const QString actionText = action->text().replace(QLatin1Char('&'), QStringLiteral(""));
0356     ret.append(actionText); // Action Friendly Name
0357     return ret;
0358 }
0359 
0360 QList<int> KGlobalAccelPrivate::intListFromShortcut(const QList<QKeySequence> &cut)
0361 {
0362     QList<int> ret;
0363     for (const QKeySequence &sequence : cut) {
0364         ret.append(sequence[0].toCombined());
0365     }
0366     while (!ret.isEmpty() && ret.last() == 0) {
0367         ret.removeLast();
0368     }
0369     return ret;
0370 }
0371 
0372 QList<QKeySequence> KGlobalAccelPrivate::shortcutFromIntList(const QList<int> &list)
0373 {
0374     QList<QKeySequence> ret;
0375     ret.reserve(list.size());
0376     std::transform(list.begin(), list.end(), std::back_inserter(ret), [](int i) {
0377         return QKeySequence(i);
0378     });
0379     return ret;
0380 }
0381 
0382 QString KGlobalAccelPrivate::componentUniqueForAction(const QAction *action)
0383 {
0384     if (!action->property("componentName").isValid()) {
0385         return QCoreApplication::applicationName();
0386     } else {
0387         return action->property("componentName").toString();
0388     }
0389 }
0390 
0391 QString KGlobalAccelPrivate::componentFriendlyForAction(const QAction *action)
0392 {
0393     QString property = action->property("componentDisplayName").toString();
0394     if (!property.isEmpty()) {
0395         return property;
0396     }
0397     if (!QGuiApplication::applicationDisplayName().isEmpty()) {
0398         return QGuiApplication::applicationDisplayName();
0399     }
0400     return QCoreApplication::applicationName();
0401 }
0402 
0403 #if HAVE_X11
0404 int timestampCompare(unsigned long time1_, unsigned long time2_) // like strcmp()
0405 {
0406     quint32 time1 = time1_;
0407     quint32 time2 = time2_;
0408     if (time1 == time2) {
0409         return 0;
0410     }
0411     return quint32(time1 - time2) < 0x7fffffffU ? 1 : -1; // time1 > time2 -> 1, handle wrapping
0412 }
0413 #endif
0414 
0415 QAction *KGlobalAccelPrivate::findAction(const QString &componentUnique, const QString &actionUnique)
0416 {
0417     QAction *action = nullptr;
0418     const QList<QAction *> candidates = nameToAction.values(actionUnique);
0419     for (QAction *const a : candidates) {
0420         if (componentUniqueForAction(a) == componentUnique) {
0421             action = a;
0422         }
0423     }
0424 
0425     // We do not trigger if
0426     // - there is no action
0427     // - the action is not enabled
0428     // - the action is an configuration action
0429     if (!action || !action->isEnabled() || action->property("isConfigurationAction").toBool()) {
0430         return nullptr;
0431     }
0432     return action;
0433 }
0434 
0435 void KGlobalAccelPrivate::invokeAction(const QString &componentUnique, const QString &actionUnique, qlonglong timestamp)
0436 {
0437     QAction *action = findAction(componentUnique, actionUnique);
0438     if (!action) {
0439         return;
0440     }
0441 
0442 #if HAVE_X11
0443     // Update this application's X timestamp if needed.
0444     // TODO The 100%-correct solution should probably be handling this action
0445     // in the proper place in relation to the X events queue in order to avoid
0446     // the possibility of wrong ordering of user events.
0447     if (QX11Info::isPlatformX11()) {
0448         if (timestampCompare(timestamp, QX11Info::appTime()) > 0) {
0449             QX11Info::setAppTime(timestamp);
0450         }
0451         if (timestampCompare(timestamp, QX11Info::appUserTime()) > 0) {
0452             QX11Info::setAppUserTime(timestamp);
0453         }
0454     }
0455 #endif
0456     action->setProperty("org.kde.kglobalaccel.activationTimestamp", timestamp);
0457 
0458     if (m_lastActivatedAction != action) {
0459         Q_EMIT q->globalShortcutActiveChanged(action, true);
0460         m_lastActivatedAction = action;
0461     }
0462     action->trigger();
0463 }
0464 
0465 void KGlobalAccelPrivate::invokeDeactivate(const QString &componentUnique, const QString &actionUnique)
0466 {
0467     QAction *action = findAction(componentUnique, actionUnique);
0468     if (!action) {
0469         return;
0470     }
0471 
0472     m_lastActivatedAction.clear();
0473 
0474     Q_EMIT q->globalShortcutActiveChanged(action, false);
0475 }
0476 
0477 void KGlobalAccelPrivate::shortcutsChanged(const QStringList &actionId, const QList<QKeySequence> &keys)
0478 {
0479     QAction *action = nameToAction.value(actionId.at(KGlobalAccel::ActionUnique));
0480     if (!action) {
0481         return;
0482     }
0483 
0484     actionShortcuts.insert(action, keys);
0485     Q_EMIT q->globalShortcutChanged(action, keys.isEmpty() ? QKeySequence() : keys.first());
0486 }
0487 
0488 void KGlobalAccelPrivate::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
0489 {
0490     Q_UNUSED(oldOwner);
0491     if (name == QLatin1String("org.kde.kglobalaccel") && !newOwner.isEmpty()) {
0492         // kglobalaccel was restarted
0493         qCDebug(KGLOBALACCEL_LOG) << "detected kglobalaccel restarting, re-registering all shortcut keys";
0494         reRegisterAll();
0495     }
0496 }
0497 
0498 void KGlobalAccelPrivate::reRegisterAll()
0499 {
0500     // We clear all our data, assume that all data on the other side is clear too,
0501     // and register each action as if it just was allowed to have global shortcuts.
0502     // If the kded side still has the data it doesn't matter because of the
0503     // autoloading mechanism. The worst case I can imagine is that an action's
0504     // shortcut was changed but the kded side died before it got the message so
0505     // autoloading will now assign an old shortcut to the action. Particularly
0506     // picky apps might assert or misbehave.
0507     const QSet<QAction *> allActions = actions;
0508     nameToAction.clear();
0509     actions.clear();
0510     for (QAction *const action : allActions) {
0511         if (doRegister(action)) {
0512             updateGlobalShortcut(action, ActiveShortcut, KGlobalAccel::Autoloading);
0513         }
0514     }
0515 }
0516 
0517 QList<KGlobalShortcutInfo> KGlobalAccel::globalShortcutsByKey(const QKeySequence &seq, MatchType type)
0518 {
0519     return self()->d->iface()->globalShortcutsByKey(seq, type);
0520 }
0521 
0522 bool KGlobalAccel::isGlobalShortcutAvailable(const QKeySequence &seq, const QString &comp)
0523 {
0524     return self()->d->iface()->globalShortcutAvailable(seq, comp);
0525 }
0526 
0527 // static
0528 bool KGlobalAccel::promptStealShortcutSystemwide(QWidget *parent, const QList<KGlobalShortcutInfo> &shortcuts, const QKeySequence &seq)
0529 {
0530     if (shortcuts.isEmpty()) {
0531         // Usage error. Just say no
0532         return false;
0533     }
0534 
0535     QString component = shortcuts[0].componentFriendlyName();
0536 
0537     QString message;
0538     if (shortcuts.size() == 1) {
0539         message = tr("The '%1' key combination is registered by application %2 for action %3.").arg(seq.toString(), component, shortcuts[0].friendlyName());
0540     } else {
0541         QString actionList;
0542         for (const KGlobalShortcutInfo &info : shortcuts) {
0543             actionList += tr("In context '%1' for action '%2'\n").arg(info.contextFriendlyName(), info.friendlyName());
0544         }
0545         message = tr("The '%1' key combination is registered by application %2.\n%3").arg(seq.toString(), component, actionList);
0546     }
0547 
0548     QString title = tr("Conflict With Registered Global Shortcut");
0549 
0550     QMessageBox box(parent);
0551     box.setWindowTitle(title);
0552     box.setText(message);
0553     box.addButton(QMessageBox::Ok)->setText(tr("Reassign"));
0554     box.addButton(QMessageBox::Cancel);
0555 
0556     return box.exec() == QMessageBox::Ok;
0557 }
0558 
0559 // static
0560 void KGlobalAccel::stealShortcutSystemwide(const QKeySequence &seq)
0561 {
0562     // get the shortcut, remove seq, and set the new shortcut
0563     const QStringList actionId = self()->d->iface()->actionList(seq);
0564     if (actionId.size() < 4) { // not a global shortcut
0565         return;
0566     }
0567     QList<QKeySequence> sc = self()->d->iface()->shortcutKeys(actionId);
0568 
0569     for (int i = 0; i < sc.count(); i++) {
0570         if (sc[i] == seq) {
0571             sc[i] = QKeySequence();
0572         }
0573     }
0574 
0575     self()->d->iface()->setForeignShortcutKeys(actionId, sc);
0576 }
0577 
0578 bool checkGarbageKeycode(const QList<QKeySequence> &shortcut)
0579 {
0580     // protect against garbage keycode -1 that Qt sometimes produces for exotic keys;
0581     // at the moment (~mid 2008) Multimedia PlayPause is one of those keys.
0582     for (const QKeySequence &sequence : shortcut) {
0583         for (int i = 0; i < 4; i++) {
0584             if (sequence[i].toCombined() == -1) {
0585                 qWarning() << "Encountered garbage keycode (keycode = -1) in input, not doing anything.";
0586                 return true;
0587             }
0588         }
0589     }
0590     return false;
0591 }
0592 
0593 bool KGlobalAccel::setDefaultShortcut(QAction *action, const QList<QKeySequence> &shortcut, GlobalShortcutLoading loadFlag)
0594 {
0595     if (checkGarbageKeycode(shortcut)) {
0596         return false;
0597     }
0598 
0599     if (!d->doRegister(action)) {
0600         return false;
0601     }
0602 
0603     d->actionDefaultShortcuts.insert(action, shortcut);
0604     d->updateGlobalShortcut(action, KGlobalAccelPrivate::DefaultShortcut, loadFlag);
0605     return true;
0606 }
0607 
0608 bool KGlobalAccel::setShortcut(QAction *action, const QList<QKeySequence> &shortcut, GlobalShortcutLoading loadFlag)
0609 {
0610     if (checkGarbageKeycode(shortcut)) {
0611         return false;
0612     }
0613 
0614     if (!d->doRegister(action)) {
0615         return false;
0616     }
0617 
0618     d->actionShortcuts.insert(action, shortcut);
0619     d->updateGlobalShortcut(action, KGlobalAccelPrivate::ActiveShortcut, loadFlag);
0620     return true;
0621 }
0622 
0623 QList<QKeySequence> KGlobalAccel::defaultShortcut(const QAction *action) const
0624 {
0625     return d->actionDefaultShortcuts.value(action);
0626 }
0627 
0628 QList<QKeySequence> KGlobalAccel::shortcut(const QAction *action) const
0629 {
0630     return d->actionShortcuts.value(action);
0631 }
0632 
0633 QList<QKeySequence> KGlobalAccel::globalShortcut(const QString &componentName, const QString &actionId) const
0634 {
0635     // see also d->updateGlobalShortcut(action, KGlobalAccelPrivate::ActiveShortcut, KGlobalAccel::Autoloading);
0636 
0637     // how componentName and actionId map to QAction, e.g:
0638     // action->setProperty("componentName", "kwin");
0639     // action->setObjectName("Kill Window");
0640 
0641     const QList<QKeySequence> scResult = self()->d->iface()->shortcutKeys({componentName, actionId, QString(), QString()});
0642     return scResult;
0643 }
0644 
0645 void KGlobalAccel::removeAllShortcuts(QAction *action)
0646 {
0647     d->remove(action, KGlobalAccelPrivate::UnRegister);
0648 }
0649 
0650 bool KGlobalAccel::hasShortcut(const QAction *action) const
0651 {
0652     return d->actionShortcuts.contains(action) || d->actionDefaultShortcuts.contains(action);
0653 }
0654 
0655 bool KGlobalAccel::setGlobalShortcut(QAction *action, const QList<QKeySequence> &shortcut)
0656 {
0657     KGlobalAccel *g = KGlobalAccel::self();
0658     return g->d->setShortcutWithDefault(action, shortcut, Autoloading);
0659 }
0660 
0661 bool KGlobalAccel::setGlobalShortcut(QAction *action, const QKeySequence &shortcut)
0662 {
0663     return KGlobalAccel::setGlobalShortcut(action, QList<QKeySequence>() << shortcut);
0664 }
0665 
0666 bool KGlobalAccelPrivate::setShortcutWithDefault(QAction *action, const QList<QKeySequence> &shortcut, KGlobalAccel::GlobalShortcutLoading loadFlag)
0667 {
0668     if (checkGarbageKeycode(shortcut)) {
0669         return false;
0670     }
0671 
0672     if (!doRegister(action)) {
0673         return false;
0674     }
0675 
0676     actionDefaultShortcuts.insert(action, shortcut);
0677     actionShortcuts.insert(action, shortcut);
0678     updateGlobalShortcut(action, KGlobalAccelPrivate::DefaultShortcut | KGlobalAccelPrivate::ActiveShortcut, loadFlag);
0679     return true;
0680 }
0681 
0682 QDBusArgument &operator<<(QDBusArgument &argument, const KGlobalAccel::MatchType &type)
0683 {
0684     argument.beginStructure();
0685     argument << static_cast<int>(type);
0686     argument.endStructure();
0687     return argument;
0688 }
0689 
0690 const QDBusArgument &operator>>(const QDBusArgument &argument, KGlobalAccel::MatchType &type)
0691 {
0692     argument.beginStructure();
0693     int arg;
0694     argument >> arg;
0695     type = static_cast<KGlobalAccel::MatchType>(arg);
0696     argument.endStructure();
0697     return argument;
0698 }
0699 
0700 #include "moc_kglobalaccel.cpp"