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 Q_ASSERT(actionId.size()==4);
00365
00366 if (actionId.size() < 4) {
00367 return;
00368 }
00369
00370
00371 setInactive(actionId);
00372 actionData *ad = d->takeAction(actionId);
00373
00374 Q_FOREACH(int key, d->keyToAction.keys(ad)) {
00375 d->keyToAction.remove(key);
00376 }
00377 delete ad;
00378
00379 scheduleWriteSettings();
00380 }
00381
00382
00383
00384 QList<int> KdedGlobalAccel::setShortcut(const QStringList &actionId,
00385 const QList<int> &keys, uint flags)
00386 {
00387
00388 const bool setPresent = (flags & SetPresent);
00389 const bool isAutoloading = !(flags & NoAutoloading);
00390 const bool isDefault = (flags & IsDefault);
00391
00392 actionData *ad = d->findAction(actionId);
00393 if (!ad) {
00394 return QList<int>();
00395 }
00396
00397
00398 if (isDefault) {
00399 if (ad->defaultKeys != keys) {
00400 ad->defaultKeys = keys;
00401 scheduleWriteSettings();
00402 }
00403 return keys;
00404 }
00405
00406
00407 if (isAutoloading && !ad->isFresh) {
00408 if (!ad->isPresent && setPresent) {
00409 ad->isPresent = true;
00410 foreach (int key, ad->keys) {
00411 if (key != 0) {
00412 Q_ASSERT(d->keyToAction.value(key) == ad);
00413 d->impl->grabKey(key, true);
00414 }
00415 }
00416 }
00417 return ad->keys;
00418 }
00419
00420
00421
00422 QList<int> added = d->nonemptyOnly(keys);
00423
00424
00425 foreach(int oldKey, ad->keys) {
00426 if (oldKey != 0) {
00427 bool remains = false;
00428 for (int i = 0; i < added.count(); i++) {
00429 if (oldKey == added[i]) {
00430 added.removeAt(i);
00431 i--;
00432 remains = true;
00433
00434 }
00435 }
00436 if (!remains) {
00437 d->keyToAction.remove(oldKey);
00438 if (ad->isPresent) {
00439 d->impl->grabKey(oldKey, false);
00440 }
00441 }
00442 }
00443 }
00444
00445
00446
00447 if (setPresent) {
00448 ad->isPresent = true;
00449 }
00450 ad->keys = keys;
00451
00452
00453
00454
00455 ad->isFresh = false;
00456
00457
00458
00459 for (int i = 0; i < added.count(); i++) {
00460 if (!d->keyToAction.contains(added[i])) {
00461 d->keyToAction.insert(added[i], ad);
00462 } else {
00463
00464 for (int j = 0; j < ad->keys.count(); j++) {
00465 if (ad->keys[j] == added[i]) {
00466 if (ad->keys.last() == added[i]) {
00467 ad->keys.removeLast();
00468 j--;
00469 } else
00470 ad->keys[j] = 0;
00471 }
00472 }
00473 added.removeAt(i);
00474 i--;
00475 }
00476 }
00477
00478 if (ad->isPresent) {
00479 foreach (int key, added) {
00480 Q_ASSERT(d->keyToAction.value(key) == ad);
00481 d->impl->grabKey(key, true);
00482 }
00483 }
00484
00485 scheduleWriteSettings();
00486
00487 return ad->keys;
00488 }
00489
00490
00491 void KdedGlobalAccel::setForeignShortcut(const QStringList &actionId, const QList<int> &keys)
00492 {
00493 actionData *ad = d->findAction(actionId);
00494 if (!ad)
00495 return;
00496
00497 uint setterFlags = NoAutoloading;
00498
00499 QList<int> oldKeys = ad->keys;
00500 QList<int> newKeys = setShortcut(actionId, keys, setterFlags);
00501
00502
00503
00504
00505
00506 emit yourShortcutGotChanged(actionId, newKeys);
00507 }
00508
00509
00510 void KdedGlobalAccel::setInactive(const QStringList &actionId)
00511 {
00512 actionData *ad = d->findAction(actionId);
00513 if (!ad)
00514 return;
00515 ad->isPresent = false;
00516
00517 const int len = ad->keys.count();
00518 for (int i = 0; i < len; i++)
00519 if (ad->keys[i] != 0)
00520 d->impl->grabKey(ad->keys[i], false);
00521 }
00522
00523
00524 void KdedGlobalAccel::scheduleWriteSettings()
00525 {
00526 if (!d->writeoutTimer.isActive())
00527 d->writeoutTimer.start(500);
00528 }
00529
00530
00531
00532 void KdedGlobalAccel::writeSettings()
00533 {
00534 foreach (const componentData *const cd, d->mainComponents) {
00535 KConfigGroup configGroup(&d->config, cd->uniqueName);
00536
00537 KConfigGroup friendlyGroup(&configGroup, "Friendly Name");
00538 friendlyGroup.writeEntry("Friendly Name", cd->friendlyName);
00539
00540 foreach (const actionData *const ad, cd->actions) {
00541 if (ad->isFresh) {
00542
00543
00544
00545 continue;
00546 }
00547 QStringList entry(stringFromKeys(ad->keys));
00548 entry.append(stringFromKeys(ad->defaultKeys));
00549 entry.append(ad->friendlyName);
00550
00551 configGroup.writeEntry(ad->uniqueName, entry);
00552 }
00553 }
00554
00555 d->config.sync();
00556 }
00557
00558
00559 void KdedGlobalAccel::loadSettings()
00560 {
00561 QStringList lActionId;
00562 for (int i = 0; i < 4; i++) {
00563 lActionId.append(QString());
00564 }
00565
00566 foreach (const QString &groupName, d->config.groupList()) {
00567 KConfigGroup configGroup(&d->config, groupName);
00568 lActionId[ComponentUnique] = groupName;
00569
00570 KConfigGroup friendlyGroup(&configGroup, "Friendly Name");
00571 lActionId[ComponentFriendly] = friendlyGroup.readEntry("Friendly Name");
00572
00573 foreach (const QString &confKey, configGroup.keyList()) {
00574 const QStringList entry = configGroup.readEntry(confKey, QStringList());
00575 if (entry.size() != 3) {
00576 continue;
00577 }
00578 lActionId[ActionUnique] = confKey;
00579 lActionId[ActionFriendly] = entry[2];
00580
00581 actionData *ad = d->addAction(lActionId);
00582 ad->keys = keysFromString(entry[0]);
00583 ad->defaultKeys = keysFromString(entry[1]);
00584 ad->isPresent = false;
00585 ad->isFresh = false;
00586
00587 foreach (int key, ad->keys) {
00588 if (key != 0) {
00589 d->keyToAction.insert(key, ad);
00590 }
00591 }
00592 }
00593 }
00594 }
00595
00596
00597 QList<int> KdedGlobalAccel::keysFromString(const QString &str)
00598 {
00599 QList<int> ret;
00600 if (str == "none") {
00601 return ret;
00602 }
00603 QStringList strList = str.split('\t');
00604 foreach (const QString &s, strList) {
00605 int key = QKeySequence(s)[0];
00606 if (key != -1) {
00607 ret.append(key);
00608 }
00609 }
00610 return ret;
00611 }
00612
00613
00614 QString KdedGlobalAccel::stringFromKeys(const QList<int> &keys)
00615 {
00616 if (keys.isEmpty()) {
00617 return "none";
00618 }
00619 QString ret;
00620 foreach (int key, keys) {
00621 ret.append(QKeySequence(key).toString());
00622 ret.append('\t');
00623 }
00624 ret.chop(1);
00625 return ret;
00626 }
00627
00628
00629 bool KdedGlobalAccel::keyPressed(int keyQt)
00630 {
00631 actionData *ad = d->keyToAction.value(keyQt);
00632 if (!ad || !ad->isPresent)
00633 return false;
00634
00635 QStringList data(ad->parent->uniqueName);
00636 data.append(ad->uniqueName);
00637 data.append(ad->parent->friendlyName);
00638 data.append(ad->friendlyName);
00639 #ifdef Q_WS_X11
00640
00641 long timestamp = QX11Info::appTime();
00642
00643
00644
00645 qApp->syncX();
00646 #else
00647 long timestamp = 0;
00648 #endif
00649 emit invokeAction(data, timestamp);
00650 return true;
00651 }
00652
00653 #include "kdedglobalaccel.moc"
00654 #include "kdedglobalaccel_adaptor.moc"