1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2001, 2002 Ellis Whitehead <ellis@kde.org>
4 SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
5 SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
6 SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
7
8 SPDX-License-Identifier: LGPL-2.0-or-later
9*/
10
11#include "kglobalaccel.h"
12#include "kglobalaccel_debug.h"
13#include "kglobalaccel_p.h"
14
15#include <memory>
16
17#include <QAction>
18#include <QDBusMessage>
19#include <QDBusMetaType>
20#include <QGuiApplication>
21#include <QMessageBox>
22#include <QPushButton>
23#include <config-kglobalaccel.h>
24
25#if WITH_X11
26#include <private/qtx11extras_p.h>
27#endif
28
29org::kde::kglobalaccel::Component *KGlobalAccelPrivate::getComponent(const QString &componentUnique, bool remember = false)
30{
31 // Check if we already have this component
32 {
33 auto component = components.value(key: componentUnique);
34 if (component) {
35 return component;
36 }
37 }
38
39 // Get the path for our component. We have to do that because
40 // componentUnique is probably not a valid dbus object path
41 QDBusReply<QDBusObjectPath> reply = iface()->getComponent(componentUnique);
42 if (!reply.isValid()) {
43 if (reply.error().name() == QLatin1String("org.kde.kglobalaccel.NoSuchComponent")) {
44 // No problem. The component doesn't exists. That's normal
45 return nullptr;
46 }
47
48 // An unknown error.
49 qCDebug(KGLOBALACCEL_LOG) << "Failed to get dbus path for component " << componentUnique << reply.error();
50 return nullptr;
51 }
52
53 // Now get the component
54 org::kde::kglobalaccel::Component *component =
55 new org::kde::kglobalaccel::Component(QStringLiteral("org.kde.kglobalaccel"), reply.value().path(), QDBusConnection::sessionBus(), q);
56
57 // No component no cleaning
58 if (!component->isValid()) {
59 qCDebug(KGLOBALACCEL_LOG) << "Failed to get component" << componentUnique << QDBusConnection::sessionBus().lastError();
60 return nullptr;
61 }
62
63 if (remember) {
64 // Connect to the signals we are interested in.
65 QObject::connect(sender: component,
66 signal: &org::kde::kglobalaccel::Component::globalShortcutPressed,
67 context: q,
68 slot: [this](const QString &componentUnique, const QString &shortcutUnique, qlonglong timestamp) {
69 invokeAction(componentUnique, shortcutUnique, timestamp, wasHeld: ShortcutState::Pressed);
70 });
71
72 QObject::connect(sender: component,
73 signal: &org::kde::kglobalaccel::Component::globalShortcutRepeated,
74 context: q,
75 slot: [this](const QString &componentUnique, const QString &shortcutUnique, qlonglong timestamp) {
76 invokeAction(componentUnique, shortcutUnique, timestamp, wasHeld: ShortcutState::Repeated);
77 });
78
79 QObject::connect(sender: component,
80 signal: &org::kde::kglobalaccel::Component::globalShortcutReleased,
81 context: q,
82 slot: [this](const QString &componentUnique, const QString &shortcutUnique, qlonglong) {
83 invokeDeactivate(componentUnique, shortcutUnique);
84 });
85
86 components[componentUnique] = component;
87 }
88
89 return component;
90}
91
92namespace
93{
94QString serviceName()
95{
96 return QStringLiteral("org.kde.kglobalaccel");
97}
98}
99
100void KGlobalAccelPrivate::cleanup()
101{
102 qDeleteAll(c: components);
103 delete m_iface;
104 m_iface = nullptr;
105 delete m_watcher;
106 m_watcher = nullptr;
107}
108
109KGlobalAccelPrivate::KGlobalAccelPrivate(KGlobalAccel *qq)
110 : q(qq)
111{
112 m_watcher = new QDBusServiceWatcher(serviceName(), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, q);
113 QObject::connect(sender: m_watcher,
114 signal: &QDBusServiceWatcher::serviceOwnerChanged,
115 context: q,
116 slot: [this](const QString &serviceName, const QString &oldOwner, const QString &newOwner) {
117 serviceOwnerChanged(name: serviceName, oldOwner, newOwner);
118 });
119}
120
121org::kde::KGlobalAccel *KGlobalAccelPrivate::iface()
122{
123 if (!m_iface) {
124 m_iface = new org::kde::KGlobalAccel(serviceName(), QStringLiteral("/kglobalaccel"), QDBusConnection::sessionBus());
125 // Make sure kglobalaccel is running. The iface declaration above somehow works anyway.
126 QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface();
127 if (bus && !bus->isServiceRegistered(serviceName: serviceName())) {
128 QDBusReply<void> reply = bus->startService(name: serviceName());
129 if (!reply.isValid()) {
130 qCritical() << "Couldn't start kglobalaccel from org.kde.kglobalaccel.service:" << reply.error();
131 }
132 }
133
134 QObject::connect(sender: m_iface, signal: &org::kde::KGlobalAccel::yourShortcutsChanged, context: q, slot: [this](const QStringList &actionId, const QList<QKeySequence> &newKeys) {
135 shortcutsChanged(actionId, newKeys);
136 });
137 }
138 return m_iface;
139}
140
141KGlobalAccel::KGlobalAccel()
142 : d(new KGlobalAccelPrivate(this))
143{
144 qDBusRegisterMetaType<QList<int>>();
145 qDBusRegisterMetaType<QKeySequence>();
146 qDBusRegisterMetaType<QList<QKeySequence>>();
147 qDBusRegisterMetaType<QList<QStringList>>();
148 qDBusRegisterMetaType<KGlobalShortcutInfo>();
149 qDBusRegisterMetaType<QList<KGlobalShortcutInfo>>();
150 qDBusRegisterMetaType<KGlobalAccel::MatchType>();
151}
152
153KGlobalAccel::~KGlobalAccel()
154{
155 delete d;
156}
157
158// static
159bool KGlobalAccel::cleanComponent(const QString &componentUnique)
160{
161 org::kde::kglobalaccel::Component *component = self()->getComponent(componentUnique);
162 if (!component) {
163 return false;
164 }
165
166 return component->cleanUp();
167}
168
169// static
170bool KGlobalAccel::isComponentActive(const QString &componentUnique)
171{
172 org::kde::kglobalaccel::Component *component = self()->getComponent(componentUnique);
173 if (!component) {
174 return false;
175 }
176
177 return component->isActive();
178}
179
180org::kde::kglobalaccel::Component *KGlobalAccel::getComponent(const QString &componentUnique)
181{
182 return d->getComponent(componentUnique);
183}
184
185class KGlobalAccelSingleton
186{
187public:
188 KGlobalAccelSingleton();
189
190 KGlobalAccel instance;
191};
192
193Q_GLOBAL_STATIC(KGlobalAccelSingleton, s_instance)
194
195KGlobalAccelSingleton::KGlobalAccelSingleton()
196{
197 qAddPostRoutine([]() {
198 s_instance->instance.d->cleanup();
199 });
200}
201
202KGlobalAccel *KGlobalAccel::self()
203{
204 return &s_instance()->instance;
205}
206
207bool KGlobalAccelPrivate::doRegister(QAction *action)
208{
209 if (!action || action->objectName().isEmpty() || action->objectName().startsWith(s: QLatin1String("unnamed-"))) {
210 qWarning() << "Attempt to set global shortcut for action without objectName()."
211 " Read the setGlobalShortcut() documentation.";
212 return false;
213 }
214
215 const bool isRegistered = actions.contains(value: action);
216 if (isRegistered) {
217 return true;
218 }
219
220 QStringList actionId = makeActionId(action);
221
222 nameToAction.insert(key: actionId.at(i: KGlobalAccel::ActionUnique), value: action);
223 actions.insert(value: action);
224 iface()->doRegister(actionId);
225
226 QObject::connect(sender: action, signal: &QObject::destroyed, context: q, slot: [this, action](QObject *) {
227 if (actions.contains(value: action) && (actionShortcuts.contains(key: action) || actionDefaultShortcuts.contains(key: action))) {
228 remove(action, r: KGlobalAccelPrivate::SetInactive);
229 }
230 });
231
232 return true;
233}
234
235void KGlobalAccelPrivate::remove(QAction *action, Removal removal)
236{
237 if (!action || action->objectName().isEmpty()) {
238 return;
239 }
240
241 const bool isRegistered = actions.contains(value: action);
242 if (!isRegistered) {
243 return;
244 }
245
246 QStringList actionId = makeActionId(action);
247
248 nameToAction.remove(key: actionId.at(i: KGlobalAccel::ActionUnique), value: action);
249 actions.remove(value: action);
250
251 if (removal == UnRegister) {
252 // Complete removal of the shortcut is requested
253 // (forgetGlobalShortcut)
254 unregister(actionId);
255 } else {
256 // If the action is a configurationAction wen only remove it from our
257 // internal registry. That happened above.
258
259 // If we are merely marking a callback as inactive there is nothing for kglobalaccel to do if kglobalaccel is not running
260 // this can happen on shutdown where all apps and kglobalaccel are all torn down at once
261 // For this reason we turn off the autostart flag in the DBus message call
262
263 if (!action->property(name: "isConfigurationAction").toBool()) {
264 // If it's a session shortcut unregister it.
265 if (action->objectName().startsWith(s: QLatin1String("_k_session:"))) {
266 unregister(actionId);
267 } else {
268 setInactive(actionId);
269 }
270 }
271 }
272
273 actionDefaultShortcuts.remove(key: action);
274 actionShortcuts.remove(key: action);
275}
276
277void KGlobalAccelPrivate::unregister(const QStringList &actionId)
278{
279 const auto component = actionId.at(i: KGlobalAccel::ComponentUnique);
280 const auto action = actionId.at(i: KGlobalAccel::ActionUnique);
281
282 auto message = QDBusMessage::createMethodCall(destination: iface()->service(), path: iface()->path(), interface: iface()->interface(), QStringLiteral("unregister"));
283 message.setArguments({component, action});
284 message.setAutoStartService(false);
285 QDBusConnection::sessionBus().call(message);
286}
287
288void KGlobalAccelPrivate::setInactive(const QStringList &actionId)
289{
290 auto message = QDBusMessage::createMethodCall(destination: iface()->service(), path: iface()->path(), interface: iface()->interface(), QStringLiteral("setInactive"));
291 message.setArguments({actionId});
292 message.setAutoStartService(false);
293 QDBusConnection::sessionBus().call(message);
294}
295
296void KGlobalAccelPrivate::updateGlobalShortcut(/*const would be better*/ QAction *action,
297 ShortcutTypes actionFlags,
298 KGlobalAccel::GlobalShortcutLoading globalFlags)
299{
300 // No action or no objectname -> Do nothing
301 if (!action || action->objectName().isEmpty()) {
302 return;
303 }
304
305 QStringList actionId = makeActionId(action);
306
307 uint setterFlags = 0;
308 if (globalFlags & KGlobalAccel::GlobalShortcutLoading::NoAutoloading) {
309 setterFlags |= NoAutoloading;
310 }
311
312 if (actionFlags & ActiveShortcut) {
313 const QList<QKeySequence> activeShortcut = actionShortcuts.value(key: action);
314 bool isConfigurationAction = action->property(name: "isConfigurationAction").toBool();
315 uint activeSetterFlags = setterFlags;
316
317 // setPresent tells kglobalaccel that the shortcut is active
318 if (!isConfigurationAction) {
319 activeSetterFlags |= SetPresent;
320 }
321
322 // Sets the shortcut, returns the active/real keys
323 const auto result = iface()->setShortcutKeys(actionId, keys: activeShortcut, flags: activeSetterFlags);
324
325 // Make sure we get informed about changes in the component by kglobalaccel
326 getComponent(componentUnique: componentUniqueForAction(action), remember: true);
327
328 // Create a shortcut from the result
329 const QList<QKeySequence> scResult(result);
330
331 if (isConfigurationAction && (globalFlags & KGlobalAccel::GlobalShortcutLoading::NoAutoloading)) {
332 // If this is a configuration action and we have set the shortcut,
333 // inform the real owner of the change.
334 // Note that setForeignShortcut will cause a signal to be sent to applications
335 // even if it did not "see" that the shortcut has changed. This is Good because
336 // at the time of comparison (now) the action *already has* the new shortcut.
337 // We called setShortcut(), remember?
338 // Also note that we will see our own signal so we may not need to call
339 // setActiveGlobalShortcutNoEnable - shortcutGotChanged() does it.
340 // In practice it's probably better to get the change propagated here without
341 // DBus delay as we do below.
342 iface()->setForeignShortcutKeys(actionId, keys: result);
343 }
344 if (scResult != activeShortcut) {
345 // If kglobalaccel returned a shortcut that differs from the one we
346 // sent, use that one. There must have been clashes or some other problem.
347 actionShortcuts.insert(key: action, value: scResult);
348 Q_EMIT q->globalShortcutChanged(action, seq: scResult.isEmpty() ? QKeySequence() : scResult.first());
349 }
350 }
351
352 if (actionFlags & DefaultShortcut) {
353 const QList<QKeySequence> defaultShortcut = actionDefaultShortcuts.value(key: action);
354 iface()->setShortcutKeys(actionId, keys: defaultShortcut, flags: setterFlags | IsDefault);
355 }
356}
357
358QStringList KGlobalAccelPrivate::makeActionId(const QAction *action)
359{
360 QStringList ret(componentUniqueForAction(action)); // Component Unique Id ( see actionIdFields )
361 Q_ASSERT(!ret.at(KGlobalAccel::ComponentUnique).isEmpty());
362 Q_ASSERT(!action->objectName().isEmpty());
363 ret.append(t: action->objectName()); // Action Unique Name
364 ret.append(t: componentFriendlyForAction(action)); // Component Friendly name
365 const QString actionText = action->text().replace(c: QLatin1Char('&'), QStringLiteral(""));
366 ret.append(t: actionText); // Action Friendly Name
367 return ret;
368}
369
370QList<int> KGlobalAccelPrivate::intListFromShortcut(const QList<QKeySequence> &cut)
371{
372 QList<int> ret;
373 for (const QKeySequence &sequence : cut) {
374 ret.append(t: sequence[0].toCombined());
375 }
376 while (!ret.isEmpty() && ret.last() == 0) {
377 ret.removeLast();
378 }
379 return ret;
380}
381
382QList<QKeySequence> KGlobalAccelPrivate::shortcutFromIntList(const QList<int> &list)
383{
384 QList<QKeySequence> ret;
385 ret.reserve(asize: list.size());
386 std::transform(first: list.begin(), last: list.end(), result: std::back_inserter(x&: ret), unary_op: [](int i) {
387 return QKeySequence(i);
388 });
389 return ret;
390}
391
392QString KGlobalAccelPrivate::componentUniqueForAction(const QAction *action)
393{
394 if (!action->property(name: "componentName").isValid()) {
395 return QCoreApplication::applicationName();
396 } else {
397 return action->property(name: "componentName").toString();
398 }
399}
400
401QString KGlobalAccelPrivate::componentFriendlyForAction(const QAction *action)
402{
403 QString property = action->property(name: "componentDisplayName").toString();
404 if (!property.isEmpty()) {
405 return property;
406 }
407 if (!QGuiApplication::applicationDisplayName().isEmpty()) {
408 return QGuiApplication::applicationDisplayName();
409 }
410 return QCoreApplication::applicationName();
411}
412
413#if WITH_X11
414int timestampCompare(unsigned long time1_, unsigned long time2_) // like strcmp()
415{
416 quint32 time1 = time1_;
417 quint32 time2 = time2_;
418 if (time1 == time2) {
419 return 0;
420 }
421 return quint32(time1 - time2) < 0x7fffffffU ? 1 : -1; // time1 > time2 -> 1, handle wrapping
422}
423#endif
424
425QAction *KGlobalAccelPrivate::findAction(const QString &componentUnique, const QString &actionUnique)
426{
427 QAction *action = nullptr;
428 const QList<QAction *> candidates = nameToAction.values(key: actionUnique);
429 for (QAction *const a : candidates) {
430 if (componentUniqueForAction(action: a) == componentUnique) {
431 action = a;
432 }
433 }
434
435 // We do not trigger if
436 // - there is no action
437 // - the action is not enabled
438 // - the action is an configuration action
439 if (!action || !action->isEnabled() || action->property(name: "isConfigurationAction").toBool()) {
440 return nullptr;
441 }
442 return action;
443}
444
445void KGlobalAccelPrivate::invokeAction(const QString &componentUnique, const QString &actionUnique, qlonglong timestamp, ShortcutState state)
446{
447 QAction *action = findAction(componentUnique, actionUnique);
448 if (!action) {
449 return;
450 }
451
452#if WITH_X11
453 // Update this application's X timestamp if needed.
454 // TODO The 100%-correct solution should probably be handling this action
455 // in the proper place in relation to the X events queue in order to avoid
456 // the possibility of wrong ordering of user events.
457 if (QX11Info::isPlatformX11()) {
458 if (timestampCompare(time1_: timestamp, time2_: QX11Info::appTime()) > 0) {
459 QX11Info::setAppTime(timestamp);
460 }
461 if (timestampCompare(time1_: timestamp, time2_: QX11Info::appUserTime()) > 0) {
462 QX11Info::setAppUserTime(timestamp);
463 }
464 }
465#endif
466 action->setProperty(name: "org.kde.kglobalaccel.activationTimestamp", value: timestamp);
467
468 if (m_lastActivatedAction != action) {
469 Q_EMIT q->globalShortcutActiveChanged(action, active: true);
470 m_lastActivatedAction = action;
471 }
472 if (!action->autoRepeat() && state == ShortcutState::Repeated) {
473 return;
474 }
475 action->trigger();
476}
477
478void KGlobalAccelPrivate::invokeDeactivate(const QString &componentUnique, const QString &actionUnique)
479{
480 QAction *action = findAction(componentUnique, actionUnique);
481 if (!action) {
482 return;
483 }
484
485 m_lastActivatedAction.clear();
486
487 Q_EMIT q->globalShortcutActiveChanged(action, active: false);
488}
489
490void KGlobalAccelPrivate::shortcutsChanged(const QStringList &actionId, const QList<QKeySequence> &keys)
491{
492 QAction *action = nameToAction.value(key: actionId.at(i: KGlobalAccel::ActionUnique));
493 if (!action) {
494 return;
495 }
496
497 actionShortcuts.insert(key: action, value: keys);
498 Q_EMIT q->globalShortcutChanged(action, seq: keys.isEmpty() ? QKeySequence() : keys.first());
499}
500
501void KGlobalAccelPrivate::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
502{
503 Q_UNUSED(oldOwner);
504 if (name == QLatin1String("org.kde.kglobalaccel") && !newOwner.isEmpty()) {
505 // kglobalaccel was restarted
506 qCDebug(KGLOBALACCEL_LOG) << "detected kglobalaccel restarting, re-registering all shortcut keys";
507 reRegisterAll();
508 }
509}
510
511void KGlobalAccelPrivate::reRegisterAll()
512{
513 // We clear all our data, assume that all data on the other side is clear too,
514 // and register each action as if it just was allowed to have global shortcuts.
515 // If the kded side still has the data it doesn't matter because of the
516 // autoloading mechanism. The worst case I can imagine is that an action's
517 // shortcut was changed but the kded side died before it got the message so
518 // autoloading will now assign an old shortcut to the action. Particularly
519 // picky apps might assert or misbehave.
520 const QSet<QAction *> allActions = actions;
521 nameToAction.clear();
522 actions.clear();
523 for (QAction *const action : allActions) {
524 if (doRegister(action)) {
525 updateGlobalShortcut(action, actionFlags: ActiveShortcut, globalFlags: KGlobalAccel::Autoloading);
526 }
527 }
528}
529
530QList<KGlobalShortcutInfo> KGlobalAccel::globalShortcutsByKey(const QKeySequence &seq, MatchType type)
531{
532 return self()->d->iface()->globalShortcutsByKey(key: seq, matchType: type);
533}
534
535bool KGlobalAccel::isGlobalShortcutAvailable(const QKeySequence &seq, const QString &comp)
536{
537 return self()->d->iface()->globalShortcutAvailable(key: seq, component: comp);
538}
539
540// static
541bool KGlobalAccel::promptStealShortcutSystemwide(QWidget *parent, const QList<KGlobalShortcutInfo> &shortcuts, const QKeySequence &seq)
542{
543 if (shortcuts.isEmpty()) {
544 // Usage error. Just say no
545 return false;
546 }
547
548 QString component = shortcuts[0].componentFriendlyName();
549
550 QString message;
551 if (shortcuts.size() == 1) {
552 message = tr(s: "The '%1' key combination is registered by application %2 for action %3.").arg(args: seq.toString(), args&: component, args: shortcuts[0].friendlyName());
553 } else {
554 QString actionList;
555 for (const KGlobalShortcutInfo &info : shortcuts) {
556 actionList += tr(s: "In context '%1' for action '%2'\n").arg(args: info.contextFriendlyName(), args: info.friendlyName());
557 }
558 message = tr(s: "The '%1' key combination is registered by application %2.\n%3").arg(args: seq.toString(), args&: component, args&: actionList);
559 }
560
561 QString title = tr(s: "Conflict With Registered Global Shortcut");
562
563 QMessageBox box(parent);
564 box.setWindowTitle(title);
565 box.setText(message);
566 box.addButton(button: QMessageBox::Ok)->setText(tr(s: "Reassign"));
567 box.addButton(button: QMessageBox::Cancel);
568
569 return box.exec() == QMessageBox::Ok;
570}
571
572// static
573void KGlobalAccel::stealShortcutSystemwide(const QKeySequence &seq)
574{
575 // get the shortcut, remove seq, and set the new shortcut
576 const QStringList actionId = self()->d->iface()->actionList(key: seq);
577 if (actionId.size() < 4) { // not a global shortcut
578 return;
579 }
580 QList<QKeySequence> sc = self()->d->iface()->shortcutKeys(actionId);
581
582 for (int i = 0; i < sc.count(); i++) {
583 if (sc[i] == seq) {
584 sc[i] = QKeySequence();
585 }
586 }
587
588 self()->d->iface()->setForeignShortcutKeys(actionId, keys: sc);
589}
590
591bool checkGarbageKeycode(const QList<QKeySequence> &shortcut)
592{
593 // protect against garbage keycode -1 that Qt sometimes produces for exotic keys;
594 // at the moment (~mid 2008) Multimedia PlayPause is one of those keys.
595 for (const QKeySequence &sequence : shortcut) {
596 for (int i = 0; i < 4; i++) {
597 if (sequence[i].toCombined() == -1) {
598 qWarning() << "Encountered garbage keycode (keycode = -1) in input, not doing anything.";
599 return true;
600 }
601 }
602 }
603 return false;
604}
605
606bool KGlobalAccel::setDefaultShortcut(QAction *action, const QList<QKeySequence> &shortcut, GlobalShortcutLoading loadFlag)
607{
608 if (checkGarbageKeycode(shortcut)) {
609 return false;
610 }
611
612 if (!d->doRegister(action)) {
613 return false;
614 }
615
616 d->actionDefaultShortcuts.insert(key: action, value: shortcut);
617 d->updateGlobalShortcut(action, actionFlags: KGlobalAccelPrivate::DefaultShortcut, globalFlags: loadFlag);
618 return true;
619}
620
621bool KGlobalAccel::setShortcut(QAction *action, const QList<QKeySequence> &shortcut, GlobalShortcutLoading loadFlag)
622{
623 if (checkGarbageKeycode(shortcut)) {
624 return false;
625 }
626
627 if (!d->doRegister(action)) {
628 return false;
629 }
630
631 d->actionShortcuts.insert(key: action, value: shortcut);
632 d->updateGlobalShortcut(action, actionFlags: KGlobalAccelPrivate::ActiveShortcut, globalFlags: loadFlag);
633 return true;
634}
635
636QList<QKeySequence> KGlobalAccel::defaultShortcut(const QAction *action) const
637{
638 return d->actionDefaultShortcuts.value(key: action);
639}
640
641QList<QKeySequence> KGlobalAccel::shortcut(const QAction *action) const
642{
643 return d->actionShortcuts.value(key: action);
644}
645
646QList<QKeySequence> KGlobalAccel::globalShortcut(const QString &componentName, const QString &actionId) const
647{
648 // see also d->updateGlobalShortcut(action, KGlobalAccelPrivate::ActiveShortcut, KGlobalAccel::Autoloading);
649
650 // how componentName and actionId map to QAction, e.g:
651 // action->setProperty("componentName", "kwin");
652 // action->setObjectName("Kill Window");
653
654 const QList<QKeySequence> scResult = self()->d->iface()->shortcutKeys(actionId: {componentName, actionId, QString(), QString()});
655 return scResult;
656}
657
658void KGlobalAccel::removeAllShortcuts(QAction *action)
659{
660 d->remove(action, removal: KGlobalAccelPrivate::UnRegister);
661}
662
663bool KGlobalAccel::hasShortcut(const QAction *action) const
664{
665 return d->actionShortcuts.contains(key: action) || d->actionDefaultShortcuts.contains(key: action);
666}
667
668bool KGlobalAccel::setGlobalShortcut(QAction *action, const QList<QKeySequence> &shortcut)
669{
670 KGlobalAccel *g = KGlobalAccel::self();
671 return g->d->setShortcutWithDefault(action, shortcut, loadFlag: Autoloading);
672}
673
674bool KGlobalAccel::setGlobalShortcut(QAction *action, const QKeySequence &shortcut)
675{
676 return KGlobalAccel::setGlobalShortcut(action, shortcut: QList<QKeySequence>() << shortcut);
677}
678
679bool KGlobalAccelPrivate::setShortcutWithDefault(QAction *action, const QList<QKeySequence> &shortcut, KGlobalAccel::GlobalShortcutLoading loadFlag)
680{
681 if (checkGarbageKeycode(shortcut)) {
682 return false;
683 }
684
685 if (!doRegister(action)) {
686 return false;
687 }
688
689 actionDefaultShortcuts.insert(key: action, value: shortcut);
690 actionShortcuts.insert(key: action, value: shortcut);
691 updateGlobalShortcut(action, actionFlags: KGlobalAccelPrivate::DefaultShortcut | KGlobalAccelPrivate::ActiveShortcut, globalFlags: loadFlag);
692 return true;
693}
694
695QDBusArgument &operator<<(QDBusArgument &argument, const KGlobalAccel::MatchType &type)
696{
697 argument.beginStructure();
698 argument << static_cast<int>(type);
699 argument.endStructure();
700 return argument;
701}
702
703const QDBusArgument &operator>>(const QDBusArgument &argument, KGlobalAccel::MatchType &type)
704{
705 argument.beginStructure();
706 int arg;
707 argument >> arg;
708 type = static_cast<KGlobalAccel::MatchType>(arg);
709 argument.endStructure();
710 return argument;
711}
712
713#include "moc_kglobalaccel.cpp"
714

source code of kglobalaccel/src/kglobalaccel.cpp