• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDEUI

kglobalaccel.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002     Copyright (C) 2001,2002 Ellis Whitehead <ellis@kde.org>
00003     Copyright (C) 2006 Hamish Rodda <rodda@kde.org>
00004     Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com>
00005     Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz>
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU Library General Public
00009     License as published by the Free Software Foundation; either
00010     version 2 of the License, or (at your option) any later version.
00011 
00012     This library is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     Library General Public License for more details.
00016 
00017     You should have received a copy of the GNU Library General Public License
00018     along with this library; see the file COPYING.LIB.  If not, write to
00019     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020     Boston, MA 02110-1301, USA.
00021 */
00022 
00023 #include "kglobalaccel.h"
00024 #include "kglobalaccel_p.h"
00025 
00026 #include <QtDBus/QDBusInterface>
00027 #include <QtDBus/QDBusMetaType>
00028 #ifdef Q_WS_X11
00029 #include <QtGui/QX11Info>
00030 #include <netwm_def.h>
00031 #endif
00032 
00033 #include <kdebug.h>
00034 #include <ktoolinvocation.h>
00035 #include <kaboutdata.h>
00036 #include <kcomponentdata.h>
00037 #include "kaction.h"
00038 #include "kaction_p.h"
00039 #include "kmessagebox.h"
00040 #include "kshortcut.h"
00041 
00042 
00043 KGlobalAccelPrivate::KGlobalAccelPrivate(KGlobalAccel *q)
00044      : isUsingForeignComponentName(false),
00045        enabled(true),
00046        iface("org.kde.kded", "/modules/kdedglobalaccel", QDBusConnection::sessionBus())
00047 {
00048     // Make sure kded is running
00049     QDBusConnectionInterface* bus = QDBusConnection::sessionBus().interface();
00050     if (!bus->isServiceRegistered("org.kde.kded")) {
00051         KToolInvocation::klauncher(); // this calls startKdeinit
00052     }
00053     QObject::connect(bus, SIGNAL(serviceOwnerChanged(QString, QString, QString)),
00054                      q, SLOT(_k_serviceOwnerChanged(QString, QString, QString)));
00055 }
00056 
00057 void KGlobalAccelPrivate::readComponentData(const KComponentData &componentData)
00058 {
00059     Q_ASSERT(!componentData.componentName().isEmpty());
00060 
00061     mainComponent = componentData;
00062     if (componentData.aboutData()->programName().isEmpty()) {
00063         kDebug(123) << componentData.componentName() << " has empty programName()";
00064     }
00065 }
00066 
00067 
00068 KGlobalAccel::KGlobalAccel()
00069     : d(new KGlobalAccelPrivate(this))
00070 {
00071     qDBusRegisterMetaType<QList<int> >();
00072     qDBusRegisterMetaType<QList<QStringList> >();
00073     qDBusRegisterMetaType<KGlobalShortcutInfo>();
00074     qDBusRegisterMetaType<QList<KGlobalShortcutInfo> >();
00075 
00076     connect(&d->iface, SIGNAL(invokeAction(const QStringList &, qlonglong)),
00077             SLOT(_k_invokeAction(const QStringList &, qlonglong)));
00078     connect(&d->iface, SIGNAL(yourShortcutGotChanged(const QStringList &, const QList<int> &)),
00079             SLOT(_k_shortcutGotChanged(const QStringList &, const QList<int> &)));
00080 
00081     if (KGlobal::hasMainComponent()) {
00082         d->readComponentData( KGlobal::mainComponent() );
00083     }
00084 
00085 }
00086 
00087 
00088 KGlobalAccel::~KGlobalAccel()
00089 {
00090     delete d;
00091 }
00092 
00093 
00094 void KGlobalAccel::activateGlobalShortcutContext(
00095         const QString &contextUnique,
00096         const QString &contextFriendly,
00097         const KComponentData &component)
00098 {
00099     // TODO provide contextFriendly
00100     self()->d->iface.activateGlobalShortcutContext(component.aboutData()->programName(), contextUnique);
00101 }
00102 
00103 
00104 bool KGlobalAccel::isEnabled() const
00105 {
00106     return d->enabled;
00107 }
00108 
00109 
00110 void KGlobalAccel::setEnabled(bool enabled)
00111 {
00112     d->enabled = enabled;
00113 }
00114 
00115 
00116 void KGlobalAccel::overrideMainComponentData(const KComponentData &kcd)
00117 {
00118     d->readComponentData(kcd);
00119     d->isUsingForeignComponentName = true;
00120 }
00121 
00122 
00123 KGlobalAccel *KGlobalAccel::self()
00124 {
00125     K_GLOBAL_STATIC(KGlobalAccel, s_instance)
00126     return s_instance;
00127 }
00128 
00129 
00130 void KGlobalAccelPrivate::doRegister(KAction *action)
00131 {
00132     if (!action || action->objectName().isEmpty()) {
00133         return;
00134     }
00135 
00136     const bool isRegistered = actions.contains(action);
00137     if (isRegistered)
00138         return;
00139 
00140     // Under configuration mode - deprecated - we ignore the component given
00141     // from the action and use our own.
00142     if (isUsingForeignComponentName) {
00143         action->d->componentData = mainComponent;
00144     }
00145     QStringList actionId = makeActionId(action);
00146 
00147     nameToAction.insertMulti(actionId.at(KGlobalAccel::ActionUnique), action);
00148     actions.insert(action);
00149     iface.doRegister(actionId);
00150 }
00151 
00152 
00153 void KGlobalAccelPrivate::remove(KAction *action, Removal removal)
00154 {
00155     if (!action  || action->objectName().isEmpty()) {
00156         return;
00157     }
00158 
00159     const bool isRegistered = actions.contains(action);
00160     if (!isRegistered) {
00161         return;
00162     }
00163 
00164     QStringList actionId = makeActionId(action);
00165 
00166     nameToAction.remove(actionId.at(KGlobalAccel::ActionUnique), action);
00167     actions.remove(action);
00168 
00169     if (removal == UnRegister) {
00170         // Complete removal of the shortcut is requested
00171         // (forgetGlobalShortcut)
00172         iface.unRegister(actionId);
00173     } else {
00174         // If the action is a configurationAction wen only remove it from our
00175         // internal registry. That happened above.
00176         if (!action->property("isConfigurationAction").toBool()) {
00177             // If it's a session shortcut unregister it.
00178             action->objectName().startsWith("_k_session:")
00179                 ? iface.unRegister(actionId)
00180                 : iface.setInactive(actionId);
00181         }
00182     }
00183 }
00184 
00185 
00186 void KGlobalAccelPrivate::updateGlobalShortcut(KAction *action, uint flags)
00187 {
00188     // No action or no objectname -> Do nothing
00189     // KAction::setGlobalShortcut informs the user
00190     if (!action || action->objectName().isEmpty()) {
00191         return;
00192     }
00193 
00194     QStringList actionId = makeActionId(action);
00195     const KShortcut activeShortcut = action->globalShortcut();
00196     const KShortcut defaultShortcut = action->globalShortcut(KAction::DefaultShortcut);
00197 
00198     uint setterFlags = 0;
00199     if (flags & KAction::NoAutoloading) {
00200         setterFlags |= NoAutoloading;
00201     }
00202 
00203     if (flags & KAction::ActiveShortcut) {
00204         bool isConfigurationAction = isUsingForeignComponentName
00205             || action->property("isConfigurationAction").toBool();
00206         uint activeSetterFlags = setterFlags;
00207 
00208         // setPresent tells kdedglobalaccel that the shortcut is active
00209         if (!isConfigurationAction) {
00210             activeSetterFlags |= SetPresent;
00211         }
00212 
00213         // Sets the shortcut, returns the active/real keys
00214         const QList<int> result = iface.setShortcut(
00215                 actionId,
00216                 intListFromShortcut(activeShortcut),
00217                 activeSetterFlags);
00218         // Create a shortcut from the result
00219         const KShortcut scResult(shortcutFromIntList(result));
00220 
00221         if (isConfigurationAction && (flags & KAction::NoAutoloading)) {
00222             // If this is a configuration action and we have set the shortcut,
00223             // inform the real owner of the change.
00224             // Note that setForeignShortcut will cause a signal to be sent to applications
00225             // even if it did not "see" that the shortcut has changed. This is Good because
00226             // at the time of comparison (now) the action *already has* the new shortcut.
00227             // We called setShortcut(), remember?
00228             // Also note that we will see our own signal so we may not need to call
00229             // setActiveGlobalShortcutNoEnable - _k_shortcutGotChanged() does it.
00230             // In practice it's probably better to get the change propagated here without
00231             // DBus delay as we do below.
00232             iface.setForeignShortcut(actionId, result);
00233         }
00234         if (scResult != activeShortcut) {
00235             // If kdedglobalaccel returned a shortcut that differs from the one we
00236             // sent, use that one. There must have been clashes or some other problem.
00237             action->d->setActiveGlobalShortcutNoEnable(scResult);
00238         }
00239     }
00240 
00241     if (flags & KAction::DefaultShortcut) {
00242         iface.setShortcut(actionId, intListFromShortcut(defaultShortcut),
00243                           setterFlags | IsDefault);
00244     }
00245 }
00246 
00247 
00248 QStringList KGlobalAccelPrivate::makeActionId(const KAction *action)
00249 {
00250     QStringList ret(componentUniqueForAction(action));  // Component Unique Id ( see actionIdFields )
00251     Q_ASSERT(!ret.at(KGlobalAccel::ComponentUnique).isEmpty());
00252     Q_ASSERT(!action->objectName().isEmpty());
00253     ret.append(action->objectName());                   // Action Unique Name
00254     ret.append(componentFriendlyForAction(action));     // Component Friendly name
00255     ret.append(action->text());                         // Action Friendly Name
00256     return ret;
00257 }
00258 
00259 
00260 QList<int> KGlobalAccelPrivate::intListFromShortcut(const KShortcut &cut)
00261 {
00262     QList<int> ret;
00263     ret.append(cut.primary()[0]);
00264     ret.append(cut.alternate()[0]);
00265     while (!ret.isEmpty() && ret.last() == 0)
00266         ret.removeLast();
00267     return ret;
00268 }
00269 
00270 
00271 KShortcut KGlobalAccelPrivate::shortcutFromIntList(const QList<int> &list)
00272 {
00273     KShortcut ret;
00274     if (list.count() > 0)
00275         ret.setPrimary(list[0]);
00276     if (list.count() > 1)
00277         ret.setAlternate(list[1]);
00278     return ret;
00279 }
00280 
00281 
00282 QString KGlobalAccelPrivate::componentUniqueForAction(const KAction *action)
00283 {
00284     Q_ASSERT(action->d->componentData.isValid());
00285     return action->d->componentData.componentName();
00286 }
00287 
00288 
00289 QString KGlobalAccelPrivate::componentFriendlyForAction(const KAction *action)
00290 {
00291     Q_ASSERT(action->d->componentData.isValid());
00292     return action->d->componentData.aboutData()->programName();
00293 }
00294 
00295 
00296 void KGlobalAccelPrivate::_k_invokeAction(const QStringList &actionId, qlonglong timestamp)
00297 {
00298     // If overrideMainComponentData() is active the app can only have
00299     // configuration actions.
00300     if (isUsingForeignComponentName ) {
00301         return;
00302     }
00303 
00304     KAction *action = 0;
00305     QList<KAction *> candidates = nameToAction.values(actionId.at(KGlobalAccel::ActionUnique));
00306     foreach (KAction *const a, candidates) {
00307         if (componentUniqueForAction(a) == actionId.at(KGlobalAccel::ComponentUnique)) {
00308             action = a;
00309         }
00310     }
00311 
00312     // We do not trigger if
00313     // - there is no action
00314     // - the action is not enabled
00315     // - the action is an configuration action
00316     if (!action || !action->isEnabled()
00317             || action->property("isConfigurationAction").toBool()) {
00318         return;
00319     }
00320 
00321 #ifdef Q_WS_X11
00322     // Update this application's X timestamp if needed.
00323     // TODO The 100%-correct solution should probably be handling this action
00324     // in the proper place in relation to the X events queue in order to avoid
00325     // the possibility of wrong ordering of user events.
00326     if( NET::timestampCompare(timestamp, QX11Info::appTime()) > 0)
00327         QX11Info::setAppTime(timestamp);
00328     if( NET::timestampCompare(timestamp, QX11Info::appUserTime()) > 0)
00329         QX11Info::setAppUserTime(timestamp);
00330 #else
00331     Q_UNUSED(timestamp);
00332 #endif
00333 
00334     action->trigger();
00335 }
00336 
00337 void KGlobalAccelPrivate::_k_shortcutGotChanged(const QStringList &actionId,
00338                                                 const QList<int> &keys)
00339 {
00340     KAction *action = nameToAction.value(actionId.at(KGlobalAccel::ActionUnique));
00341     if (!action)
00342         return;
00343 
00344     action->d->setActiveGlobalShortcutNoEnable(shortcutFromIntList(keys));
00345 }
00346 
00347 void KGlobalAccelPrivate::_k_serviceOwnerChanged(const QString &name, const QString &oldOwner,
00348                                                  const QString &newOwner)
00349 {
00350     Q_UNUSED(oldOwner);
00351     if (name == QLatin1String("org.kde.kded") && !newOwner.isEmpty()) {
00352         // kded was restarted (what? you mean it crashes sometimes?)
00353         reRegisterAll();
00354     }
00355 }
00356 
00357 void KGlobalAccelPrivate::reRegisterAll()
00358 {
00359     //### Special case for isUsingForeignComponentName?
00360 
00361     //We clear all our data, assume that all data on the other side is clear too,
00362     //and register each action as if it just was allowed to have global shortcuts.
00363     //If the kded side still has the data it doesn't matter because of the
00364     //autoloading mechanism. The worst case I can imagine is that an action's
00365     //shortcut was changed but the kded side died before it got the message so
00366     //autoloading will now assign an old shortcut to the action. Particularly
00367     //picky apps might assert or misbehave.
00368     QSet<KAction *> allActions = actions;
00369     nameToAction.clear();
00370     actions.clear();
00371     foreach(KAction *const action, allActions) {
00372         doRegister(action);
00373         updateGlobalShortcut(action, KAction::Autoloading | KAction::ActiveShortcut);
00374     }
00375 }
00376 
00377 
00378 QList<QStringList> KGlobalAccel::allMainComponents()
00379 {
00380     return d->iface.allMainComponents();
00381 }
00382 
00383 
00384 QList<QStringList> KGlobalAccel::allActionsForComponent(const QStringList &actionId)
00385 {
00386     return d->iface.allActionsForComponent(actionId);
00387 }
00388 
00389 
00390 //static
00391 QStringList KGlobalAccel::findActionNameSystemwide(const QKeySequence &seq)
00392 {
00393     return self()->d->iface.action(seq[0]);
00394 }
00395 
00396 
00397 QList<KGlobalShortcutInfo> KGlobalAccel::getGlobalShortcutsByKey(const QKeySequence &seq)
00398 {
00399     return self()->d->iface.getGlobalShortcutsByKey(seq[0]);
00400 }
00401 
00402 
00403 bool KGlobalAccel::isGlobalShortcutAvailable(const QKeySequence &seq, const QString &comp)
00404 {
00405         return self()->d->iface.isGlobalShortcutAvailable(seq[0], comp);
00406 }
00407 
00408 
00409 //static
00410 bool KGlobalAccel::promptStealShortcutSystemwide(QWidget *parent, const QStringList &actionIdentifier,
00411                                                  const QKeySequence &seq)
00412 {
00413     if (actionIdentifier.size() < 4) {
00414         return false;
00415     }
00416     QString title = i18n("Conflict with Global Shortcut");
00417     QString message = i18n("The '%1' key combination has already been allocated "
00418                            "to the global action \"%2\" in %3.\n"
00419                            "Do you want to reassign it from that action to the current one?",
00420                            seq.toString(), actionIdentifier.at(KGlobalAccel::ActionFriendly),
00421                            actionIdentifier.at(KGlobalAccel::ComponentFriendly));
00422 
00423     return KMessageBox::warningContinueCancel(parent, message, title, KGuiItem(i18n("Reassign")))
00424            == KMessageBox::Continue;
00425 }
00426 
00427 
00428 //static
00429 bool KGlobalAccel::promptStealShortcutSystemwide(
00430         QWidget *parent,
00431         const QList<KGlobalShortcutInfo> &shortcuts,
00432         const QKeySequence &seq)
00433 {
00434     if (shortcuts.isEmpty()) {
00435         // Usage error. Just say no
00436         return false;
00437     }
00438 
00439     QString component = shortcuts[0].componentFriendlyName();
00440 
00441     QString message;
00442     if (shortcuts.size()==1) {
00443         message = i18n("The '%1' key combination is registered by application %2 for action %3:",
00444                 seq.toString(),
00445                 component,
00446                 shortcuts[0].friendlyName());
00447     } else {
00448         QString actionList;
00449         Q_FOREACH(const KGlobalShortcutInfo &info, shortcuts) {
00450             actionList += i18n("In context '%1' for action '%2'\n",
00451                     info.contextFriendlyName(),
00452                     info.friendlyName());
00453         }
00454         message = i18n("The '%1' key combination is registered by application %2.\n%3",
00455                            seq.toString(),
00456                            component,
00457                            actionList);
00458     }
00459 
00460     QString title = i18n("Conflict With Registered Global Shortcut");
00461 
00462     return KMessageBox::warningContinueCancel(parent, message, title, KGuiItem(i18n("Reassign")))
00463            == KMessageBox::Continue;
00464 }
00465 
00466 
00467 //static
00468 void KGlobalAccel::stealShortcutSystemwide(const QKeySequence &seq)
00469 {
00470     //get the shortcut, remove seq, and set the new shorctut
00471     const QStringList actionId = self()->d->iface.action(seq[0]);
00472     if (actionId.size() < 4) // not a global shortcut
00473         return;
00474     QList<int> sc = self()->d->iface.shortcut(actionId);
00475 
00476     for (int i = 0; i < sc.count(); i++)
00477         if (sc[i] == seq[0])
00478             sc[i] = 0;
00479 
00480     self()->d->iface.setForeignShortcut(actionId, sc);
00481 }
00482 
00483 #include "kglobalaccel.moc"
00484 

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal