00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
00049 QDBusConnectionInterface* bus = QDBusConnection::sessionBus().interface();
00050 if (!bus->isServiceRegistered("org.kde.kded")) {
00051 KToolInvocation::klauncher();
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
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
00141
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
00171
00172 iface.unRegister(actionId);
00173 } else {
00174
00175
00176 if (!action->property("isConfigurationAction").toBool()) {
00177
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
00189
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
00209 if (!isConfigurationAction) {
00210 activeSetterFlags |= SetPresent;
00211 }
00212
00213
00214 const QList<int> result = iface.setShortcut(
00215 actionId,
00216 intListFromShortcut(activeShortcut),
00217 activeSetterFlags);
00218
00219 const KShortcut scResult(shortcutFromIntList(result));
00220
00221 if (isConfigurationAction && (flags & KAction::NoAutoloading)) {
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232 iface.setForeignShortcut(actionId, result);
00233 }
00234 if (scResult != activeShortcut) {
00235
00236
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));
00251 Q_ASSERT(!ret.at(KGlobalAccel::ComponentUnique).isEmpty());
00252 Q_ASSERT(!action->objectName().isEmpty());
00253 ret.append(action->objectName());
00254 ret.append(componentFriendlyForAction(action));
00255 ret.append(action->text());
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
00299
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
00313
00314
00315
00316 if (!action || !action->isEnabled()
00317 || action->property("isConfigurationAction").toBool()) {
00318 return;
00319 }
00320
00321 #ifdef Q_WS_X11
00322
00323
00324
00325
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
00353 reRegisterAll();
00354 }
00355 }
00356
00357 void KGlobalAccelPrivate::reRegisterAll()
00358 {
00359
00360
00361
00362
00363
00364
00365
00366
00367
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
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
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
00429 bool KGlobalAccel::promptStealShortcutSystemwide(
00430 QWidget *parent,
00431 const QList<KGlobalShortcutInfo> &shortcuts,
00432 const QKeySequence &seq)
00433 {
00434 if (shortcuts.isEmpty()) {
00435
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
00468 void KGlobalAccel::stealShortcutSystemwide(const QKeySequence &seq)
00469 {
00470
00471 const QStringList actionId = self()->d->iface.action(seq[0]);
00472 if (actionId.size() < 4)
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