1 | /* Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz> |
2 | |
3 | This library is free software; you can redistribute it and/or |
4 | modify it under the terms of the GNU Library General Public |
5 | License as published by the Free Software Foundation; either |
6 | version 2 of the License, or (at your option) any later version. |
7 | |
8 | This library is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
11 | Library General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU Library General Public License |
14 | along with this library; see the file COPYING.LIB. If not, write to |
15 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
16 | Boston, MA 02110-1301, USA. |
17 | */ |
18 | |
19 | #include "globalshortcutsregistry.h" |
20 | #include "component.h" |
21 | #include "globalshortcut.h" |
22 | #include "globalshortcutcontext.h" |
23 | |
24 | #include "kdebug.h" |
25 | #include "kglobal.h" |
26 | #include "klocale.h" |
27 | #include "knotification.h" |
28 | |
29 | #include <QKeySequence> |
30 | #include <QDBusConnection> |
31 | |
32 | #ifdef Q_WS_X11 |
33 | #include "kglobalaccel_x11.h" |
34 | #include <QX11Info> |
35 | #include <QApplication> |
36 | #elif defined(Q_WS_MACX) |
37 | #include "kglobalaccel_mac.h" |
38 | #elif defined(Q_WS_WIN) |
39 | #include "kglobalaccel_win.h" |
40 | #else |
41 | #include "kglobalaccel_qws.h" |
42 | #endif |
43 | |
44 | GlobalShortcutsRegistry::GlobalShortcutsRegistry() |
45 | : QObject() |
46 | ,_active_keys() |
47 | ,_components() |
48 | ,_manager(new KGlobalAccelImpl(this)) |
49 | ,_config("kglobalshortcutsrc" , KConfig::SimpleConfig) |
50 | { |
51 | _manager->setEnabled(true); |
52 | } |
53 | |
54 | |
55 | GlobalShortcutsRegistry::~GlobalShortcutsRegistry() |
56 | { |
57 | _manager->setEnabled(false); |
58 | |
59 | // Ungrab all keys. We don't go over GlobalShortcuts because |
60 | // GlobalShortcutsRegistry::self() doesn't work anymore. |
61 | Q_FOREACH (const int key, _active_keys.keys()) |
62 | { |
63 | _manager->grabKey(key, false); |
64 | } |
65 | _active_keys.clear(); |
66 | } |
67 | |
68 | |
69 | KdeDGlobalAccel::Component *GlobalShortcutsRegistry::addComponent(KdeDGlobalAccel::Component *component) |
70 | { |
71 | if (_components.value(component->uniqueName())) |
72 | { |
73 | Q_ASSERT_X(false, "GlobalShortcutsRegistry::addComponent" , "component already registered?!?!" ); |
74 | return _components.value(component->uniqueName()); |
75 | } |
76 | |
77 | _components.insert(component->uniqueName(), component); |
78 | QDBusConnection conn(QDBusConnection::sessionBus()); |
79 | |
80 | conn.registerObject( |
81 | component->dbusPath().path(), |
82 | component, |
83 | QDBusConnection::ExportScriptableContents); |
84 | return component; |
85 | } |
86 | |
87 | |
88 | void GlobalShortcutsRegistry::activateShortcuts() |
89 | { |
90 | Q_FOREACH (KdeDGlobalAccel::Component *component, _components) |
91 | { |
92 | component->activateShortcuts(); |
93 | } |
94 | } |
95 | |
96 | |
97 | QList<KdeDGlobalAccel::Component*> GlobalShortcutsRegistry::allMainComponents() const |
98 | { |
99 | return _components.values(); |
100 | } |
101 | |
102 | |
103 | void GlobalShortcutsRegistry::clear() |
104 | { |
105 | Q_FOREACH(KdeDGlobalAccel::Component *component, _components) |
106 | { |
107 | delete component; |
108 | } |
109 | _components.clear(); |
110 | |
111 | // The shortcuts should have deregistered themselves |
112 | Q_ASSERT(_active_keys.isEmpty()); |
113 | } |
114 | |
115 | |
116 | QDBusObjectPath GlobalShortcutsRegistry::dbusPath() const |
117 | { |
118 | return _dbusPath; |
119 | } |
120 | |
121 | |
122 | void GlobalShortcutsRegistry::deactivateShortcuts(bool temporarily) |
123 | { |
124 | Q_FOREACH (KdeDGlobalAccel::Component *component, _components) |
125 | { |
126 | component->deactivateShortcuts(temporarily); |
127 | } |
128 | } |
129 | |
130 | |
131 | GlobalShortcut *GlobalShortcutsRegistry::getActiveShortcutByKey(int key) const |
132 | { |
133 | return _active_keys.value(key); |
134 | } |
135 | |
136 | |
137 | KdeDGlobalAccel::Component *GlobalShortcutsRegistry::getComponent(const QString &uniqueName) |
138 | { |
139 | return _components.value(uniqueName); |
140 | } |
141 | |
142 | |
143 | GlobalShortcut *GlobalShortcutsRegistry::getShortcutByKey(int key) const |
144 | { |
145 | Q_FOREACH (KdeDGlobalAccel::Component *component, _components) |
146 | { |
147 | GlobalShortcut *rc = component->getShortcutByKey(key); |
148 | if (rc) return rc; |
149 | } |
150 | return NULL; |
151 | } |
152 | |
153 | |
154 | QList<GlobalShortcut*> GlobalShortcutsRegistry::getShortcutsByKey(int key) const |
155 | { |
156 | QList<GlobalShortcut *> rc; |
157 | |
158 | Q_FOREACH (KdeDGlobalAccel::Component *component, _components) |
159 | { |
160 | rc = component->getShortcutsByKey(key); |
161 | if (!rc.isEmpty()) return rc; |
162 | } |
163 | return rc; |
164 | } |
165 | |
166 | |
167 | bool GlobalShortcutsRegistry::isShortcutAvailable( |
168 | int shortcut, |
169 | const QString &componentName, |
170 | const QString &contextName) const |
171 | { |
172 | Q_FOREACH (KdeDGlobalAccel::Component *component, _components) |
173 | { |
174 | if (!component->isShortcutAvailable(shortcut, componentName, contextName)) |
175 | return false; |
176 | } |
177 | return true; |
178 | } |
179 | |
180 | |
181 | GlobalShortcutsRegistry * GlobalShortcutsRegistry::self() |
182 | { |
183 | K_GLOBAL_STATIC( GlobalShortcutsRegistry, self ); |
184 | return self; |
185 | } |
186 | |
187 | |
188 | bool GlobalShortcutsRegistry::keyPressed(int keyQt) |
189 | { |
190 | GlobalShortcut *shortcut = getShortcutByKey(keyQt); |
191 | if (!shortcut) |
192 | { |
193 | // This can happen for example with the ALT-Print shortcut of kwin. |
194 | // ALT+PRINT is SYSREQ on my keyboard. So we grab something we think |
195 | // is ALT+PRINT but symXToKeyQt and modXToQt make ALT+SYSREQ of it |
196 | // when pressed (correctly). We can't match that. |
197 | kDebug() << "Got unknown key" << QKeySequence(keyQt).toString(); |
198 | |
199 | // In production mode just do nothing. |
200 | return false; |
201 | } |
202 | else if (!shortcut->isActive()) |
203 | { |
204 | kDebug() << "Got inactive key" << QKeySequence(keyQt).toString(); |
205 | |
206 | // In production mode just do nothing. |
207 | return false; |
208 | } |
209 | |
210 | kDebug() << QKeySequence(keyQt).toString() << "=" << shortcut->uniqueName(); |
211 | |
212 | QStringList data(shortcut->context()->component()->uniqueName()); |
213 | data.append(shortcut->uniqueName()); |
214 | data.append(shortcut->context()->component()->friendlyName()); |
215 | data.append(shortcut->friendlyName()); |
216 | #ifdef Q_WS_X11 |
217 | // Make sure kglobalacceld has ungrabbed the keyboard after receiving the |
218 | // keypress, otherwise actions in application that try to grab the |
219 | // keyboard (e.g. in kwin) may fail to do so. There is still a small race |
220 | // condition with this being out-of-process. |
221 | qApp->syncX(); |
222 | #endif |
223 | |
224 | // 1st Invoke the action |
225 | shortcut->context()->component()->emitGlobalShortcutPressed( *shortcut ); |
226 | |
227 | // Then do anything else |
228 | KNotification *notification = new KNotification( |
229 | "globalshortcutpressed" , |
230 | KNotification::CloseOnTimeout); |
231 | |
232 | notification->setText( |
233 | i18n("The global shortcut for %1 was issued." , shortcut->friendlyName())); |
234 | |
235 | notification->addContext( "application" , shortcut->context()->component()->friendlyName() ); |
236 | |
237 | notification->sendEvent(); |
238 | |
239 | return true; |
240 | } |
241 | |
242 | |
243 | void GlobalShortcutsRegistry::loadSettings() |
244 | { |
245 | foreach (const QString &groupName, _config.groupList()) |
246 | { |
247 | kDebug() << "Loading group " << groupName; |
248 | |
249 | Q_ASSERT(groupName.indexOf('\x1d')==-1); |
250 | |
251 | // loadSettings isn't designed to be called in between. Only at the |
252 | // beginning. |
253 | Q_ASSERT(!getComponent(groupName)); |
254 | |
255 | KConfigGroup configGroup(&_config, groupName); |
256 | |
257 | // We previously stored the friendly name in a separate group. migrate |
258 | // that |
259 | QString friendlyName; |
260 | KConfigGroup friendlyGroup(&configGroup, "Friendly Name" ); |
261 | if (friendlyGroup.isValid()) |
262 | { |
263 | friendlyName = friendlyGroup.readEntry("Friendly Name" ); |
264 | friendlyGroup.deleteGroup(); |
265 | } |
266 | else |
267 | { |
268 | friendlyName = configGroup.readEntry("_k_friendly_name" ); |
269 | } |
270 | |
271 | // Create the component |
272 | KdeDGlobalAccel::Component *component = new KdeDGlobalAccel::Component( |
273 | groupName, |
274 | friendlyName, |
275 | this); |
276 | |
277 | // Now load the contexts |
278 | Q_FOREACH(const QString& context, configGroup.groupList()) |
279 | { |
280 | // Skip the friendly name group |
281 | if (context=="Friendly Name" ) continue; |
282 | |
283 | KConfigGroup contextGroup(&configGroup, context); |
284 | QString contextFriendlyName = contextGroup.readEntry("_k_friendly_name" ); |
285 | component->createGlobalShortcutContext(context, contextFriendlyName); |
286 | component->activateGlobalShortcutContext(context); |
287 | component->loadSettings(contextGroup); |
288 | } |
289 | |
290 | // Load the default context |
291 | component->activateGlobalShortcutContext("default" ); |
292 | component->loadSettings(configGroup); |
293 | } |
294 | } |
295 | |
296 | |
297 | void GlobalShortcutsRegistry::grabKeys() |
298 | { |
299 | activateShortcuts(); |
300 | } |
301 | |
302 | |
303 | bool GlobalShortcutsRegistry::registerKey(int key, GlobalShortcut *shortcut) |
304 | { |
305 | if (key == 0) |
306 | { |
307 | kDebug() << shortcut->uniqueName() << ": Key '" << QKeySequence(key).toString() |
308 | << "' already taken by " << _active_keys.value(key)->uniqueName() << "." ; |
309 | return false; |
310 | } |
311 | else if (_active_keys.value(key)) |
312 | { |
313 | kDebug() << shortcut->uniqueName() << ": Attempt to register key 0." ; |
314 | return false; |
315 | } |
316 | |
317 | kDebug() << "Registering key" << QKeySequence(key).toString() << "for" |
318 | << shortcut->context()->component()->uniqueName() << ":" << shortcut->uniqueName(); |
319 | |
320 | _active_keys.insert(key, shortcut); |
321 | return _manager->grabKey(key, true); |
322 | } |
323 | |
324 | |
325 | void GlobalShortcutsRegistry::setAccelManager(KGlobalAccelImpl *manager) |
326 | { |
327 | _manager = manager; |
328 | } |
329 | |
330 | |
331 | void GlobalShortcutsRegistry::setDBusPath(const QDBusObjectPath &path) |
332 | { |
333 | _dbusPath = path; |
334 | } |
335 | |
336 | |
337 | KdeDGlobalAccel::Component *GlobalShortcutsRegistry::takeComponent(KdeDGlobalAccel::Component *component) |
338 | { |
339 | QDBusConnection conn(QDBusConnection::sessionBus()); |
340 | conn.unregisterObject(component->dbusPath().path()); |
341 | return _components.take(component->uniqueName()); |
342 | } |
343 | |
344 | |
345 | void GlobalShortcutsRegistry::ungrabKeys() |
346 | { |
347 | deactivateShortcuts(); |
348 | } |
349 | |
350 | |
351 | bool GlobalShortcutsRegistry::unregisterKey(int key, GlobalShortcut *shortcut) |
352 | { |
353 | if (_active_keys.value(key)!=shortcut) |
354 | { |
355 | // The shortcut doesn't own the key or the key isn't grabbed |
356 | return false; |
357 | } |
358 | |
359 | kDebug() << "Unregistering key" << QKeySequence(key).toString() << "for" |
360 | << shortcut->context()->component()->uniqueName() << ":" << shortcut->uniqueName(); |
361 | |
362 | _manager->grabKey(key, false); |
363 | _active_keys.take(key); |
364 | return true; |
365 | } |
366 | |
367 | |
368 | void GlobalShortcutsRegistry::writeSettings() const |
369 | { |
370 | Q_FOREACH( |
371 | const KdeDGlobalAccel::Component *component, |
372 | GlobalShortcutsRegistry::self()->allMainComponents()) |
373 | { |
374 | KConfigGroup configGroup(&_config, component->uniqueName()); |
375 | if (component->allShortcuts().isEmpty()) |
376 | { |
377 | configGroup.deleteGroup(); |
378 | delete component; |
379 | } |
380 | else |
381 | { |
382 | component->writeSettings(configGroup); |
383 | } |
384 | } |
385 | |
386 | _config.sync(); |
387 | } |
388 | |
389 | |
390 | #include "moc_globalshortcutsregistry.cpp" |
391 | |