2 This file is part of the KDE libraries
4 Copyright (c) 2007 Andreas Hartmetz <ahartmetz@gmail.com>
5 Copyright (c) 2007 Michael Jansen <kde@michael-jansen.biz>
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
23 #include "kdedglobalaccel.h"
25 #include "component.h"
26 #include "globalshortcut.h"
27 #include "globalshortcutcontext.h"
28 #include "globalshortcutsregistry.h"
30 #include <QtCore/QTimer>
31 #include <QtCore/QMetaMethod>
32 #include <QtDBus/QDBusMetaType>
33 #include <QtDBus/QDBusObjectPath>
35 #include "kglobalaccel.h"
36 #include "kglobalsettings.h"
39 #include "kpluginfactory.h"
40 #include "kpluginloader.h"
42 K_PLUGIN_FACTORY(KdedGlobalAccelFactory
,
43 registerPlugin
<KdedGlobalAccel
>();
45 K_EXPORT_PLUGIN(KdedGlobalAccelFactory("globalaccel"))
47 struct KdedGlobalAccelPrivate
49 GlobalShortcut
*findAction(const QStringList
&actionId
) const;
50 GlobalShortcut
*addAction(const QStringList
&actionId
);
51 KdeDGlobalAccel::Component
*component(const QStringList
&actionId
) const;
54 void splitComponent(QString
&component
, QString
&context
) const
57 if (component
.indexOf('|')!=-1)
59 QStringList tmp
= component
.split("|");
60 Q_ASSERT(tmp
.size()==2);
66 void _k_initializeDBus(const QDBusObjectPath
&path
);
69 void KdedGlobalAccelPrivate::_k_initializeDBus(const QDBusObjectPath
&path
)
71 GlobalShortcutsRegistry::self()->setDBusPath(QDBusObjectPath(path
));
72 GlobalShortcutsRegistry::self()->loadSettings();
76 GlobalShortcut
*KdedGlobalAccelPrivate::findAction(const QStringList
&actionId
) const
78 // Check if actionId is valid
79 if (actionId
.size() != 4)
81 kDebug() << "Invalid! '" << actionId
<< "'";
85 QString componentUnique
= actionId
.at(KGlobalAccel::ComponentUnique
);
87 KdeDGlobalAccel::Component
*component
;
88 QString contextUnique
;
89 if (componentUnique
.indexOf('|')==-1)
91 component
= GlobalShortcutsRegistry::self()->getComponent( componentUnique
);
92 if (component
) contextUnique
= component
->currentContext()->uniqueName();
96 splitComponent(componentUnique
, contextUnique
);
97 component
= GlobalShortcutsRegistry::self()->getComponent( componentUnique
);
102 #ifdef KDEDGLOBALACCEL_TRACE
103 kDebug() << componentUnique
<< "not found";
108 GlobalShortcut
*shortcut
= component
109 ? component
->getShortcutByName(actionId
.at(KGlobalAccel::ActionUnique
), contextUnique
)
112 #ifdef KDEDGLOBALACCEL_TRACE
115 kDebug() << componentUnique
117 << shortcut
->uniqueName();
121 kDebug() << "No match for" << actionId
;
128 KdeDGlobalAccel::Component
*KdedGlobalAccelPrivate::component(const QStringList
&actionId
) const
130 // Get the component for the action. If we have none create a new one
131 KdeDGlobalAccel::Component
*component
= GlobalShortcutsRegistry::self()->getComponent(actionId
.at(KGlobalAccel::ComponentUnique
));
134 component
= new KdeDGlobalAccel::Component(
135 actionId
.at(KGlobalAccel::ComponentUnique
),
136 actionId
.at(KGlobalAccel::ComponentFriendly
),
137 GlobalShortcutsRegistry::self());
144 GlobalShortcut
*KdedGlobalAccelPrivate::addAction(const QStringList
&actionId
)
146 Q_ASSERT(actionId
.size() >= 4);
148 QString componentUnique
= actionId
.at(KGlobalAccel::ComponentUnique
);
149 QString contextUnique
= "default";
151 if (componentUnique
.indexOf("|")!=-1) {
152 QStringList tmp
= componentUnique
.split("|");
153 Q_ASSERT(tmp
.size()==2);
154 componentUnique
= tmp
.at(0);
155 contextUnique
= tmp
.at(1);
158 QStringList actionIdTmp
= actionId
;
159 actionIdTmp
.replace(KGlobalAccel::ComponentUnique
, componentUnique
);
161 // Create the component if necessary
162 KdeDGlobalAccel::Component
*component
= this->component(actionIdTmp
);
165 // Create the context if necessary
166 if (component
->getShortcutContexts().count(contextUnique
)==0) {
167 component
->createGlobalShortcutContext(contextUnique
);
170 Q_ASSERT(!component
->getShortcutByName(componentUnique
, contextUnique
));
172 return new GlobalShortcut(
173 actionId
.at(KGlobalAccel::ActionUnique
),
174 actionId
.at(KGlobalAccel::ActionFriendly
),
175 component
->shortcutContext(contextUnique
));
178 Q_DECLARE_METATYPE(QStringList
)
180 KdedGlobalAccel::KdedGlobalAccel(QObject
* parent
, const QList
<QVariant
>&)
181 : KDEDModule(parent
),
182 d(new KdedGlobalAccelPrivate
)
184 qDBusRegisterMetaType
< QList
<int> >();
185 qDBusRegisterMetaType
< QList
<QDBusObjectPath
> >();
186 qDBusRegisterMetaType
< QList
<QStringList
> >();
187 qDBusRegisterMetaType
<QStringList
>();
188 qDBusRegisterMetaType
<KGlobalShortcutInfo
>();
189 qDBusRegisterMetaType
< QList
<KGlobalShortcutInfo
> >();
191 GlobalShortcutsRegistry
*reg
= GlobalShortcutsRegistry::self();
194 connect(&d
->writeoutTimer
, SIGNAL(timeout()),
195 reg
, SLOT(writeSettings()));
197 connect(this, SIGNAL(moduleRegistered(const QDBusObjectPath
&)),
198 this, SLOT(_k_initializeDBus(const QDBusObjectPath
&)));
200 d
->writeoutTimer
.setSingleShot(true);
201 connect(this, SIGNAL(moduleDeleted(KDEDModule
*)),
202 reg
, SLOT(writeSettings()));
204 connect(reg
, SIGNAL(invokeAction(const QStringList
&, qlonglong
)),
205 SIGNAL(invokeAction(const QStringList
&, qlonglong
)));
207 connect(KGlobalSettings::self(), SIGNAL(blockShortcuts(int)),
208 SLOT(blockGlobalShortcuts(int)));
212 KdedGlobalAccel::~KdedGlobalAccel()
214 // Unregister all currently registered actions. Enables the module to be
215 // loaded / unloaded by kded.
216 GlobalShortcutsRegistry::self()->deactivateShortcuts();
220 QList
<QStringList
> KdedGlobalAccel::allMainComponents() const
222 QList
<QStringList
> ret
;
223 QStringList emptyList
;
224 for (int i
= 0; i
< 4; i
++) {
225 emptyList
.append(QString());
228 foreach (const KdeDGlobalAccel::Component
*component
, GlobalShortcutsRegistry::self()->allMainComponents()) {
229 QStringList
actionId(emptyList
);
230 actionId
[KGlobalAccel::ComponentUnique
] = component
->uniqueName();
231 actionId
[KGlobalAccel::ComponentFriendly
] = component
->friendlyName();
232 ret
.append(actionId
);
239 QList
<QStringList
> KdedGlobalAccel::allActionsForComponent(const QStringList
&actionId
) const
241 //### Would it be advantageous to sort the actions by unique name?
242 QList
<QStringList
> ret
;
244 KdeDGlobalAccel::Component
*const component
=
245 GlobalShortcutsRegistry::self()->getComponent(actionId
[KGlobalAccel::ComponentUnique
]);
250 QStringList
partialId(actionId
[KGlobalAccel::ComponentUnique
]); //ComponentUnique
251 partialId
.append(QString()); //ActionUnique
252 //Use our internal friendlyName, not the one passed in. We should have the latest data.
253 partialId
.append(component
->friendlyName()); //ComponentFriendly
254 partialId
.append(QString()); //ActionFriendly
256 foreach (const GlobalShortcut
*const shortcut
, component
->allShortcuts()) {
257 if (shortcut
->isFresh()) {
258 // isFresh is only an intermediate state, not to be reported outside.
261 QStringList
actionId(partialId
);
262 actionId
[KGlobalAccel::ActionUnique
] = shortcut
->uniqueName();
263 actionId
[KGlobalAccel::ActionFriendly
] = shortcut
->friendlyName();
264 ret
.append(actionId
);
270 QStringList
KdedGlobalAccel::action(int key
) const
272 GlobalShortcut
*shortcut
= GlobalShortcutsRegistry::self()->getShortcutByKey(key
);
275 ret
.append(shortcut
->context()->component()->uniqueName());
276 ret
.append(shortcut
->uniqueName());
277 ret
.append(shortcut
->context()->component()->friendlyName());
278 ret
.append(shortcut
->friendlyName());
284 void KdedGlobalAccel::activateGlobalShortcutContext(
285 const QString
&component
,
286 const QString
&uniqueName
)
288 KdeDGlobalAccel::Component
*const comp
=
289 GlobalShortcutsRegistry::self()->getComponent(component
);
291 comp
->activateGlobalShortcutContext(uniqueName
);
295 QList
<QDBusObjectPath
> KdedGlobalAccel::allComponents() const
297 QList
<QDBusObjectPath
> allComp
;
299 Q_FOREACH (const KdeDGlobalAccel::Component
*component
,
300 GlobalShortcutsRegistry::self()->allMainComponents())
302 allComp
.append(component
->dbusPath());
309 void KdedGlobalAccel::blockGlobalShortcuts(int block
)
311 #ifdef KDEDGLOBALACCEL_TRACE
315 ? GlobalShortcutsRegistry::self()->deactivateShortcuts(true)
316 : GlobalShortcutsRegistry::self()->activateShortcuts();
320 QList
<int> KdedGlobalAccel::shortcut(const QStringList
&action
) const
322 GlobalShortcut
*shortcut
= d
->findAction(action
);
324 return shortcut
->keys();
329 QList
<int> KdedGlobalAccel::defaultShortcut(const QStringList
&action
) const
331 GlobalShortcut
*shortcut
= d
->findAction(action
);
333 return shortcut
->defaultKeys();
338 // This method just registers the action. Nothing else. Shortcut has to be set
340 void KdedGlobalAccel::doRegister(const QStringList
&actionId
)
342 #ifdef KDEDGLOBALACCEL_TRACE
343 kDebug() << actionId
;
346 // Check because we would not want to add a action for an invalid
347 // actionId. findAction returns NULL in that case.
348 if (actionId
.size() < 4) {
352 GlobalShortcut
*shortcut
= d
->findAction(actionId
);
354 shortcut
= d
->addAction(actionId
);
356 //a switch of locales is one common reason for a changing friendlyName
357 if ((!actionId
[KGlobalAccel::ActionFriendly
].isEmpty()) && shortcut
->friendlyName() != actionId
[KGlobalAccel::ActionFriendly
]) {
358 shortcut
->setFriendlyName(actionId
[KGlobalAccel::ActionFriendly
]);
359 scheduleWriteSettings();
361 if ((!actionId
[KGlobalAccel::ComponentFriendly
].isEmpty())
362 && shortcut
->context()->component()->friendlyName() != actionId
[KGlobalAccel::ComponentFriendly
]) {
363 shortcut
->context()->component()->setFriendlyName(actionId
[KGlobalAccel::ComponentFriendly
]);
364 scheduleWriteSettings();
370 QList
<KGlobalShortcutInfo
> KdedGlobalAccel::getGlobalShortcutsByKey(int key
) const
372 QList
<GlobalShortcut
*> shortcuts
=
373 GlobalShortcutsRegistry::self()->getShortcutsByKey(key
);
375 QList
<KGlobalShortcutInfo
> rc
;
376 Q_FOREACH(GlobalShortcut
const*sc
, shortcuts
)
378 rc
.append(static_cast<KGlobalShortcutInfo
>(*sc
));
385 bool KdedGlobalAccel::isGlobalShortcutAvailable(int shortcut
, const QString
&component
) const
387 QString realComponent
= component
;
389 d
->splitComponent(realComponent
, context
);
390 return GlobalShortcutsRegistry::self()->isShortcutAvailable(shortcut
, realComponent
, context
);
394 void KdedGlobalAccel::setInactive(const QStringList
&actionId
)
396 #ifdef KDEDGLOBALACCEL_TRACE
397 kDebug() << actionId
;
400 GlobalShortcut
*shortcut
= d
->findAction(actionId
);
402 shortcut
->setIsPresent(false);
406 void KdedGlobalAccel::unRegister(const QStringList
&actionId
)
408 #ifdef KDEDGLOBALACCEL_TRACE
409 kDebug() << actionId
;
412 // Stop grabbing the key
413 GlobalShortcut
*shortcut
= d
->findAction(actionId
);
418 scheduleWriteSettings();
422 QList
<int> KdedGlobalAccel::setShortcut(const QStringList
&actionId
,
423 const QList
<int> &keys
, uint flags
)
425 //spare the DBus framework some work
426 const bool setPresent
= (flags
& SetPresent
);
427 const bool isAutoloading
= !(flags
& NoAutoloading
);
428 const bool isDefault
= (flags
& IsDefault
);
430 GlobalShortcut
*shortcut
= d
->findAction(actionId
);
435 //default shortcuts cannot clash because they don't do anything
437 if (shortcut
->defaultKeys() != keys
) {
438 shortcut
->setDefaultKeys(keys
);
439 scheduleWriteSettings();
441 return keys
; //doesn't matter
444 if (isAutoloading
&& !shortcut
->isFresh()) {
445 //the trivial and common case - synchronize the action from our data
447 if (!shortcut
->isPresent() && setPresent
) {
448 shortcut
->setIsPresent(true);
450 // We are finished here. Return the list of current active keys.
451 return shortcut
->keys();
454 //now we are actually changing the shortcut of the action
455 shortcut
->setKeys(keys
);
458 shortcut
->setIsPresent(true);
461 //maybe isFresh should really only be set if setPresent, but only two things should use !setPresent:
462 //- the global shortcuts KCM: very unlikely to catch KWin/etc.'s actions in isFresh state
463 //- KGlobalAccel::stealGlobalShortcutSystemwide(): only applies to actions with shortcuts
464 // which can never be fresh if created the usual way
465 shortcut
->setIsFresh(false);
467 scheduleWriteSettings();
469 return shortcut
->keys();
473 void KdedGlobalAccel::setForeignShortcut(const QStringList
&actionId
, const QList
<int> &keys
)
475 #ifdef KDEDGLOBALACCEL_TRACE
476 kDebug() << actionId
;
479 GlobalShortcut
*shortcut
= d
->findAction(actionId
);
483 QList
<int> newKeys
= setShortcut(actionId
, keys
, NoAutoloading
);
485 emit
yourShortcutGotChanged(actionId
, newKeys
);
489 void KdedGlobalAccel::scheduleWriteSettings() const
491 if (!d
->writeoutTimer
.isActive())
492 d
->writeoutTimer
.start(500);
496 #include "moc_kdedglobalaccel.cpp"