diff --git a/src/runtime/component.cpp b/src/runtime/component.cpp index 669df67..a3d94ba 100644 --- a/src/runtime/component.cpp +++ b/src/runtime/component.cpp @@ -1,499 +1,500 @@ /* Copyright (C) 2008 Michael Jansen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "component.h" #include "globalshortcut.h" #include "globalshortcutcontext.h" #include "globalshortcutsregistry.h" #include "logging_p.h" #include #include "kglobalaccel_interface.h" #include #include #include #if HAVE_X11 #include #endif static QList keysFromString(const QString &str) { QList ret; if (str == QLatin1String("none")) { return ret; } const QStringList strList = str.split('\t'); for (const QString &s : strList) { int key = QKeySequence(s)[0]; if (key != -1) { //sanity check just in case ret.append(key); } } return ret; } static QString stringFromKeys(const QList &keys) { if (keys.isEmpty()) { return QStringLiteral("none"); } QString ret; for (int key : keys) { ret.append(QKeySequence(key).toString()); ret.append('\t'); } ret.chop(1); return ret; } namespace KdeDGlobalAccel { Component::Component( const QString &uniqueName, const QString &friendlyName, GlobalShortcutsRegistry *registry) : _uniqueName(uniqueName) ,_friendlyName(friendlyName) ,_registry(registry) { // Make sure we do no get uniquenames still containing the context Q_ASSERT(uniqueName.indexOf("|")==-1); // Register ourselve with the registry if (_registry) { _registry->addComponent(this); } QString DEFAULT=QStringLiteral("default"); createGlobalShortcutContext(DEFAULT, QStringLiteral("Default Context")); _current = _contexts.value(DEFAULT); } Component::~Component() { // Remove ourselve from the registry if (_registry) { _registry->takeComponent(this); } // We delete all shortcuts from all contexts qDeleteAll(_contexts); } bool Component::activateGlobalShortcutContext( const QString &uniqueName) { if (!_contexts.value(uniqueName)) { createGlobalShortcutContext(uniqueName, "TODO4"); return false; } // Deactivate the current contexts shortcuts deactivateShortcuts(); // Switch the context _current = _contexts.value(uniqueName); return true; } void Component::activateShortcuts() { for (GlobalShortcut *shortcut : qAsConst(_current->_actions)) { shortcut->setActive(); } } QList Component::allShortcuts(const QString &contextName) const { GlobalShortcutContext *context = _contexts.value(contextName); if (context) { return context->_actions.values(); } else { return QList (); } } QList Component::allShortcutInfos(const QString &contextName) const { GlobalShortcutContext *context = _contexts.value(contextName); if (!context) { return QList(); } return context->allShortcutInfos(); } bool Component::cleanUp() { bool changed = false; - for (GlobalShortcut *shortcut : qAsConst(_current->_actions)) + const auto actions = _current->_actions; + for (GlobalShortcut *shortcut : actions) { qCDebug(KGLOBALACCELD) << _current->_actions.size(); if (!shortcut->isPresent()) { changed = true; shortcut->unRegister(); } } if (changed) { _registry->writeSettings(); // We could be destroyed after this call! } return changed; } bool Component::createGlobalShortcutContext( const QString &uniqueName, const QString &friendlyName) { if (_contexts.value(uniqueName)) { qCDebug(KGLOBALACCELD) << "Shortcut Context " << uniqueName << "already exists for component " << _uniqueName; return false; } _contexts.insert(uniqueName, new GlobalShortcutContext(uniqueName, friendlyName, this)); return true; } GlobalShortcutContext *Component::currentContext() { return _current; } QDBusObjectPath Component::dbusPath() const { QString dbusPath = _uniqueName; // Clean up for dbus usage: any non-alphanumeric char should be turned into '_' const int len = dbusPath.length(); for ( int i = 0; i < len; ++i ) { if ( !dbusPath[i].isLetterOrNumber() ) dbusPath[i] = QLatin1Char('_'); } // QDBusObjectPath could be a little bit easier to handle :-) return QDBusObjectPath( _registry->dbusPath().path() + "component/" + dbusPath); } void Component::deactivateShortcuts(bool temporarily) { for (GlobalShortcut *shortcut : qAsConst(_current->_actions)) { if (temporarily && uniqueName() == QLatin1String("kwin") && shortcut->uniqueName() == QLatin1String("Block Global Shortcuts")) { continue; } shortcut->setInactive(); } } void Component::emitGlobalShortcutPressed( const GlobalShortcut &shortcut ) { #if HAVE_X11 // pass X11 timestamp long timestamp = QX11Info::appTime(); // Make sure kglobalacceld has ungrabbed the keyboard after receiving the // keypress, otherwise actions in application that try to grab the // keyboard (e.g. in kwin) may fail to do so. There is still a small race // condition with this being out-of-process. if (_registry->_manager) { _registry->_manager->syncWindowingSystem(); } #else long timestamp = 0; #endif // Make sure it is one of ours if (shortcut.context()->component() != this) { // In production mode do nothing return; } emit globalShortcutPressed( shortcut.context()->component()->uniqueName(), shortcut.uniqueName(), timestamp); } void Component::invokeShortcut(const QString &shortcutName, const QString &context) { GlobalShortcut *shortcut = getShortcutByName(shortcutName, context); if (shortcut) emitGlobalShortcutPressed(*shortcut); } QString Component::friendlyName() const { if (_friendlyName.isEmpty()) return _uniqueName; return _friendlyName; } GlobalShortcut *Component::getShortcutByKey(int key) const { return _current->getShortcutByKey(key); } QList Component::getShortcutsByKey(int key) const { QList rc; for (GlobalShortcutContext *context : qAsConst(_contexts)) { GlobalShortcut *sc = context->getShortcutByKey(key); if (sc) rc.append(sc); } return rc; } GlobalShortcut *Component::getShortcutByName(const QString &uniqueName, const QString &context) const { if (!_contexts.value(context)) { return nullptr; } return _contexts.value(context)->_actions.value(uniqueName); } QStringList Component::getShortcutContexts() const { return _contexts.keys(); } bool Component::isActive() const { // The component is active if at least one of it's global shortcuts is // present. for (GlobalShortcut *shortcut : qAsConst(_current->_actions)) { if (shortcut->isPresent()) return true; } return false; } bool Component::isShortcutAvailable( int key, const QString &component, const QString &context) const { qCDebug(KGLOBALACCELD) << QKeySequence(key).toString() << component; // if this component asks for the key. only check the keys in the same // context if (component==uniqueName()) { const auto actions = shortcutContext(context)->_actions; for (GlobalShortcut *sc : actions) { if (sc->keys().contains(key)) return false; } } else { for (GlobalShortcutContext *ctx : qAsConst(_contexts)) { for (GlobalShortcut *sc : qAsConst(ctx->_actions)) { if (sc->keys().contains(key)) return false; } } } return true; } GlobalShortcut *Component::registerShortcut(const QString &uniqueName, const QString &friendlyName, const QString &shortcutString, const QString &defaultShortcutString) { // The shortcut will register itself with us GlobalShortcut *shortcut = new GlobalShortcut( uniqueName, friendlyName, currentContext()); const QList keys = keysFromString(shortcutString); shortcut->setDefaultKeys(keysFromString(defaultShortcutString)); shortcut->setIsFresh(false); QList newKeys = keys; for (int key : keys) { if (key != 0) { if (GlobalShortcutsRegistry::self()->getShortcutByKey(key)) { // The shortcut is already used. The config file is // broken. Ignore the request. newKeys.removeAll(key); qCWarning(KGLOBALACCELD) << "Shortcut found twice in kglobalshortcutsrc."<setKeys(keys); return shortcut; } void Component::loadSettings(KConfigGroup &configGroup) { // GlobalShortcutsRegistry::loadSettings handles contexts. const auto listKeys = configGroup.keyList(); for (const QString &confKey : listKeys) { const QStringList entry = configGroup.readEntry(confKey, QStringList()); if (entry.size() != 3) { continue; } GlobalShortcut *shortcut = registerShortcut(confKey, entry[2], entry[0], entry[1]); if (configGroup.name().endsWith(QLatin1String(".desktop"))) { shortcut->setIsPresent(true); } } } void Component::setFriendlyName(const QString &name) { _friendlyName = name; } GlobalShortcutContext *Component::shortcutContext( const QString &contextName ) { return _contexts.value(contextName); } GlobalShortcutContext const *Component::shortcutContext( const QString &contextName ) const { return _contexts.value(contextName); } QStringList Component::shortcutNames( const QString &contextName) const { GlobalShortcutContext *context = _contexts.value(contextName); if (!context) { return QStringList(); } return context->_actions.keys(); } QString Component::uniqueName() const { return _uniqueName; } void Component::unregisterShortcut(const QString &uniqueName) { // Now wrote all contexts for( GlobalShortcutContext *context : qAsConst(_contexts)) { if (context->_actions.value(uniqueName)) { delete context->takeShortcut(context->_actions.value(uniqueName)); } } } void Component::writeSettings(KConfigGroup& configGroup) const { // If we don't delete the current content global shortcut // registrations will never not deleted after forgetGlobalShortcut() configGroup.deleteGroup(); // Now write all contexts for( GlobalShortcutContext *context : qAsConst(_contexts)) { KConfigGroup contextGroup; if (context->uniqueName() == QLatin1String("default")) { contextGroup = configGroup; // Write the friendly name contextGroup.writeEntry("_k_friendly_name", friendlyName()); } else { contextGroup = KConfigGroup(&configGroup, context->uniqueName()); // Write the friendly name contextGroup.writeEntry("_k_friendly_name", context->friendlyName()); } // qCDebug(KGLOBALACCELD) << "writing group " << _uniqueName << ":" << context->uniqueName(); for (const GlobalShortcut *shortcut : qAsConst(context->_actions)) { // qCDebug(KGLOBALACCELD) << "writing" << shortcut->uniqueName(); // We do not write fresh shortcuts. // We do not write session shortcuts if (shortcut->isFresh() || shortcut->isSessionShortcut()) { continue; } // qCDebug(KGLOBALACCELD) << "really writing" << shortcut->uniqueName(); QStringList entry(stringFromKeys(shortcut->keys())); entry.append(stringFromKeys(shortcut->defaultKeys())); entry.append(shortcut->friendlyName()); contextGroup.writeEntry(shortcut->uniqueName(), entry); } } } } // namespace KdeDGlobalAccel