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 <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
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
00059
00060 QDBusReply<QDBusObjectPath> reply = kglobalaccel.getComponent(componentUnique);
00061 if (!reply.isValid()) {
00062
00063 if (reply.error().name() == "org.kde.kglobalaccel.NoSuchComponent") {
00064
00065 return NULL;
00066 }
00067
00068
00069 kDebug() << "Failed to get dbus path for component " << componentUnique << reply.error();
00070 return NULL;
00071 }
00072
00073
00074 org::kde::kglobalaccel::Component *component = new org::kde::kglobalaccel::Component(
00075 "org.kde.kglobalaccel",
00076 reply.value().path(),
00077 QDBusConnection::sessionBus());
00078
00079
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
00096
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
00158 self()->d->iface.activateGlobalShortcutContext(component.aboutData()->programName(), contextUnique);
00159 }
00160
00161
00162
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
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
00219
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
00249
00250 iface.unRegister(actionId);
00251 } else {
00252
00253
00254 if (!action->property("isConfigurationAction").toBool()) {
00255
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
00267
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
00287 if (!isConfigurationAction) {
00288 activeSetterFlags |= SetPresent;
00289 }
00290
00291
00292 const QList<int> result = iface.setShortcut(
00293 actionId,
00294 intListFromShortcut(activeShortcut),
00295 activeSetterFlags);
00296
00297 const KShortcut scResult(shortcutFromIntList(result));
00298
00299 if (isConfigurationAction && (flags & KAction::NoAutoloading)) {
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310 iface.setForeignShortcut(actionId, result);
00311 }
00312 if (scResult != activeShortcut) {
00313
00314
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));
00329 Q_ASSERT(!ret.at(KGlobalAccel::ComponentUnique).isEmpty());
00330 Q_ASSERT(!action->objectName().isEmpty());
00331 ret.append(action->objectName());
00332 ret.append(componentFriendlyForAction(action));
00333 ret.append(action->text());
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
00377
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
00391
00392
00393
00394 if (!action || !action->isEnabled()
00395 || action->property("isConfigurationAction").toBool()) {
00396 return;
00397 }
00398
00399 #ifdef Q_WS_X11
00400
00401
00402
00403
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
00431 reRegisterAll();
00432 }
00433 }
00434
00435 void KGlobalAccelPrivate::reRegisterAll()
00436 {
00437
00438
00439
00440
00441
00442
00443
00444
00445
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
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
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
00507 bool KGlobalAccel::promptStealShortcutSystemwide(
00508 QWidget *parent,
00509 const QList<KGlobalShortcutInfo> &shortcuts,
00510 const QKeySequence &seq)
00511 {
00512 if (shortcuts.isEmpty()) {
00513
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
00546 void KGlobalAccel::stealShortcutSystemwide(const QKeySequence &seq)
00547 {
00548
00549 const QStringList actionId = self()->d->iface.action(seq[0]);
00550 if (actionId.size() < 4)
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