File indexing completed on 2024-04-28 15:22:24

0001 /*
0002     SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "globalshortcut.h"
0008 
0009 #include "kglobalshortcutinfo_p.h"
0010 
0011 #include "component.h"
0012 #include "globalshortcutcontext.h"
0013 #include "globalshortcutsregistry.h"
0014 #include "logging_p.h"
0015 
0016 #include <QKeySequence>
0017 
0018 GlobalShortcut::GlobalShortcut()
0019     : GlobalShortcut(QString{}, QString{}, nullptr)
0020 {
0021 }
0022 
0023 GlobalShortcut::GlobalShortcut(const QString &uniqueName, const QString &friendlyName, GlobalShortcutContext *context)
0024     : _isPresent(false)
0025     , _isRegistered(false)
0026     , _isFresh(true)
0027     , _registry(GlobalShortcutsRegistry::self())
0028     , _context(context)
0029     , _uniqueName(uniqueName)
0030     , _friendlyName(friendlyName)
0031 {
0032     if (_context) {
0033         _context->addShortcut(this);
0034     }
0035 }
0036 
0037 GlobalShortcut::~GlobalShortcut()
0038 {
0039     setInactive();
0040 }
0041 
0042 GlobalShortcut::operator KGlobalShortcutInfo() const
0043 {
0044     KGlobalShortcutInfo info;
0045     info.d->uniqueName = _uniqueName;
0046     info.d->friendlyName = _friendlyName;
0047     info.d->contextUniqueName = context()->uniqueName();
0048     info.d->contextFriendlyName = context()->friendlyName();
0049     info.d->componentUniqueName = context()->component()->uniqueName();
0050     info.d->componentFriendlyName = context()->component()->friendlyName();
0051     for (const QKeySequence &key : std::as_const(_keys)) {
0052         info.d->keys.append(key);
0053     }
0054     for (const QKeySequence &key : std::as_const(_defaultKeys)) {
0055         info.d->defaultKeys.append(key);
0056     }
0057     return info;
0058 }
0059 
0060 bool GlobalShortcut::isActive() const
0061 {
0062     return _isRegistered;
0063 }
0064 
0065 bool GlobalShortcut::isFresh() const
0066 {
0067     return _isFresh;
0068 }
0069 
0070 bool GlobalShortcut::isPresent() const
0071 {
0072     return _isPresent;
0073 }
0074 
0075 bool GlobalShortcut::isSessionShortcut() const
0076 {
0077     return uniqueName().startsWith(QLatin1String("_k_session:"));
0078 }
0079 
0080 void GlobalShortcut::setIsFresh(bool value)
0081 {
0082     _isFresh = value;
0083 }
0084 
0085 void GlobalShortcut::setIsPresent(bool value)
0086 {
0087     // (de)activate depending on old/new value
0088     _isPresent = value;
0089     if (_isPresent) {
0090         setActive();
0091     } else {
0092         setInactive();
0093     }
0094 }
0095 
0096 GlobalShortcutContext *GlobalShortcut::context()
0097 {
0098     return _context;
0099 }
0100 
0101 GlobalShortcutContext const *GlobalShortcut::context() const
0102 {
0103     return _context;
0104 }
0105 
0106 QString GlobalShortcut::uniqueName() const
0107 {
0108     return _uniqueName;
0109 }
0110 
0111 void GlobalShortcut::unRegister()
0112 {
0113     return _context->component()->unregisterShortcut(uniqueName());
0114 }
0115 
0116 QString GlobalShortcut::friendlyName() const
0117 {
0118     return _friendlyName;
0119 }
0120 
0121 void GlobalShortcut::setFriendlyName(const QString &name)
0122 {
0123     _friendlyName = name;
0124 }
0125 
0126 QList<QKeySequence> GlobalShortcut::keys() const
0127 {
0128     return _keys;
0129 }
0130 
0131 void GlobalShortcut::setKeys(const QList<QKeySequence> &newKeys)
0132 {
0133     bool active = _isRegistered;
0134     if (active) {
0135         setInactive();
0136     }
0137 
0138     _keys.clear();
0139 
0140     auto getKey = [this](const QKeySequence &key) {
0141         if (key.isEmpty()) {
0142             qCDebug(KGLOBALACCELD) << _uniqueName << "skipping because key is empty";
0143             return QKeySequence{};
0144         }
0145 
0146         if (_registry->getShortcutByKey(key) //
0147             || _registry->getShortcutByKey(key, KGlobalAccel::MatchType::Shadowed) //
0148             || _registry->getShortcutByKey(key, KGlobalAccel::MatchType::Shadows) //
0149         ) {
0150             qCDebug(KGLOBALACCELD) << _uniqueName << "skipping because key" << QKeySequence(key).toString() << "is already taken";
0151             return QKeySequence{};
0152         }
0153 
0154         return key;
0155     };
0156 
0157     std::transform(newKeys.cbegin(), newKeys.cend(), std::back_inserter(_keys), getKey);
0158 
0159     if (active) {
0160         setActive();
0161     }
0162 }
0163 
0164 QList<QKeySequence> GlobalShortcut::defaultKeys() const
0165 {
0166     return _defaultKeys;
0167 }
0168 
0169 void GlobalShortcut::setDefaultKeys(const QList<QKeySequence> &newKeys)
0170 {
0171     _defaultKeys = newKeys;
0172 }
0173 
0174 void GlobalShortcut::setActive()
0175 {
0176     if (!_isPresent || _isRegistered) {
0177         // The corresponding application is not present or the keys are
0178         // already grabbed
0179         return;
0180     }
0181 
0182     for (const QKeySequence &key : std::as_const(_keys)) {
0183         if (!key.isEmpty() && !_registry->registerKey(key, this)) {
0184             qCDebug(KGLOBALACCELD) << uniqueName() << ": Failed to register " << QKeySequence(key).toString();
0185         }
0186     }
0187 
0188     _isRegistered = true;
0189 }
0190 
0191 void GlobalShortcut::setInactive()
0192 {
0193     if (!_isRegistered) {
0194         // The keys are not grabbed currently
0195         return;
0196     }
0197 
0198     for (const QKeySequence &key : std::as_const(_keys)) {
0199         if (!key.isEmpty() && !_registry->unregisterKey(key, this)) {
0200             qCDebug(KGLOBALACCELD) << uniqueName() << ": Failed to unregister " << QKeySequence(key).toString();
0201         }
0202     }
0203 
0204     _isRegistered = false;
0205 }