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

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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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