File indexing completed on 2024-12-08 03:39:24
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"