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

source code of kglobalaccel/src/kglobalaccel.cpp