/* 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 "globalshortcutsregistry.h" #include "component.h" #include "globalshortcut.h" #include "globalshortcutcontext.h" #include "kdebug.h" #include "kglobal.h" #include "klocale.h" #include "knotification.h" #include #include #ifdef Q_WS_X11 #include "kglobalaccel_x11.h" #include #include #else #include "kglobalaccel_qws.h" #endif GlobalShortcutsRegistry::GlobalShortcutsRegistry() : QObject() ,_active_keys() ,_components() ,_manager(new KGlobalAccelImpl(this)) ,_config("kglobalshortcutsrc", KConfig::SimpleConfig) { _manager->setEnabled(true); } GlobalShortcutsRegistry::~GlobalShortcutsRegistry() { _manager->setEnabled(false); // Ungrab all keys. We don't go over GlobalShortcuts because // GlobalShortcutsRegistry::self() doesn't work anymore. Q_FOREACH (const int key, _active_keys.keys()) { _manager->grabKey(key, false); } _active_keys.clear(); } KdeDGlobalAccel::Component *GlobalShortcutsRegistry::addComponent(KdeDGlobalAccel::Component *component) { if (_components.value(component->uniqueName())) { Q_ASSERT_X(false, "GlobalShortcutsRegistry::addComponent", "component already registered?!?!"); return _components.value(component->uniqueName()); } _components.insert(component->uniqueName(), component); QDBusConnection conn(QDBusConnection::sessionBus()); conn.registerObject( component->dbusPath().path(), component, QDBusConnection::ExportScriptableContents); return component; } void GlobalShortcutsRegistry::activateShortcuts() { Q_FOREACH (KdeDGlobalAccel::Component *component, _components) { component->activateShortcuts(); } } QList GlobalShortcutsRegistry::allMainComponents() const { return _components.values(); } void GlobalShortcutsRegistry::clear() { Q_FOREACH(KdeDGlobalAccel::Component *component, _components) { delete component; } _components.clear(); // The shortcuts should have deregistered themselves Q_ASSERT(_active_keys.isEmpty()); } QDBusObjectPath GlobalShortcutsRegistry::dbusPath() const { return _dbusPath; } void GlobalShortcutsRegistry::deactivateShortcuts(bool temporarily) { Q_FOREACH (KdeDGlobalAccel::Component *component, _components) { component->deactivateShortcuts(temporarily); } } GlobalShortcut *GlobalShortcutsRegistry::getActiveShortcutByKey(int key) const { return _active_keys.value(key); } KdeDGlobalAccel::Component *GlobalShortcutsRegistry::getComponent(const QString &uniqueName) { return _components.value(uniqueName); } GlobalShortcut *GlobalShortcutsRegistry::getShortcutByKey(int key) const { Q_FOREACH (KdeDGlobalAccel::Component *component, _components) { GlobalShortcut *rc = component->getShortcutByKey(key); if (rc) return rc; } return NULL; } QList GlobalShortcutsRegistry::getShortcutsByKey(int key) const { QList rc; Q_FOREACH (KdeDGlobalAccel::Component *component, _components) { rc = component->getShortcutsByKey(key); if (!rc.isEmpty()) return rc; } return rc; } bool GlobalShortcutsRegistry::isShortcutAvailable( int shortcut, const QString &componentName, const QString &contextName) const { Q_FOREACH (KdeDGlobalAccel::Component *component, _components) { if (!component->isShortcutAvailable(shortcut, componentName, contextName)) return false; } return true; } GlobalShortcutsRegistry * GlobalShortcutsRegistry::self() { K_GLOBAL_STATIC( GlobalShortcutsRegistry, self ); return self; } bool GlobalShortcutsRegistry::keyPressed(int keyQt) { GlobalShortcut *shortcut = getShortcutByKey(keyQt); if (!shortcut) { // This can happen for example with the ALT-Print shortcut of kwin. // ALT+PRINT is SYSREQ on my keyboard. So we grab something we think // is ALT+PRINT but symXToKeyQt and modXToQt make ALT+SYSREQ of it // when pressed (correctly). We can't match that. kDebug() << "Got unknown key" << QKeySequence(keyQt).toString(); // In production mode just do nothing. return false; } else if (!shortcut->isActive()) { kDebug() << "Got inactive key" << QKeySequence(keyQt).toString(); // In production mode just do nothing. return false; } kDebug() << QKeySequence(keyQt).toString() << "=" << shortcut->uniqueName(); QStringList data(shortcut->context()->component()->uniqueName()); data.append(shortcut->uniqueName()); data.append(shortcut->context()->component()->friendlyName()); data.append(shortcut->friendlyName()); #ifdef Q_WS_X11 // 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. qApp->syncX(); #endif // 1st Invoke the action shortcut->context()->component()->emitGlobalShortcutPressed( *shortcut ); // Then do anything else KNotification *notification = new KNotification( "globalshortcutpressed", KNotification::CloseOnTimeout); notification->setText( i18n("The global shortcut for %1 was issued.", shortcut->friendlyName())); notification->addContext( "application", shortcut->context()->component()->friendlyName() ); notification->sendEvent(); return true; } void GlobalShortcutsRegistry::loadSettings() { foreach (const QString &groupName, _config.groupList()) { kDebug() << "Loading group " << groupName; Q_ASSERT(groupName.indexOf('\x1d')==-1); // loadSettings isn't designed to be called in between. Only at the // beginning. Q_ASSERT(!getComponent(groupName)); KConfigGroup configGroup(&_config, groupName); // We previously stored the friendly name in a separate group. migrate // that QString friendlyName; KConfigGroup friendlyGroup(&configGroup, "Friendly Name"); if (friendlyGroup.isValid()) { friendlyName = friendlyGroup.readEntry("Friendly Name"); friendlyGroup.deleteGroup(); } else { friendlyName = configGroup.readEntry("_k_friendly_name"); } // Create the component KdeDGlobalAccel::Component *component = new KdeDGlobalAccel::Component( groupName, friendlyName, this); // Now load the contexts Q_FOREACH(const QString& context, configGroup.groupList()) { // Skip the friendly name group if (context=="Friendly Name") continue; KConfigGroup contextGroup(&configGroup, context); QString contextFriendlyName = contextGroup.readEntry("_k_friendly_name"); component->createGlobalShortcutContext(context, contextFriendlyName); component->activateGlobalShortcutContext(context); component->loadSettings(contextGroup); } // Load the default context component->activateGlobalShortcutContext("default"); component->loadSettings(configGroup); } } void GlobalShortcutsRegistry::grabKeys() { activateShortcuts(); } bool GlobalShortcutsRegistry::registerKey(int key, GlobalShortcut *shortcut) { if (key == 0) { kDebug() << shortcut->uniqueName() << ": Key '" << QKeySequence(key).toString() << "' already taken by " << _active_keys.value(key)->uniqueName() << "."; return false; } else if (_active_keys.value(key)) { kDebug() << shortcut->uniqueName() << ": Attempt to register key 0."; return false; } kDebug() << "Registering key" << QKeySequence(key).toString() << "for" << shortcut->context()->component()->uniqueName() << ":" << shortcut->uniqueName(); _active_keys.insert(key, shortcut); return _manager->grabKey(key, true); } void GlobalShortcutsRegistry::setAccelManager(KGlobalAccelImpl *manager) { _manager = manager; } void GlobalShortcutsRegistry::setDBusPath(const QDBusObjectPath &path) { _dbusPath = path; } KdeDGlobalAccel::Component *GlobalShortcutsRegistry::takeComponent(KdeDGlobalAccel::Component *component) { QDBusConnection conn(QDBusConnection::sessionBus()); conn.unregisterObject(component->dbusPath().path()); return _components.take(component->uniqueName()); } void GlobalShortcutsRegistry::ungrabKeys() { deactivateShortcuts(); } bool GlobalShortcutsRegistry::unregisterKey(int key, GlobalShortcut *shortcut) { if (_active_keys.value(key)!=shortcut) { // The shortcut doesn't own the key or the key isn't grabbed return false; } kDebug() << "Unregistering key" << QKeySequence(key).toString() << "for" << shortcut->context()->component()->uniqueName() << ":" << shortcut->uniqueName(); _manager->grabKey(key, false); _active_keys.take(key); return true; } void GlobalShortcutsRegistry::writeSettings() const { Q_FOREACH( const KdeDGlobalAccel::Component *component, GlobalShortcutsRegistry::self()->allMainComponents()) { KConfigGroup configGroup(&_config, component->uniqueName()); if (component->allShortcuts().isEmpty()) { configGroup.deleteGroup(); delete component; } else { component->writeSettings(configGroup); } } _config.sync(); } #include "moc_globalshortcutsregistry.cpp"