• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • 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 <memory>
00027 
00028 #include <QtDBus/QDBusInterface>
00029 #include <QtDBus/QDBusMetaType>
00030 #ifdef Q_WS_X11
00031 #include <QtGui/QX11Info>
00032 #include <netwm_def.h>
00033 #endif
00034 
00035 #include <kdebug.h>
00036 #include <ktoolinvocation.h>
00037 #include <kaboutdata.h>
00038 #include <kcomponentdata.h>
00039 #include "kaction.h"
00040 #include "kaction_p.h"
00041 #include "kmessagebox.h"
00042 #include "kshortcut.h"
00043 
00044 org::kde::kglobalaccel::Component *KGlobalAccelPrivate::getComponent(const QString &componentUnique, bool remember = false)
00045 {
00046     // Check if we already have this component
00047     if (components.contains(componentUnique)) {
00048         return components[componentUnique];
00049     }
00050 
00051     // Connect to the kglobalaccel daemon
00052     org::kde::KGlobalAccel kglobalaccel(
00053             "org.kde.kglobalaccel",
00054             "/kglobalaccel",
00055             QDBusConnection::sessionBus());
00056     if (!kglobalaccel.isValid()) {
00057         kDebug() << "Failed to connect to the kglobalaccel daemon" << QDBusConnection::sessionBus().lastError();
00058         return NULL;
00059     }
00060 
00061     // Get the path for our component. We have to do that because
00062     // componentUnique is probably not a valid dbus object path
00063     QDBusReply<QDBusObjectPath> reply = kglobalaccel.getComponent(componentUnique);
00064     if (!reply.isValid()) {
00065 
00066         if (reply.error().name() == "org.kde.kglobalaccel.NoSuchComponent") {
00067             // No problem. The component doesn't exists. That's normal
00068             return NULL;
00069         }
00070 
00071         // An unknown error.
00072         kDebug() << "Failed to get dbus path for component " << componentUnique << reply.error();
00073         return NULL;
00074     }
00075 
00076     // Now get the component
00077     org::kde::kglobalaccel::Component *component = new org::kde::kglobalaccel::Component(
00078         "org.kde.kglobalaccel",
00079         reply.value().path(),
00080         QDBusConnection::sessionBus(),
00081         q);
00082 
00083     // No component no cleaning
00084     if (!component->isValid()) {
00085         kDebug() << "Failed to get component" << componentUnique << QDBusConnection::sessionBus().lastError();
00086         return NULL;
00087     }
00088 
00089     if (remember)
00090         {
00091         // Connect to the signals we are interested in.
00092         q->connect(component, SIGNAL(globalShortcutPressed(const QString &, const QString &, qlonglong)),
00093                 SLOT(_k_invokeAction(const QString &, const QString &, qlonglong)));
00094 
00095         components[componentUnique] = component;
00096         }
00097 
00098     return component;
00099 }
00100 
00101 
00102 
00103 KGlobalAccelPrivate::KGlobalAccelPrivate(KGlobalAccel *q)
00104      :  isUsingForeignComponentName(false),
00105 #ifndef KDE_NO_DEPRECATED
00106         enabled(true),
00107 #endif
00108         iface("org.kde.kglobalaccel", "/kglobalaccel", QDBusConnection::sessionBus()),
00109         q(q)
00110 {
00111     // Make sure kded is running. The iface declaration above somehow
00112     // works anyway.
00113     QDBusConnectionInterface* bus = QDBusConnection::sessionBus().interface();
00114     if (!bus->isServiceRegistered("org.kde.kglobalaccel")) {
00115         QString error;
00116         int ret = KToolInvocation::startServiceByDesktopPath(
00117                 "kglobalaccel.desktop",
00118                 QStringList(),
00119                 &error);
00120 
00121         if (ret > 0) {
00122             kError() << "Couldn't start kglobalaccel from kglobalaccel.desktop: " << error << endl;
00123         }
00124     }
00125     QDBusServiceWatcher *watcher = new QDBusServiceWatcher(iface.service(),
00126                                                            QDBusConnection::sessionBus(),
00127                                                            QDBusServiceWatcher::WatchForOwnerChange,
00128                                                            q);
00129     q->connect(watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
00130                      q, SLOT(_k_serviceOwnerChanged(QString,QString,QString)));
00131 }
00132 
00133 
00134 void KGlobalAccelPrivate::readComponentData(const KComponentData &componentData)
00135 {
00136     Q_ASSERT(!componentData.componentName().isEmpty());
00137 
00138     mainComponent = componentData;
00139     if (componentData.aboutData()->programName().isEmpty()) {
00140         kDebug(123) << componentData.componentName() << " has empty programName()";
00141     }
00142 }
00143 
00144 
00145 KGlobalAccel::KGlobalAccel()
00146     : d(new KGlobalAccelPrivate(this))
00147 {
00148     qDBusRegisterMetaType<QList<int> >();
00149     qDBusRegisterMetaType<QList<QStringList> >();
00150     qDBusRegisterMetaType<KGlobalShortcutInfo>();
00151     qDBusRegisterMetaType<QList<KGlobalShortcutInfo> >();
00152 
00153     connect(&d->iface, SIGNAL(yourShortcutGotChanged(const QStringList &, const QList<int> &)),
00154             SLOT(_k_shortcutGotChanged(const QStringList &, const QList<int> &)));
00155 
00156     if (KGlobal::hasMainComponent()) {
00157         d->readComponentData( KGlobal::mainComponent() );
00158     }
00159 
00160 }
00161 
00162 
00163 KGlobalAccel::~KGlobalAccel()
00164 {
00165     delete d;
00166 }
00167 
00168 
00169 void KGlobalAccel::activateGlobalShortcutContext(
00170         const QString &contextUnique,
00171         const QString &contextFriendly,
00172         const KComponentData &component)
00173 {
00174     Q_UNUSED(contextFriendly);
00175     // TODO: provide contextFriendly
00176     self()->d->iface.activateGlobalShortcutContext(component.aboutData()->programName(), contextUnique);
00177 }
00178 
00179 
00180 // static
00181 bool KGlobalAccel::cleanComponent(const QString &componentUnique)
00182 {
00183     org::kde::kglobalaccel::Component* component = self()->getComponent(componentUnique);
00184     if (!component) return false;
00185 
00186     return component->cleanUp();
00187 }
00188 
00189 
00190 // static
00191 bool KGlobalAccel::isComponentActive(const QString &componentUnique)
00192 {
00193     org::kde::kglobalaccel::Component* component = self()->getComponent(componentUnique);
00194     if (!component) return false;
00195 
00196     return component->isActive();
00197 }
00198 
00199 
00200 #ifndef KDE_NO_DEPRECATED
00201 bool KGlobalAccel::isEnabled() const
00202 {
00203     return d->enabled;
00204 }
00205 #endif
00206 
00207 
00208 org::kde::kglobalaccel::Component *KGlobalAccel::getComponent(const QString &componentUnique)
00209 {
00210     return d->getComponent(componentUnique);
00211 }
00212 
00213 
00214 #ifndef KDE_NO_DEPRECATED
00215 void KGlobalAccel::setEnabled(bool enabled)
00216 {
00217     d->enabled = enabled;
00218 }
00219 #endif
00220 
00221 
00222 #ifndef KDE_NO_DEPRECATED
00223 void KGlobalAccel::overrideMainComponentData(const KComponentData &kcd)
00224 {
00225     d->readComponentData(kcd);
00226     d->isUsingForeignComponentName = true;
00227 }
00228 #endif
00229 
00230 
00231 KGlobalAccel *KGlobalAccel::self()
00232 {
00233     K_GLOBAL_STATIC(KGlobalAccel, s_instance)
00234     return s_instance;
00235 }
00236 
00237 
00238 void KGlobalAccelPrivate::doRegister(KAction *action)
00239 {
00240     if (!action || action->objectName().isEmpty()) {
00241         return;
00242     }
00243 
00244     const bool isRegistered = actions.contains(action);
00245     if (isRegistered)
00246         return;
00247 
00248     // Under configuration mode - deprecated - we ignore the component given
00249     // from the action and use our own.
00250     if (isUsingForeignComponentName) {
00251         action->d->componentData = mainComponent;
00252     }
00253     QStringList actionId = makeActionId(action);
00254 
00255     nameToAction.insertMulti(actionId.at(KGlobalAccel::ActionUnique), action);
00256     actions.insert(action);
00257     iface.doRegister(actionId);
00258 }
00259 
00260 
00261 void KGlobalAccelPrivate::remove(KAction *action, Removal removal)
00262 {
00263     if (!action  || action->objectName().isEmpty()) {
00264         return;
00265     }
00266 
00267     const bool isRegistered = actions.contains(action);
00268     if (!isRegistered) {
00269         return;
00270     }
00271 
00272     QStringList actionId = makeActionId(action);
00273 
00274     nameToAction.remove(actionId.at(KGlobalAccel::ActionUnique), action);
00275     actions.remove(action);
00276 
00277     if (removal == UnRegister) {
00278         // Complete removal of the shortcut is requested
00279         // (forgetGlobalShortcut)
00280         iface.unRegister(actionId);
00281     } else {
00282         // If the action is a configurationAction wen only remove it from our
00283         // internal registry. That happened above.
00284         if (!action->property("isConfigurationAction").toBool()) {
00285             // If it's a session shortcut unregister it.
00286             action->objectName().startsWith(QLatin1String("_k_session:"))
00287                 ? iface.unRegister(actionId)
00288                 : iface.setInactive(actionId);
00289         }
00290     }
00291 }
00292 
00293 
00294 void KGlobalAccelPrivate::updateGlobalShortcut(KAction *action, uint flags)
00295 {
00296     // No action or no objectname -> Do nothing
00297     // KAction::setGlobalShortcut informs the user
00298     if (!action || action->objectName().isEmpty()) {
00299         return;
00300     }
00301 
00302     QStringList actionId = makeActionId(action);
00303     const KShortcut activeShortcut = action->globalShortcut();
00304     const KShortcut defaultShortcut = action->globalShortcut(KAction::DefaultShortcut);
00305 
00306     uint setterFlags = 0;
00307     if (flags & KAction::NoAutoloading) {
00308         setterFlags |= NoAutoloading;
00309     }
00310 
00311     if (flags & KAction::ActiveShortcut) {
00312         bool isConfigurationAction = isUsingForeignComponentName
00313             || action->property("isConfigurationAction").toBool();
00314         uint activeSetterFlags = setterFlags;
00315 
00316         // setPresent tells kglobalaccel that the shortcut is active
00317         if (!isConfigurationAction) {
00318             activeSetterFlags |= SetPresent;
00319         }
00320 
00321         // Sets the shortcut, returns the active/real keys
00322         const QList<int> result = iface.setShortcut(
00323                 actionId,
00324                 intListFromShortcut(activeShortcut),
00325                 activeSetterFlags);
00326 
00327         // Make sure we get informed about changes in the component by kglobalaccel
00328         getComponent(componentUniqueForAction(action), true);
00329 
00330         // Create a shortcut from the result
00331         const KShortcut scResult(shortcutFromIntList(result));
00332 
00333         if (isConfigurationAction && (flags & KAction::NoAutoloading)) {
00334             // If this is a configuration action and we have set the shortcut,
00335             // inform the real owner of the change.
00336             // Note that setForeignShortcut will cause a signal to be sent to applications
00337             // even if it did not "see" that the shortcut has changed. This is Good because
00338             // at the time of comparison (now) the action *already has* the new shortcut.
00339             // We called setShortcut(), remember?
00340             // Also note that we will see our own signal so we may not need to call
00341             // setActiveGlobalShortcutNoEnable - _k_shortcutGotChanged() does it.
00342             // In practice it's probably better to get the change propagated here without
00343             // DBus delay as we do below.
00344             iface.setForeignShortcut(actionId, result);
00345         }
00346         if (scResult != activeShortcut) {
00347             // If kglobalaccel returned a shortcut that differs from the one we
00348             // sent, use that one. There must have been clashes or some other problem.
00349             action->d->setActiveGlobalShortcutNoEnable(scResult);
00350         }
00351     }
00352 
00353     if (flags & KAction::DefaultShortcut) {
00354         iface.setShortcut(actionId, intListFromShortcut(defaultShortcut),
00355                           setterFlags | IsDefault);
00356     }
00357 }
00358 
00359 
00360 QStringList KGlobalAccelPrivate::makeActionId(const KAction *action)
00361 {
00362     QStringList ret(componentUniqueForAction(action));  // Component Unique Id ( see actionIdFields )
00363     Q_ASSERT(!ret.at(KGlobalAccel::ComponentUnique).isEmpty());
00364     Q_ASSERT(!action->objectName().isEmpty());
00365     ret.append(action->objectName());                   // Action Unique Name
00366     ret.append(componentFriendlyForAction(action));     // Component Friendly name
00367     const QString actionText = KGlobal::locale()->removeAcceleratorMarker(action->text());
00368     ret.append(actionText);                             // Action Friendly Name
00369     return ret;
00370 }
00371 
00372 
00373 QList<int> KGlobalAccelPrivate::intListFromShortcut(const KShortcut &cut)
00374 {
00375     QList<int> ret;
00376     ret.append(cut.primary()[0]);
00377     ret.append(cut.alternate()[0]);
00378     while (!ret.isEmpty() && ret.last() == 0)
00379         ret.removeLast();
00380     return ret;
00381 }
00382 
00383 
00384 KShortcut KGlobalAccelPrivate::shortcutFromIntList(const QList<int> &list)
00385 {
00386     KShortcut ret;
00387     if (list.count() > 0)
00388         ret.setPrimary(list[0]);
00389     if (list.count() > 1)
00390         ret.setAlternate(list[1]);
00391     return ret;
00392 }
00393 
00394 
00395 QString KGlobalAccelPrivate::componentUniqueForAction(const KAction *action)
00396 {
00397     Q_ASSERT(action->d->componentData.isValid());
00398     return action->d->componentData.componentName();
00399 }
00400 
00401 
00402 QString KGlobalAccelPrivate::componentFriendlyForAction(const KAction *action)
00403 {
00404     Q_ASSERT(action->d->componentData.isValid());
00405     return action->d->componentData.aboutData()->programName();
00406 }
00407 
00408 
00409 void KGlobalAccelPrivate::_k_invokeAction(
00410         const QString &componentUnique,
00411         const QString &actionUnique,
00412         qlonglong timestamp)
00413 {
00414     // If overrideMainComponentData() is active the app can only have
00415     // configuration actions.
00416     if (isUsingForeignComponentName ) {
00417         return;
00418     }
00419 
00420     KAction *action = 0;
00421     QList<KAction *> candidates = nameToAction.values(actionUnique);
00422     foreach (KAction *const a, candidates) {
00423         if (componentUniqueForAction(a) == componentUnique) {
00424             action = a;
00425         }
00426     }
00427 
00428     // We do not trigger if
00429     // - there is no action
00430     // - the action is not enabled
00431     // - the action is an configuration action
00432     if (!action || !action->isEnabled() || action->property("isConfigurationAction").toBool()) {
00433         return;
00434     }
00435 
00436 #ifdef Q_WS_X11
00437     // Update this application's X timestamp if needed.
00438     // TODO The 100%-correct solution should probably be handling this action
00439     // in the proper place in relation to the X events queue in order to avoid
00440     // the possibility of wrong ordering of user events.
00441     if( NET::timestampCompare(timestamp, QX11Info::appTime()) > 0)
00442         QX11Info::setAppTime(timestamp);
00443     if( NET::timestampCompare(timestamp, QX11Info::appUserTime()) > 0)
00444         QX11Info::setAppUserTime(timestamp);
00445 #else
00446     Q_UNUSED(timestamp);
00447 #endif
00448 
00449     action->trigger();
00450 }
00451 
00452 
00453 void KGlobalAccelPrivate::_k_shortcutGotChanged(const QStringList &actionId,
00454                                                 const QList<int> &keys)
00455 {
00456     KAction *action = nameToAction.value(actionId.at(KGlobalAccel::ActionUnique));
00457     if (!action)
00458         return;
00459 
00460     action->d->setActiveGlobalShortcutNoEnable(shortcutFromIntList(keys));
00461 }
00462 
00463 void KGlobalAccelPrivate::_k_serviceOwnerChanged(const QString &name, const QString &oldOwner,
00464                                                  const QString &newOwner)
00465 {
00466     Q_UNUSED(oldOwner);
00467     if (name == QLatin1String("org.kde.kglobalaccel") && !newOwner.isEmpty()) {
00468         // kglobalaccel was restarted
00469         kDebug(123) << "detected kglobalaccel restarting, re-registering all shortcut keys";
00470         reRegisterAll();
00471     }
00472 }
00473 
00474 void KGlobalAccelPrivate::reRegisterAll()
00475 {
00476     //### Special case for isUsingForeignComponentName?
00477 
00478     //We clear all our data, assume that all data on the other side is clear too,
00479     //and register each action as if it just was allowed to have global shortcuts.
00480     //If the kded side still has the data it doesn't matter because of the
00481     //autoloading mechanism. The worst case I can imagine is that an action's
00482     //shortcut was changed but the kded side died before it got the message so
00483     //autoloading will now assign an old shortcut to the action. Particularly
00484     //picky apps might assert or misbehave.
00485     QSet<KAction *> allActions = actions;
00486     nameToAction.clear();
00487     actions.clear();
00488     foreach(KAction *const action, allActions) {
00489         doRegister(action);
00490         updateGlobalShortcut(action, KAction::Autoloading | KAction::ActiveShortcut);
00491     }
00492 }
00493 
00494 
00495 #ifndef KDE_NO_DEPRECATED
00496 QList<QStringList> KGlobalAccel::allMainComponents()
00497 {
00498     return d->iface.allMainComponents();
00499 }
00500 #endif
00501 
00502 
00503 #ifndef KDE_NO_DEPRECATED
00504 QList<QStringList> KGlobalAccel::allActionsForComponent(const QStringList &actionId)
00505 {
00506     return d->iface.allActionsForComponent(actionId);
00507 }
00508 #endif
00509 
00510 
00511 //static
00512 #ifndef KDE_NO_DEPRECATED
00513 QStringList KGlobalAccel::findActionNameSystemwide(const QKeySequence &seq)
00514 {
00515     return self()->d->iface.action(seq[0]);
00516 }
00517 #endif
00518 
00519 
00520 QList<KGlobalShortcutInfo> KGlobalAccel::getGlobalShortcutsByKey(const QKeySequence &seq)
00521 {
00522     return self()->d->iface.getGlobalShortcutsByKey(seq[0]);
00523 }
00524 
00525 
00526 bool KGlobalAccel::isGlobalShortcutAvailable(const QKeySequence &seq, const QString &comp)
00527 {
00528         return self()->d->iface.isGlobalShortcutAvailable(seq[0], comp);
00529 }
00530 
00531 
00532 //static
00533 #ifndef KDE_NO_DEPRECATED
00534 bool KGlobalAccel::promptStealShortcutSystemwide(QWidget *parent, const QStringList &actionIdentifier,
00535                                                  const QKeySequence &seq)
00536 {
00537     if (actionIdentifier.size() < 4) {
00538         return false;
00539     }
00540     QString title = i18n("Conflict with Global Shortcut");
00541     QString message = i18n("The '%1' key combination has already been allocated "
00542                            "to the global action \"%2\" in %3.\n"
00543                            "Do you want to reassign it from that action to the current one?",
00544                            seq.toString(), actionIdentifier.at(KGlobalAccel::ActionFriendly),
00545                            actionIdentifier.at(KGlobalAccel::ComponentFriendly));
00546 
00547     return KMessageBox::warningContinueCancel(parent, message, title, KGuiItem(i18n("Reassign")))
00548            == KMessageBox::Continue;
00549 }
00550 #endif
00551 
00552 
00553 //static
00554 bool KGlobalAccel::promptStealShortcutSystemwide(
00555         QWidget *parent,
00556         const QList<KGlobalShortcutInfo> &shortcuts,
00557         const QKeySequence &seq)
00558 {
00559     if (shortcuts.isEmpty()) {
00560         // Usage error. Just say no
00561         return false;
00562     }
00563 
00564     QString component = shortcuts[0].componentFriendlyName();
00565 
00566     QString message;
00567     if (shortcuts.size()==1) {
00568         message = i18n("The '%1' key combination is registered by application %2 for action %3:",
00569                 seq.toString(),
00570                 component,
00571                 shortcuts[0].friendlyName());
00572     } else {
00573         QString actionList;
00574         Q_FOREACH(const KGlobalShortcutInfo &info, shortcuts) {
00575             actionList += i18n("In context '%1' for action '%2'\n",
00576                     info.contextFriendlyName(),
00577                     info.friendlyName());
00578         }
00579         message = i18n("The '%1' key combination is registered by application %2.\n%3",
00580                            seq.toString(),
00581                            component,
00582                            actionList);
00583     }
00584 
00585     QString title = i18n("Conflict With Registered Global Shortcut");
00586 
00587     return KMessageBox::warningContinueCancel(parent, message, title, KGuiItem(i18n("Reassign")))
00588            == KMessageBox::Continue;
00589 }
00590 
00591 
00592 //static
00593 void KGlobalAccel::stealShortcutSystemwide(const QKeySequence &seq)
00594 {
00595     //get the shortcut, remove seq, and set the new shortcut
00596     const QStringList actionId = self()->d->iface.action(seq[0]);
00597     if (actionId.size() < 4) // not a global shortcut
00598         return;
00599     QList<int> sc = self()->d->iface.shortcut(actionId);
00600 
00601     for (int i = 0; i < sc.count(); i++)
00602         if (sc[i] == seq[0])
00603             sc[i] = 0;
00604 
00605     self()->d->iface.setForeignShortcut(actionId, sc);
00606 }
00607 
00608 #include "kglobalaccel.moc"
00609 

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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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