diff --git a/kdeui/shortcuts/README b/kdeui/shortcuts/README index 04cc61f67b..7badd0ebf1 100644 --- a/kdeui/shortcuts/README +++ b/kdeui/shortcuts/README @@ -1,64 +1,64 @@ # Overall summary of global shortcut implementation ## KAction, KGlobalAccel and KdedGlobalAccel [Basic functionality] - You call KAction::setGlobalShortcut() to set a shortcut for an action. KAction then calls KGlobalAccel which is, among other things, the interface to KdedGlobalAccel (communication via DBus). KdedGlobalAccel is a KDED module as you might have guessed. - KdedGlobalAccel then grabs the shortcut key in a platform-specific way and makes an entry of the mapping key<->action where actions are identified by their main component name and their own name. - When a key grab triggers, KdedGlobalAccel calls (via DBus) KGlobalAccel which tells the action to trigger. The KdedGlobalAccel is responsible for actually handling the shortcuts, loading and saving the shortcut keys to kglobalshortcutrc. It doesn't really know the actions, it just know what KGlobalAccel gave it. [Conflict resolution] KdedGlobalAccel has a list of all global shortcuts. If you try to assign a key twice, it will tell the appropriate KdedGlobalAccel/KGlobalAccel that the corresponding shortcut was changed to an empty one, which goes back to the KAction. When manually assigning shortcuts, the config widget asks KGlobalAccel/KdedGlobalAccel for conflicts and presents options to the user to fix them. To prevent all clashes as good as possible, KdedGlobalAccel remembers key<-> action mappings even after the corresponding application shuts down. [More details] KAction instances talk to the KGlobalAccel singleton to make it aware of global shortcuts changes via KGlobalAccel::updateGlobalShortcuts() (to define the shortcut) KGlobalAccel::updateGlobalShortcutsAllowed() (to enable/disable the shortcut) These two methods do the following: - Create an action "id" which is a QStringList of two items: the application component and the action text (this is bound to cause trouble with i18n) - Convert the KAction shortcuts to a QList - Pass all this via DBus to the KdedGlobalAccel instance, which lives in the kded4 process. KGlobalAccel::updateGlobalShortcutsAllowed(true) sets the "SetPresent" flag when calling kdedglobalaccel, which makes kdedglobalaccel actually grab the key shortcut (so that the grab is done after the action has been defined, and only if it is enabled). kdedglobalaccel must know about inactive global shortcuts too (e.g. those defined in -appliations not running at the moment), for conflict resolution. +applications not running at the moment), for conflict resolution. ## kdebase side: keyboard shortcuts KCM The keyboard shortcuts KCM can be found in kdebase/workspace/kcontrol/keys/ The KCM gets the global shortcut info from the KdedGlobalAccel instance via DBus. It uses KShortcutsEditor to let the user edit the shortcuts. Since KShortcutsEditor manipulates KAction instances, the kcm creates "fake" actions. -- Aurélien Gâteau, 2008.02.01 aurelien.gateau@free.fr David Faure, 2008.02.05 faure@kde.org diff --git a/kdeui/shortcuts/kdedglobalaccel.cpp b/kdeui/shortcuts/kdedglobalaccel.cpp index 3d6bf0604d..11a91cdffd 100644 --- a/kdeui/shortcuts/kdedglobalaccel.cpp +++ b/kdeui/shortcuts/kdedglobalaccel.cpp @@ -1,502 +1,503 @@ /* This file is part of the KDE libraries Copyright (c) 2007 Andreas Hartmetz 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 "kdedglobalaccel.h" #include #include "kdedglobalaccel_adaptor.h" // For KGlobalAccelImpl #ifdef Q_WS_X11 #include "kglobalaccel_x11.h" #elif defined(Q_WS_MACX) #include "kglobalaccel_mac.h" #elif defined(Q_WS_WIN) #include "kglobalaccel_win.h" #else #include "kglobalaccel_qws.h" #endif #include #include #ifdef Q_WS_X11 #include #endif #include #include #include #include #include K_PLUGIN_FACTORY(KdedGlobalAccelFactory, registerPlugin(); ) K_EXPORT_PLUGIN(KdedGlobalAccelFactory("globalaccel")) struct actionData { //TODO: clear isPresent when an action's application/mainComponent disappears bool isPresent : 1; bool isDefaultEmpty : 1; QStringList actionId; QList keys; QList defaultKeys; }; enum IdField { ComponentField = 0, ActionField = 1 }; class KdedGlobalAccelPrivate { public: KdedGlobalAccelPrivate(); ~KdedGlobalAccelPrivate(); actionData *findAction(int) const; actionData *findAction(const QStringList &actionId) const; actionData *addAction(const QStringList &actionId); actionData *takeAction(const QStringList &actionId); QList componentActions(const QString &mainComponentName); //helpers static bool isEmpty(const QList&); static QList nonemptyOnly(const QList &); KGlobalAccelImpl *impl; QHash keyToAction; QHash *> mainComponentHashes; KConfig config; KConfigGroup configGroup; QTimer writeoutTimer; }; KdedGlobalAccelPrivate::KdedGlobalAccelPrivate() : config("kglobalshortcutsrc"), configGroup(&config, "KDE Global Shortcuts") { } KdedGlobalAccelPrivate::~KdedGlobalAccelPrivate() { } actionData *KdedGlobalAccelPrivate::findAction(int key) const { return keyToAction.value(key); } actionData *KdedGlobalAccelPrivate::findAction(const QStringList &actionId) const { if (actionId.count() < 2) return 0; QHash *componentHash = mainComponentHashes.value(actionId.at(ComponentField)); if (!componentHash) return 0; return componentHash->value(actionId.at(ActionField)); } actionData *KdedGlobalAccelPrivate::addAction(const QStringList &actionId) { QHash *componentHash = mainComponentHashes.value(actionId.at(ComponentField)); if (!componentHash) { componentHash = new QHash; mainComponentHashes.insert(actionId.at(ComponentField), componentHash); } Q_ASSERT(!componentHash->value(actionId.at(ActionField))); actionData *ad = new actionData; ad->actionId = actionId; componentHash->insert(actionId.at(ActionField), ad); return ad; } actionData *KdedGlobalAccelPrivate::takeAction(const QStringList &actionId) { QHash *componentHash = mainComponentHashes.value(actionId.at(ComponentField)); if (!componentHash) return 0; actionData *ret = componentHash->take(actionId.at(ActionField)); if (componentHash->isEmpty()) delete mainComponentHashes.take(actionId.at(ComponentField)); return ret; } //return if a list of keys is *logically* empty //static bool KdedGlobalAccelPrivate::isEmpty(const QList& keys) { const int count = keys.count(); for (int i = 0; i < count; i++) if (keys[i] != 0) return false; return true; } //static QList KdedGlobalAccelPrivate::nonemptyOnly(const QList &keys) { QList ret; const int count = keys.count(); for (int i = 0; i < count; i++) if (keys[i] != 0) ret.append(keys[i]); return ret; } KdedGlobalAccel::KdedGlobalAccel(QObject* parent, const QList&) : KDEDModule(parent), d(new KdedGlobalAccelPrivate) { qDBusRegisterMetaType >(); d->impl = new KGlobalAccelImpl(this); //TODO: Make this controllable from applications, for example to prevent //shortcuts from triggering when the user is entering a shortcut d->impl->setEnabled(true); connect(&d->writeoutTimer, SIGNAL(timeout()), SLOT(writeSettings())); d->writeoutTimer.setSingleShot(true); connect(this, SIGNAL(moduleDeleted(KDEDModule *)), SLOT(writeSettings())); loadSettings(); new KdedGlobalAccelAdaptor(this); QDBusConnection::sessionBus().registerObject("/KdedGlobalAccel", this); } KdedGlobalAccel::~KdedGlobalAccel() { //TODO: is this safe? delete d->impl; delete d; } QStringList KdedGlobalAccel::allComponents() { return d->mainComponentHashes.keys(); } QStringList KdedGlobalAccel::allActionsForComponent(const QString& component) { return d->mainComponentHashes[component]->keys(); } QList KdedGlobalAccel::allKeys() { QList ret = d->keyToAction.keys(); kDebug() << ret; return ret; } QStringList KdedGlobalAccel::allKeysAsString() { QStringList ret; foreach(int keyQt, d->keyToAction.keys()) ret << QKeySequence(keyQt).toString(); return ret; } QStringList KdedGlobalAccel::actionId(int key) { actionData *ad = d->findAction(key); if (ad) return ad->actionId; return QStringList(); } QList KdedGlobalAccel::shortcut(const QStringList &action) { actionData *ad = d->findAction(action); if (ad) return ad->keys; return QList(); } QList KdedGlobalAccel::defaultShortcut(const QStringList &action) { actionData *ad = d->findAction(action); if (ad) return ad->defaultKeys; return QList(); } //TODO: make sure and document that we don't want trailing zero shortcuts in the list QList KdedGlobalAccel::setShortcut(const QStringList &actionId, const QList &keys, uint flags) { //spare the DBus framework some work const bool isDefaultEmpty = (flags & IsDefaultEmpty); const bool setPresent = (flags & SetPresent); const bool isAutoloading = !(flags & NoAutoloading); const bool isDefault = (flags & IsDefault); + //kDebug() << actionId << keys << "isDefaultEmpty=" << isDefaultEmpty << "setPresent=" << setPresent + // << "isAutoloading=" << isAutoloading << "isDefault=" << isDefault; + actionData *ad = d->findAction(actionId); //the trivial and common case - synchronize the action from our data and exit bool loadKeys = (isAutoloading && ad); if (loadKeys) { if (!ad->isPresent && setPresent) { ad->isPresent = true; foreach (int key, ad->keys) if (key != 0) { Q_ASSERT(d->keyToAction.value(key) == ad); d->impl->grabKey(key, true); } } ad->isDefaultEmpty = isDefaultEmpty; return ad->keys; } //now we are actually changing the shortcut of the action QList added = d->nonemptyOnly(keys); bool didCreate = false; if (!ad) { didCreate = true; ad = d->addAction(actionId); ad->isPresent = false; //the rest will be initialized below } - if (!ad) - return QList(); //take care of stale keys and remove from added these that remain. foreach(int oldKey, ad->keys) { if (oldKey != 0) { bool remains = false; for (int i = 0; i < added.count(); i++) { if (oldKey == added[i]) { added.removeAt(i); i--; remains = true; //no break; - remove possible duplicates } } if (!remains) { d->keyToAction.remove(oldKey); if (ad->isPresent) d->impl->grabKey(oldKey, false); } } } //update ad //note that ad->keys may still get changed later if conflicts are found ad->isDefaultEmpty = isDefaultEmpty; if (setPresent) ad->isPresent = true; if (isDefault) ad->defaultKeys = keys; ad->keys = keys; //update keyToAction and find conflicts with other actions //this code inherently does the right thing for duplicates in added for (int i = 0; i < added.count(); i++) { if (!d->keyToAction.contains(added[i])) { d->keyToAction.insert(added[i], ad); } else { //conflict for (int j = 0; j < ad->keys.count(); j++) { if (ad->keys[j] == added[i]) { if (ad->keys.last() == added[i]) { ad->keys.removeLast(); j--; } else ad->keys[j] = 0; } } added.removeAt(i); i--; } } if (ad->isPresent) foreach (int key, added) { Q_ASSERT(d->keyToAction.value(key) == ad); d->impl->grabKey(key, true); } scheduleWriteSettings(); return ad->keys; } void KdedGlobalAccel::setForeignShortcut(const QStringList &actionId, const QList &keys) { actionData *ad = d->findAction(actionId); if (!ad) return; uint setterFlags = NoAutoloading; if (ad->isDefaultEmpty) setterFlags |= IsDefaultEmpty; QList oldKeys = ad->keys; QList newKeys = setShortcut(actionId, keys, setterFlags); if (oldKeys != newKeys) emit yourShortcutGotChanged(actionId, newKeys); } void KdedGlobalAccel::setInactive(const QStringList &actionId) { actionData *ad = d->findAction(actionId); if (!ad) return; ad->isPresent = false; const int len = ad->keys.count(); for (int i = 0; i < len; i++) if (ad->keys[i] != 0) d->impl->grabKey(ad->keys[i], false); } void KdedGlobalAccel::scheduleWriteSettings() { if (!d->writeoutTimer.isActive()) d->writeoutTimer.start(500); } //slot void KdedGlobalAccel::writeSettings() { typedef QHash adHash; //avoid comma in macro arguments foreach (const adHash *const mc, d->mainComponentHashes) { foreach (const actionData *const ad, *mc) { QString confKey = ad->actionId.join("\01"); if (ad->keys == ad->defaultKeys) { // If this is a default key, make sure we don't keep an old // custom key in the config file d->configGroup.deleteEntry(confKey); } else if (!d->isEmpty(ad->keys)) d->configGroup.writeEntry(confKey, stringFromKeys(ad->keys)); else d->configGroup.writeEntry(confKey, "none"); } } d->configGroup.sync(); } void KdedGlobalAccel::loadSettings() { //TODO: more sanity checks QMap entries = d->configGroup.entryMap(); QString empty; QStringList lActionId(empty); lActionId.append(empty); QMap::const_iterator it; for (it = entries.constBegin(); it != entries.constEnd(); ++it) { //cut at the first occurrence *only*, so no split('\01') int snip = it.key().indexOf('\01'); //TODO: definitely more sanity checks, like bounds check of (snip + 1) :) lActionId[ComponentField] = it.key().left(snip); lActionId[ActionField] = it.key().mid(snip + 1); QList lKeys = keysFromString(it.value()); actionData *ad = d->addAction(lActionId); ad->keys = lKeys; ad->isPresent = false; //If we loaded an empty action, that action must have been saved //because it was *not* empty by default. This boolean does not propagate //out of this class, so it's ok to mess with it as we like. ad->isDefaultEmpty = false; foreach (int key, lKeys) if (key != 0) d->keyToAction.insert(key, ad); } } QList KdedGlobalAccel::keysFromString(const QString &str) { QList ret; if (str == "none") return ret; QStringList strList = str.split('\01'); foreach (const QString &s, strList) ret.append(QKeySequence(s)[0]); return ret; } QString KdedGlobalAccel::stringFromKeys(const QList &keys) { //the special output "none" is generated at the caller QString ret; foreach (int key, keys) { ret.append(QKeySequence(key).toString()); ret.append('\01'); } //this is safe if the index is out of bounds ret.truncate(ret.length() - 1); return ret; } bool KdedGlobalAccel::keyPressed(int keyQt) { actionData *ad = d->keyToAction.value(keyQt); if (!ad || !ad->isPresent) return false; QStringList data = ad->actionId; #ifdef Q_WS_X11 // pass X11 timestamp data.append(QString::number(QX11Info::appTime())); #endif emit invokeAction(data); return true; } #include "kdedglobalaccel.moc" #include "kdedglobalaccel_adaptor.moc" diff --git a/kdeui/shortcuts/kglobalaccel.cpp b/kdeui/shortcuts/kglobalaccel.cpp index b82b61b4a7..d7f377e63f 100644 --- a/kdeui/shortcuts/kglobalaccel.cpp +++ b/kdeui/shortcuts/kglobalaccel.cpp @@ -1,347 +1,337 @@ /* This file is part of the KDE libraries Copyright (C) 2001,2002 Ellis Whitehead Copyright (C) 2006 Hamish Rodda Copyright (C) 2007 Andreas Hartmetz 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 "kglobalaccel.h" #include "kglobalaccel_p.h" #include "kdedglobalaccel.h" // For KGlobalAccelImpl #ifdef Q_WS_X11 #include "kglobalaccel_x11.h" #elif defined(Q_WS_MACX) #include "kglobalaccel_mac.h" #elif defined(Q_WS_WIN) #include "kglobalaccel_win.h" #elif defined(Q_WS_QWS) #include "kglobalaccel_qws.h" #else #include "kglobalaccel_emb.h" #endif #include #include #include #ifdef Q_WS_X11 #include #include #include #include #endif #include #include #include #include #include #include #include "kaction.h" #include "kaction_p.h" #include "kactioncollection.h" #include "kmessagebox.h" #include "kshortcut.h" //TODO what was the problem that got fixed recently in the old version? - forward port if necessary KGlobalAccelPrivate::KGlobalAccelPrivate(KGlobalAccel* q) : isUsingForeignComponentName(false), enabled(true), iface("org.kde.kded", "/modules/kdedglobalaccel", QDBusConnection::sessionBus()) { // Make sure kded is running QDBusConnectionInterface* bus = QDBusConnection::sessionBus().interface(); if (!bus->isServiceRegistered("org.kde.kded")) { KToolInvocation::klauncher(); // this calls startKdeinit } QObject::connect(bus, SIGNAL(serviceOwnerChanged(QString,QString,QString)), q, SLOT(_k_serviceOwnerChanged(QString,QString,QString))); } KGlobalAccel::KGlobalAccel() : d(new KGlobalAccelPrivate(this)) { qDBusRegisterMetaType >(); connect(&d->iface, SIGNAL(invokeAction(const QStringList &)), SLOT(_k_invokeAction(const QStringList &))); connect(&d->iface, SIGNAL(yourShortcutGotChanged(const QStringList &, const QList &)), SLOT(_k_shortcutGotChanged(const QStringList &, const QList &))); if (KGlobal::hasMainComponent()) d->mainComponentName = KGlobal::mainComponent().componentName(); } KGlobalAccel::~KGlobalAccel() { //TODO *maybe* we need to ungrab/unregister all delete d; } bool KGlobalAccel::isEnabled() const { return d->enabled; } void KGlobalAccel::setEnabled( bool enabled ) { d->enabled = enabled; //TODO: implement this in KdedGlobalAccel... or not at all #if 0 if (enabled) { foreach (KAction* action, d->actionsWithGlobalShortcuts) checkAction(action); } else { foreach (int key, d->grabbedKeys.keys()) d->impl->grabKey(key, false); d->grabbedActions.clear(); d->grabbedKeys.clear(); } #endif } void KGlobalAccel::overrideMainComponentData(const KComponentData &kcd) { d->mainComponentName = kcd.componentName(); d->isUsingForeignComponentName = true; } KGlobalAccel *KGlobalAccel::self( ) { K_GLOBAL_STATIC(KGlobalAccel, s_instance) return s_instance; } +QList KGlobalAccelPrivate::updateGlobalShortcutInKded(KAction* action, const QStringList& actionId, uint flags, uint initialSetterFlags) +{ + uint setterFlags = initialSetterFlags; + const KShortcut defaultShortcut = action->globalShortcut(KAction::DefaultShortcut); + const KShortcut activeShortcut = action->globalShortcut(); + if (flags & KAction::NoAutoloading) + setterFlags |= KdedGlobalAccel::NoAutoloading; + if (defaultShortcut.isEmpty()) + setterFlags |= KdedGlobalAccel::IsDefaultEmpty; + if (defaultShortcut == activeShortcut) + setterFlags |= KdedGlobalAccel::IsDefault; + + const QList result = iface.setShortcut(actionId, + intListFromShortcut(activeShortcut), + setterFlags); + const KShortcut scResult(shortcutFromIntList(result)); + + if (scResult != activeShortcut) + action->d->setActiveGlobalShortcutNoEnable(scResult); + return result; +} void KGlobalAccelPrivate::updateGlobalShortcutAllowed(KAction *action, uint flags) { if (!action) return; bool oldEnabled = actionToName.contains(action); bool newEnabled = action->globalShortcutAllowed(); if (oldEnabled == newEnabled) return; if (action->text().isEmpty()) return; QStringList actionId(mainComponentName); actionId.append(action->text()); //TODO: what about i18ned names? if (!oldEnabled && newEnabled) { - uint setterFlags = KdedGlobalAccel::SetPresent; - KShortcut defaultShortcut = action->globalShortcut(KAction::DefaultShortcut); - KShortcut activeShortcut = action->globalShortcut(); - if (flags & KAction::NoAutoloading) - setterFlags |= KdedGlobalAccel::NoAutoloading; - if (defaultShortcut.isEmpty()) - setterFlags |= KdedGlobalAccel::IsDefaultEmpty; - if (defaultShortcut == activeShortcut) - setterFlags |= KdedGlobalAccel::IsDefault; - + // action is now enabled nameToAction.insert(actionId.at(1), action); actionToName.insert(action, actionId.at(1)); - QList result = iface.setShortcut(actionId, - intListFromShortcut(action->globalShortcut()), - setterFlags); - KShortcut scResult(shortcutFromIntList(result)); - - if (scResult != action->globalShortcut()) - action->d->setActiveGlobalShortcutNoEnable(scResult); + updateGlobalShortcutInKded(action, actionId, flags, KdedGlobalAccel::SetPresent); } - - if (oldEnabled && !newEnabled) { + else if (oldEnabled && !newEnabled) { + // action is now disabled nameToAction.remove(actionToName.take(action)); iface.setInactive(actionId); } } void KGlobalAccelPrivate::updateGlobalShortcut(KAction *action, uint flags) { if (!action) return; if (action->text().isEmpty()) return; QStringList actionId(mainComponentName); actionId.append(action->text()); //TODO: what about i18ned names? - uint setterFlags = 0; - KShortcut defaultShortcut = action->globalShortcut(KAction::DefaultShortcut); - KShortcut activeShortcut = action->globalShortcut(); - if (flags & KAction::NoAutoloading) - setterFlags |= KdedGlobalAccel::NoAutoloading; - if (defaultShortcut.isEmpty()) - setterFlags |= KdedGlobalAccel::IsDefaultEmpty; - if (defaultShortcut == activeShortcut) - setterFlags |= KdedGlobalAccel::IsDefault; - - QList result = iface.setShortcut(actionId, - intListFromShortcut(action->globalShortcut()), - setterFlags); - KShortcut scResult(shortcutFromIntList(result)); - - if (scResult != action->globalShortcut()) { - action->d->setActiveGlobalShortcutNoEnable(scResult); - } + const QList result = updateGlobalShortcutInKded(action, actionId, flags, 0); //We might be able to avoid that call sometimes, but it's neither worth the effort nor //the bytes to determine the cases where it's safe to avoid it. if (isUsingForeignComponentName) { iface.setForeignShortcut(actionId, result); } } QList KGlobalAccelPrivate::intListFromShortcut(const KShortcut &cut) { QList ret; ret.append(cut.primary()[0]); ret.append(cut.alternate()[0]); while (!ret.isEmpty() && ret.last() == 0) ret.removeLast(); return ret; } KShortcut KGlobalAccelPrivate::shortcutFromIntList(const QList &list) { KShortcut ret; if (list.count() > 0) ret.setPrimary(list[0]); if (list.count() > 1) ret.setAlternate(list[1]); return ret; } void KGlobalAccelPrivate::_k_invokeAction(const QStringList &actionId) { //TODO: can we make it so that we don't have to check the mainComponentName? (i.e. targeted signals) + // Well, how about making the full QStringList the key in nameToAction? if (actionId.at(0) != mainComponentName || isUsingForeignComponentName) return; KAction *action = nameToAction.value(actionId.at(1)); if (!action) return; #ifdef Q_WS_X11 // Update this application's X timestamp if needed. // TODO The 100%-correct solution should probably be handling this action // in the proper place in relation to the X events queue in order to avoid // the possibility of wrong ordering of user events. Time timestamp = actionId.at( 2 ).toULong(); if( NET::timestampCompare( timestamp, QX11Info::appTime()) > 0 ) QX11Info::setAppTime( timestamp ); if( NET::timestampCompare( timestamp, QX11Info::appUserTime()) > 0 ) QX11Info::setAppUserTime( timestamp ); #endif action->trigger(); } void KGlobalAccelPrivate::_k_shortcutGotChanged(const QStringList &actionId, const QList &keys) { KAction *action = nameToAction.value(actionId.at(1)); if (!action) return; action->d->setActiveGlobalShortcutNoEnable(shortcutFromIntList(keys)); } void KGlobalAccelPrivate::_k_serviceOwnerChanged(const QString& name, const QString& oldOwner, const QString& newOwner) { Q_UNUSED(oldOwner); if (name == QLatin1String("org.kde.kded") && !newOwner.isEmpty()) { // kded was restarted (what? you mean it crashes sometimes?) reRegisterAll(); } } void KGlobalAccelPrivate::reRegisterAll() { //### Special case for isUsingForeignComponentName? //We clear all our data, assume that all data on the other side is clear too, //and register each action as if it just was allowed to have global shortcuts. //If the kded side still has the data it doesn't matter because of the //autoloading mechanism. The worst case I can imagine is that an action's //shortcut was changed but the kded side died before it got the message so //autoloading will now assign an old shortcut to the action. Particularly //picky apps might assert or misbehave. QList allActions = actionToName.keys(); nameToAction.clear(); actionToName.clear(); foreach(KAction *const action, allActions) { updateGlobalShortcutAllowed(action, 0/*flags*/); } } //static QStringList KGlobalAccel::findActionNameSystemwide(const QKeySequence &seq) { return self()->d->iface.action(seq[0]); } //static bool KGlobalAccel::promptStealShortcutSystemwide(QWidget *parent, const QStringList &actionIdentifier, const QKeySequence &seq) { QString title = i18n("Conflict with Global Shortcut"); QString message = i18n("The '%1' key combination has already been allocated " "to the global action \"%2\" in %3.\n" "Do you want to reassign it from that action to the current one?", seq.toString(), actionIdentifier.at(1), actionIdentifier.at(0)); return KMessageBox::warningContinueCancel(parent, message, title, KGuiItem(i18n("Reassign"))) == KMessageBox::Continue; } //static void KGlobalAccel::stealShortcutSystemwide(const QKeySequence &seq) { //get the shortcut, remove seq, and set the new shorctut const QStringList actionId = self()->d->iface.action(seq[0]); if (actionId.size() < 2) // not a global shortcut return; QList sc = self()->d->iface.shortcut(actionId); for (int i = 0; i < sc.count(); i++) if (sc[i] == seq[0]) sc[i] = 0; self()->d->iface.setForeignShortcut(actionId, sc); } #include "kglobalaccel.moc" #include "kdedglobalaccel_interface.moc" diff --git a/kdeui/shortcuts/kglobalaccel_p.h b/kdeui/shortcuts/kglobalaccel_p.h index 8aa0ceb7b4..f710c15d7d 100644 --- a/kdeui/shortcuts/kglobalaccel_p.h +++ b/kdeui/shortcuts/kglobalaccel_p.h @@ -1,66 +1,69 @@ /* This file is part of the KDE libraries Copyright (C) 2001,2002 Ellis Whitehead Copyright (C) 2006 Hamish Rodda Copyright (C) 2007 Andreas Hartmetz 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. */ #ifndef KGLOBALACCEL_P_H #define KGLOBALACCEL_P_H #include #include #include "kdedglobalaccel_interface.h" class KAction; class KShortcut; class KGlobalAccelPrivate { public: KGlobalAccelPrivate(KGlobalAccel*); ///Propagate any shortcut changes to the KDED module that does the bookkeeping ///and the key grabbing. ///If this is called with an action that has an empty active global shortcut and ///an empty default shortcut, the record of that action will be deleted. void updateGlobalShortcut(KAction *action, /*KAction::ShortcutTypes*/uint flags); ///Register or unregister the action in this class, and notify the KDED module void updateGlobalShortcutAllowed(KAction *action, /*KAction::ShortcutTypes*/uint flags); + /// Helper method for the above two, takes care of the actual call to kded + QList updateGlobalShortcutInKded(KAction* action, const QStringList& actionId, uint flags, uint initialSetterFlags); + QList intListFromShortcut(const KShortcut &cut); KShortcut shortcutFromIntList(const QList &list); void _k_invokeAction(const QStringList&); void _k_shortcutGotChanged(const QStringList&, const QList&); void _k_serviceOwnerChanged(const QString& name, const QString& oldOwner, const QString& newOwner); void reRegisterAll(); //for all actions with (isEnabled() && globalShortcutAllowed()) QHash nameToAction; QHash actionToName; QString mainComponentName; bool isUsingForeignComponentName; bool enabled; org::kde::KdedGlobalAccelInterface iface; }; #endif