00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "kdedglobalaccel.h"
00024 #include <kdebug.h>
00025 #include "kdedglobalaccel_adaptor.h"
00026
00027
00028 #ifdef Q_WS_X11
00029 #include "kglobalaccel_x11.h"
00030 #elif defined(Q_WS_MACX)
00031 #include "kglobalaccel_mac.h"
00032 #elif defined(Q_WS_WIN)
00033 #include "kglobalaccel_win.h"
00034 #else
00035 #include "kglobalaccel_qws.h"
00036 #endif
00037
00038 #include <QtCore/QHash>
00039 #include <QtCore/QTimer>
00040
00041 #ifdef Q_WS_X11
00042 #include <QtGui/QX11Info>
00043 #include <QtGui/QApplication>
00044 #endif
00045
00046 #include <kconfiggroup.h>
00047 #include <kglobal.h>
00048 #include <ksharedconfig.h>
00049 #include <kpluginfactory.h>
00050 #include <kpluginloader.h>
00051
00052 K_PLUGIN_FACTORY(KdedGlobalAccelFactory,
00053 registerPlugin<KdedGlobalAccel>();
00054 )
00055 K_EXPORT_PLUGIN(KdedGlobalAccelFactory("globalaccel"))
00056
00057 struct componentData
00058 {
00059 QString uniqueName;
00060
00061
00062 QString friendlyName;
00063 QHash<QString, actionData *> actions;
00064 };
00065
00066 struct actionData
00067 {
00068
00069 bool isPresent:1;
00070 bool isFresh:1;
00071 componentData *parent;
00072 QString uniqueName;
00073 QString friendlyName;
00074 QList<int> keys;
00075 QList<int> defaultKeys;
00076 };
00077
00078 enum actionIdFields
00079 {
00080 ComponentUnique = 0,
00081 ActionUnique = 1,
00082 ComponentFriendly = 2,
00083 ActionFriendly = 3
00084 };
00085
00086
00087
00088
00089
00090
00091 class KdedGlobalAccelPrivate
00092 {
00093 public:
00094 KdedGlobalAccelPrivate();
00095 ~KdedGlobalAccelPrivate();
00096 actionData *findAction(int) const;
00097 actionData *findAction(const QStringList &actionId) const;
00098 actionData *addAction(const QStringList &actionId);
00099 actionData *takeAction(const QStringList &actionId);
00100
00101
00102 static bool isEmpty(const QList<int>&);
00103 static QList<int> nonemptyOnly(const QList<int> &);
00104
00105 KGlobalAccelImpl *impl;
00106
00107 QHash<int, actionData *> keyToAction;
00108 QHash<QString, componentData *> mainComponents;
00109
00110 KConfig config;
00111 QTimer writeoutTimer;
00112 };
00113
00114
00115 KdedGlobalAccelPrivate::KdedGlobalAccelPrivate()
00116 : config("kglobalshortcutsrc", KConfig::SimpleConfig)
00117 {
00118 }
00119
00120
00121 KdedGlobalAccelPrivate::~KdedGlobalAccelPrivate()
00122 {
00123 }
00124
00125
00126 actionData *KdedGlobalAccelPrivate::findAction(int key) const
00127 {
00128 return keyToAction.value(key);
00129 }
00130
00131
00132 actionData *KdedGlobalAccelPrivate::findAction(const QStringList &actionId) const
00133 {
00134 if (actionId.count() < 2)
00135 return 0;
00136 componentData *cd = mainComponents.value(actionId.at(ComponentUnique));
00137 if (!cd)
00138 return 0;
00139 return cd->actions.value(actionId.at(ActionUnique));
00140 }
00141
00142
00143 actionData *KdedGlobalAccelPrivate::addAction(const QStringList &actionId)
00144 {
00145 Q_ASSERT(actionId.size() >= 4);
00146 componentData *cd = mainComponents.value(actionId.at(ComponentUnique));
00147 if (!cd) {
00148 cd = new componentData;
00149 cd->uniqueName = actionId.at(ComponentUnique);
00150 cd->friendlyName = actionId.at(ComponentFriendly);
00151 mainComponents.insert(actionId.at(ComponentUnique), cd);
00152 }
00153 Q_ASSERT(!cd->actions.value(actionId.at(ActionUnique)));
00154 actionData *ad = new actionData;
00155 ad->parent = cd;
00156 ad->uniqueName = actionId.at(ActionUnique);
00157 ad->friendlyName = actionId.at(ActionFriendly);
00158 cd->actions.insert(actionId.at(ActionUnique), ad);
00159 return ad;
00160 }
00161
00162
00163 actionData *KdedGlobalAccelPrivate::takeAction(const QStringList &actionId)
00164 {
00165 componentData *cd = mainComponents.value(actionId.at(ComponentUnique));
00166 if (!cd)
00167 return 0;
00168 actionData *ret = cd->actions.take(actionId.at(ActionUnique));
00169 if (cd->actions.isEmpty())
00170 delete mainComponents.take(actionId.at(ComponentUnique));
00171 return ret;
00172 }
00173
00174
00175
00176
00177 bool KdedGlobalAccelPrivate::isEmpty(const QList<int>& keys)
00178 {
00179 const int count = keys.count();
00180 for (int i = 0; i < count; i++)
00181 if (keys[i] != 0)
00182 return false;
00183
00184 return true;
00185 }
00186
00187
00188
00189 QList<int> KdedGlobalAccelPrivate::nonemptyOnly(const QList<int> &keys)
00190 {
00191 QList<int> ret;
00192 const int count = keys.count();
00193 for (int i = 0; i < count; i++)
00194 if (keys[i] != 0)
00195 ret.append(keys[i]);
00196
00197 return ret;
00198 }
00199
00200
00201 KdedGlobalAccel::KdedGlobalAccel(QObject* parent, const QList<QVariant>&)
00202 : KDEDModule(parent),
00203 d(new KdedGlobalAccelPrivate)
00204 {
00205 qDBusRegisterMetaType<QList<int> >();
00206
00207 d->impl = new KGlobalAccelImpl(this);
00208
00209
00210 d->impl->setEnabled(true);
00211 connect(&d->writeoutTimer, SIGNAL(timeout()), SLOT(writeSettings()));
00212 d->writeoutTimer.setSingleShot(true);
00213 connect(this, SIGNAL(moduleDeleted(KDEDModule *)), SLOT(writeSettings()));
00214
00215 loadSettings();
00216 new KdedGlobalAccelAdaptor(this);
00217 QDBusConnection::sessionBus().registerObject("/KdedGlobalAccel", this);
00218 }
00219
00220
00221 KdedGlobalAccel::~KdedGlobalAccel()
00222 {
00223
00224
00225
00226
00227
00228 QDBusConnection::sessionBus().unregisterObject("/KdedGlobalAccel" );
00229
00230
00231
00232 Q_FOREACH (const QStringList &component, allComponents()) {
00233 Q_FOREACH (const QStringList &actionId, allActionsForComponent(component)) {
00234 setInactive(actionId);
00235 }
00236 }
00237
00238
00239 delete d->impl;
00240 delete d;
00241 }
00242
00243 QList<QStringList> KdedGlobalAccel::allComponents()
00244 {
00245
00246 QList<QStringList> ret;
00247 QStringList emptyList;
00248 for (int i = 0; i < 4; i++) {
00249 emptyList.append(QString());
00250 }
00251
00252 foreach (const componentData *const cd, d->mainComponents) {
00253 QStringList actionId(emptyList);
00254 actionId[ComponentUnique] = cd->uniqueName;
00255 actionId[ComponentFriendly] = cd->friendlyName;
00256 ret.append(actionId);
00257 }
00258 return ret;
00259 }
00260
00261 QList<QStringList> KdedGlobalAccel::allActionsForComponent(const QStringList &actionId)
00262 {
00263
00264 QList<QStringList> ret;
00265
00266 componentData *const cd = d->mainComponents.value(actionId[ComponentUnique]);
00267 if (!cd) {
00268 return ret;
00269 }
00270
00271 QStringList partialId(actionId[ComponentUnique]);
00272 partialId.append(QString());
00273
00274 partialId.append(cd->friendlyName);
00275 partialId.append(QString());
00276
00277 foreach (const actionData *const ad, cd->actions) {
00278 if (ad->isFresh) {
00279
00280 continue;
00281 }
00282 QStringList actionId(partialId);
00283 actionId[ActionUnique] = ad->uniqueName;
00284 actionId[ActionFriendly] = ad->friendlyName;
00285 ret.append(actionId);
00286 }
00287 return ret;
00288 }
00289
00290 QList<int> KdedGlobalAccel::allKeys()
00291 {
00292 QList<int> ret = d->keyToAction.keys();
00293 kDebug() << ret;
00294 return ret;
00295 }
00296
00297 QStringList KdedGlobalAccel::allKeysAsString()
00298 {
00299 QStringList ret;
00300 foreach(int keyQt, d->keyToAction.keys())
00301 ret << QKeySequence(keyQt).toString();
00302 return ret;
00303 }
00304
00305 QStringList KdedGlobalAccel::actionId(int key)
00306 {
00307 QStringList ret;
00308 if (actionData *ad = d->findAction(key)) {
00309 ret.append(ad->parent->uniqueName);
00310 ret.append(ad->uniqueName);
00311 ret.append(ad->parent->friendlyName);
00312 ret.append(ad->friendlyName);
00313 }
00314 return ret;
00315 }
00316
00317
00318 QList<int> KdedGlobalAccel::shortcut(const QStringList &action)
00319 {
00320 actionData *ad = d->findAction(action);
00321 if (ad)
00322 return ad->keys;
00323 return QList<int>();
00324 }
00325
00326
00327 QList<int> KdedGlobalAccel::defaultShortcut(const QStringList &action)
00328 {
00329 actionData *ad = d->findAction(action);
00330 if (ad)
00331 return ad->defaultKeys;
00332 return QList<int>();
00333 }
00334
00335
00336 void KdedGlobalAccel::doRegister(const QStringList &actionId)
00337 {
00338 if (actionId.size() < 4) {
00339 return;
00340 }
00341 actionData *ad = d->findAction(actionId);
00342 if (!ad) {
00343 ad = d->addAction(actionId);
00344
00345 ad->isPresent = false;
00346 ad->isFresh = true;
00347
00348 } else {
00349
00350 if ((!actionId[ActionFriendly].isEmpty()) && ad->friendlyName != actionId[ActionFriendly]) {
00351 ad->friendlyName = actionId[ActionFriendly];
00352 scheduleWriteSettings();
00353 }
00354 if ((!actionId[ComponentFriendly].isEmpty()) && ad->parent->friendlyName != actionId[ComponentFriendly]) {
00355 ad->parent->friendlyName = actionId[ComponentFriendly];
00356 scheduleWriteSettings();
00357 }
00358 }
00359 }
00360
00361
00362 void KdedGlobalAccel::unRegister(const QStringList &actionId)
00363 {
00364 kDebug(125) << actionId;
00365
00366 Q_ASSERT(actionId.size()==4);
00367 if (actionId.size() < 4) {
00368 return;
00369 }
00370
00371
00372 setInactive(actionId);
00373 actionData *ad = d->takeAction(actionId);
00374
00375 Q_FOREACH(int key, d->keyToAction.keys(ad)) {
00376 d->keyToAction.remove(key);
00377 }
00378 delete ad;
00379
00380 scheduleWriteSettings();
00381 }
00382
00383
00384
00385 QList<int> KdedGlobalAccel::setShortcut(const QStringList &actionId,
00386 const QList<int> &keys, uint flags)
00387 {
00388
00389 const bool setPresent = (flags & SetPresent);
00390 const bool isAutoloading = !(flags & NoAutoloading);
00391 const bool isDefault = (flags & IsDefault);
00392
00393 actionData *ad = d->findAction(actionId);
00394 if (!ad) {
00395 return QList<int>();
00396 }
00397
00398
00399 if (isDefault) {
00400 if (ad->defaultKeys != keys) {
00401 ad->defaultKeys = keys;
00402 scheduleWriteSettings();
00403 }
00404 return keys;
00405 }
00406
00407
00408 if (isAutoloading && !ad->isFresh) {
00409 if (!ad->isPresent && setPresent) {
00410 ad->isPresent = true;
00411 foreach (int key, ad->keys) {
00412 if (key != 0) {
00413 Q_ASSERT(d->keyToAction.value(key) == ad);
00414 d->impl->grabKey(key, true);
00415 }
00416 }
00417 }
00418 return ad->keys;
00419 }
00420
00421
00422
00423 QList<int> added = d->nonemptyOnly(keys);
00424
00425
00426 foreach(int oldKey, ad->keys) {
00427 if (oldKey != 0) {
00428 bool remains = false;
00429 for (int i = 0; i < added.count(); i++) {
00430 if (oldKey == added[i]) {
00431 added.removeAt(i);
00432 i--;
00433 remains = true;
00434
00435 }
00436 }
00437 if (!remains) {
00438 d->keyToAction.remove(oldKey);
00439 if (ad->isPresent) {
00440 d->impl->grabKey(oldKey, false);
00441 }
00442 }
00443 }
00444 }
00445
00446
00447
00448 if (setPresent) {
00449 ad->isPresent = true;
00450 }
00451 ad->keys = keys;
00452
00453
00454
00455
00456 ad->isFresh = false;
00457
00458
00459
00460 for (int i = 0; i < added.count(); i++) {
00461 if (!d->keyToAction.contains(added[i])) {
00462 d->keyToAction.insert(added[i], ad);
00463 } else {
00464
00465 for (int j = 0; j < ad->keys.count(); j++) {
00466 if (ad->keys[j] == added[i]) {
00467 if (ad->keys.last() == added[i]) {
00468 ad->keys.removeLast();
00469 j--;
00470 } else
00471 ad->keys[j] = 0;
00472 }
00473 }
00474 added.removeAt(i);
00475 i--;
00476 }
00477 }
00478
00479 if (ad->isPresent) {
00480 foreach (int key, added) {
00481 Q_ASSERT(d->keyToAction.value(key) == ad);
00482 d->impl->grabKey(key, true);
00483 }
00484 }
00485
00486 scheduleWriteSettings();
00487
00488 return ad->keys;
00489 }
00490
00491
00492 void KdedGlobalAccel::setForeignShortcut(const QStringList &actionId, const QList<int> &keys)
00493 {
00494 actionData *ad = d->findAction(actionId);
00495 if (!ad)
00496 return;
00497
00498 uint setterFlags = NoAutoloading;
00499
00500 QList<int> oldKeys = ad->keys;
00501 QList<int> newKeys = setShortcut(actionId, keys, setterFlags);
00502
00503
00504
00505
00506
00507 emit yourShortcutGotChanged(actionId, newKeys);
00508 }
00509
00510
00511 void KdedGlobalAccel::setInactive(const QStringList &actionId)
00512 {
00513 actionData *ad = d->findAction(actionId);
00514 if (!ad)
00515 return;
00516 ad->isPresent = false;
00517
00518 const int len = ad->keys.count();
00519 for (int i = 0; i < len; i++)
00520 if (ad->keys[i] != 0)
00521 d->impl->grabKey(ad->keys[i], false);
00522 }
00523
00524
00525 void KdedGlobalAccel::scheduleWriteSettings()
00526 {
00527 if (!d->writeoutTimer.isActive())
00528 d->writeoutTimer.start(500);
00529 }
00530
00531
00532
00533 void KdedGlobalAccel::writeSettings()
00534 {
00535 foreach (const componentData *const cd, d->mainComponents) {
00536 KConfigGroup configGroup(&d->config, cd->uniqueName);
00537
00538 KConfigGroup friendlyGroup(&configGroup, "Friendly Name");
00539 friendlyGroup.writeEntry("Friendly Name", cd->friendlyName);
00540
00541 foreach (const actionData *const ad, cd->actions) {
00542 if (ad->isFresh) {
00543
00544
00545
00546 continue;
00547 }
00548 QStringList entry(stringFromKeys(ad->keys));
00549 entry.append(stringFromKeys(ad->defaultKeys));
00550 entry.append(ad->friendlyName);
00551
00552 configGroup.writeEntry(ad->uniqueName, entry);
00553 }
00554 }
00555
00556 d->config.sync();
00557 }
00558
00559
00560 void KdedGlobalAccel::loadSettings()
00561 {
00562 QStringList lActionId;
00563 for (int i = 0; i < 4; i++) {
00564 lActionId.append(QString());
00565 }
00566
00567 foreach (const QString &groupName, d->config.groupList()) {
00568 KConfigGroup configGroup(&d->config, groupName);
00569 lActionId[ComponentUnique] = groupName;
00570
00571 KConfigGroup friendlyGroup(&configGroup, "Friendly Name");
00572 lActionId[ComponentFriendly] = friendlyGroup.readEntry("Friendly Name");
00573
00574 foreach (const QString &confKey, configGroup.keyList()) {
00575 const QStringList entry = configGroup.readEntry(confKey, QStringList());
00576 if (entry.size() != 3) {
00577 continue;
00578 }
00579 lActionId[ActionUnique] = confKey;
00580 lActionId[ActionFriendly] = entry[2];
00581
00582 actionData *ad = d->addAction(lActionId);
00583 ad->keys = keysFromString(entry[0]);
00584 ad->defaultKeys = keysFromString(entry[1]);
00585 ad->isPresent = false;
00586 ad->isFresh = false;
00587
00588 foreach (int key, ad->keys) {
00589 if (key != 0) {
00590 if (d->keyToAction.contains(key)) {
00591
00592
00593 ad->keys.removeAll(key);
00594 kWarning() << "Shortcut found twice in kglobalshortcutsrc.";
00595 } else {
00596 d->keyToAction.insert(key, ad);
00597 }
00598 }
00599 }
00600 }
00601 }
00602 }
00603
00604
00605 QList<int> KdedGlobalAccel::keysFromString(const QString &str)
00606 {
00607 QList<int> ret;
00608 if (str == "none") {
00609 return ret;
00610 }
00611 QStringList strList = str.split('\t');
00612 foreach (const QString &s, strList) {
00613 int key = QKeySequence(s)[0];
00614 if (key != -1) {
00615 ret.append(key);
00616 }
00617 }
00618 return ret;
00619 }
00620
00621
00622 QString KdedGlobalAccel::stringFromKeys(const QList<int> &keys)
00623 {
00624 if (keys.isEmpty()) {
00625 return "none";
00626 }
00627 QString ret;
00628 foreach (int key, keys) {
00629 ret.append(QKeySequence(key).toString());
00630 ret.append('\t');
00631 }
00632 ret.chop(1);
00633 return ret;
00634 }
00635
00636
00637 bool KdedGlobalAccel::keyPressed(int keyQt)
00638 {
00639 actionData *ad = d->keyToAction.value(keyQt);
00640 if (!ad || !ad->isPresent)
00641 return false;
00642
00643 QStringList data(ad->parent->uniqueName);
00644 data.append(ad->uniqueName);
00645 data.append(ad->parent->friendlyName);
00646 data.append(ad->friendlyName);
00647 #ifdef Q_WS_X11
00648
00649 long timestamp = QX11Info::appTime();
00650
00651
00652
00653 qApp->syncX();
00654 #else
00655 long timestamp = 0;
00656 #endif
00657 emit invokeAction(data, timestamp);
00658 return true;
00659 }
00660
00661 #include "kdedglobalaccel.moc"
00662 #include "kdedglobalaccel_adaptor.moc"