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

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
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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