Line |
Branch |
Exec |
Source |
1 |
|
|
/* |
2 |
|
|
* Copyright (C) 2010 Intel, Inc |
3 |
|
|
* Copyright (C) 2016 Endless, Inc |
4 |
|
|
* |
5 |
|
|
* This program is free software; you can redistribute it and/or modify |
6 |
|
|
* it under the terms of the GNU General Public License as published by |
7 |
|
|
* the Free Software Foundation; either version 2 of the License, or |
8 |
|
|
* (at your option) any later version. |
9 |
|
|
* |
10 |
|
|
* This program is distributed in the hope that it will be useful, |
11 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 |
|
|
* GNU General Public License for more details. |
14 |
|
|
* |
15 |
|
|
* You should have received a copy of the GNU General Public License |
16 |
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>. |
17 |
|
|
* |
18 |
|
|
* Author: Thomas Wood <thomas.wood@intel.com> |
19 |
|
|
* Georges Basile Stavracas Neto <georges.stavracas@gmail.com> |
20 |
|
|
* |
21 |
|
|
*/ |
22 |
|
|
|
23 |
|
|
#include <glib/gi18n.h> |
24 |
|
|
|
25 |
|
|
#include "cc-keyboard-manager.h" |
26 |
|
|
#include "keyboard-shortcuts.h" |
27 |
|
|
|
28 |
|
|
#include <gdk/gdk.h> |
29 |
|
|
#ifdef GDK_WINDOWING_X11 |
30 |
|
|
#include <gdk/x11/gdkx.h> |
31 |
|
|
#include <X11/Xatom.h> |
32 |
|
|
#endif |
33 |
|
|
|
34 |
|
|
#define BINDINGS_SCHEMA "org.gnome.settings-daemon.plugins.media-keys" |
35 |
|
|
#define CUSTOM_SHORTCUTS_ID "custom" |
36 |
|
|
|
37 |
|
|
struct _CcKeyboardManager |
38 |
|
|
{ |
39 |
|
|
GObject parent; |
40 |
|
|
|
41 |
|
|
GtkListStore *sections_store; |
42 |
|
|
|
43 |
|
|
GHashTable *kb_system_sections; |
44 |
|
|
GHashTable *kb_apps_sections; |
45 |
|
|
GHashTable *kb_user_sections; |
46 |
|
|
|
47 |
|
|
GSettings *binding_settings; |
48 |
|
|
}; |
49 |
|
|
|
50 |
|
✗ |
G_DEFINE_TYPE (CcKeyboardManager, cc_keyboard_manager, G_TYPE_OBJECT) |
51 |
|
|
|
52 |
|
|
enum |
53 |
|
|
{ |
54 |
|
|
SHORTCUT_ADDED, |
55 |
|
|
SHORTCUT_CHANGED, |
56 |
|
|
SHORTCUT_REMOVED, |
57 |
|
|
SHORTCUTS_LOADED, |
58 |
|
|
LAST_SIGNAL |
59 |
|
|
}; |
60 |
|
|
|
61 |
|
|
static guint signals[LAST_SIGNAL] = { 0, }; |
62 |
|
|
|
63 |
|
|
/* |
64 |
|
|
* Auxiliary methods |
65 |
|
|
*/ |
66 |
|
|
static void |
67 |
|
✗ |
free_key_array (GPtrArray *keys) |
68 |
|
|
{ |
69 |
|
✗ |
if (keys != NULL) |
70 |
|
|
{ |
71 |
|
|
gint i; |
72 |
|
|
|
73 |
|
✗ |
for (i = 0; i < keys->len; i++) |
74 |
|
|
{ |
75 |
|
|
CcKeyboardItem *item; |
76 |
|
|
|
77 |
|
✗ |
item = g_ptr_array_index (keys, i); |
78 |
|
|
|
79 |
|
✗ |
g_object_unref (item); |
80 |
|
|
} |
81 |
|
|
|
82 |
|
✗ |
g_ptr_array_free (keys, TRUE); |
83 |
|
|
} |
84 |
|
✗ |
} |
85 |
|
|
|
86 |
|
|
static gboolean |
87 |
|
✗ |
find_conflict (CcUniquenessData *data, |
88 |
|
|
CcKeyboardItem *item) |
89 |
|
|
{ |
90 |
|
|
GList *l; |
91 |
|
✗ |
gboolean is_conflict = FALSE; |
92 |
|
|
|
93 |
|
✗ |
if (data->orig_item && cc_keyboard_item_equal (data->orig_item, item)) |
94 |
|
✗ |
return FALSE; |
95 |
|
|
|
96 |
|
✗ |
for (l = cc_keyboard_item_get_key_combos (item); l; l = l->next) |
97 |
|
|
{ |
98 |
|
✗ |
CcKeyCombo *combo = l->data; |
99 |
|
|
|
100 |
|
✗ |
if (data->new_mask != combo->mask) |
101 |
|
✗ |
continue; |
102 |
|
|
|
103 |
|
✗ |
if (data->new_keyval != 0) |
104 |
|
✗ |
is_conflict = data->new_keyval == combo->keyval; |
105 |
|
|
else |
106 |
|
✗ |
is_conflict = combo->keyval == 0 && data->new_keycode == combo->keycode; |
107 |
|
|
|
108 |
|
✗ |
if (is_conflict) |
109 |
|
✗ |
break; |
110 |
|
|
} |
111 |
|
|
|
112 |
|
✗ |
if (is_conflict) |
113 |
|
✗ |
data->conflict_item = item; |
114 |
|
|
|
115 |
|
✗ |
return is_conflict; |
116 |
|
|
} |
117 |
|
|
|
118 |
|
|
static gboolean |
119 |
|
✗ |
compare_keys_for_uniqueness (CcKeyboardItem *current_item, |
120 |
|
|
CcUniquenessData *data) |
121 |
|
|
{ |
122 |
|
|
CcKeyboardItem *reverse_item; |
123 |
|
|
|
124 |
|
|
/* No conflict for: blanks or ourselves */ |
125 |
|
✗ |
if (!current_item || data->orig_item == current_item) |
126 |
|
✗ |
return FALSE; |
127 |
|
|
|
128 |
|
✗ |
reverse_item = cc_keyboard_item_get_reverse_item (current_item); |
129 |
|
|
|
130 |
|
|
/* When the current item is the reversed shortcut of a main item, simply ignore it */ |
131 |
|
✗ |
if (reverse_item && cc_keyboard_item_is_hidden (current_item)) |
132 |
|
✗ |
return FALSE; |
133 |
|
|
|
134 |
|
✗ |
if (find_conflict (data, current_item)) |
135 |
|
✗ |
return TRUE; |
136 |
|
|
|
137 |
|
|
/* Also check for the reverse item if any */ |
138 |
|
✗ |
if (reverse_item && find_conflict (data, reverse_item)) |
139 |
|
✗ |
return TRUE; |
140 |
|
|
|
141 |
|
✗ |
return FALSE; |
142 |
|
|
} |
143 |
|
|
|
144 |
|
|
static gboolean |
145 |
|
✗ |
check_for_uniqueness (gpointer key, |
146 |
|
|
GPtrArray *keys_array, |
147 |
|
|
CcUniquenessData *data) |
148 |
|
|
{ |
149 |
|
|
guint i; |
150 |
|
|
|
151 |
|
✗ |
for (i = 0; i < keys_array->len; i++) |
152 |
|
|
{ |
153 |
|
|
CcKeyboardItem *item; |
154 |
|
|
|
155 |
|
✗ |
item = keys_array->pdata[i]; |
156 |
|
|
|
157 |
|
✗ |
if (compare_keys_for_uniqueness (item, data)) |
158 |
|
✗ |
return TRUE; |
159 |
|
|
} |
160 |
|
|
|
161 |
|
✗ |
return FALSE; |
162 |
|
|
} |
163 |
|
|
|
164 |
|
|
|
165 |
|
|
static GHashTable* |
166 |
|
✗ |
get_hash_for_group (CcKeyboardManager *self, |
167 |
|
|
BindingGroupType group) |
168 |
|
|
{ |
169 |
|
|
GHashTable *hash; |
170 |
|
|
|
171 |
|
✗ |
switch (group) |
172 |
|
|
{ |
173 |
|
✗ |
case BINDING_GROUP_SYSTEM: |
174 |
|
✗ |
hash = self->kb_system_sections; |
175 |
|
✗ |
break; |
176 |
|
✗ |
case BINDING_GROUP_APPS: |
177 |
|
✗ |
hash = self->kb_apps_sections; |
178 |
|
✗ |
break; |
179 |
|
✗ |
case BINDING_GROUP_USER: |
180 |
|
✗ |
hash = self->kb_user_sections; |
181 |
|
✗ |
break; |
182 |
|
✗ |
default: |
183 |
|
✗ |
hash = NULL; |
184 |
|
|
} |
185 |
|
|
|
186 |
|
✗ |
return hash; |
187 |
|
|
} |
188 |
|
|
|
189 |
|
|
static gboolean |
190 |
|
✗ |
have_key_for_group (CcKeyboardManager *self, |
191 |
|
|
int group, |
192 |
|
|
const gchar *name) |
193 |
|
|
{ |
194 |
|
|
GHashTableIter iter; |
195 |
|
|
GPtrArray *keys; |
196 |
|
|
gint i; |
197 |
|
|
|
198 |
|
✗ |
g_hash_table_iter_init (&iter, get_hash_for_group (self, group)); |
199 |
|
✗ |
while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &keys)) |
200 |
|
|
{ |
201 |
|
✗ |
for (i = 0; i < keys->len; i++) |
202 |
|
|
{ |
203 |
|
✗ |
CcKeyboardItem *item = g_ptr_array_index (keys, i); |
204 |
|
|
|
205 |
|
✗ |
if (cc_keyboard_item_get_item_type (item) == CC_KEYBOARD_ITEM_TYPE_GSETTINGS && |
206 |
|
✗ |
g_strcmp0 (name, cc_keyboard_item_get_key (item)) == 0) |
207 |
|
|
{ |
208 |
|
✗ |
return TRUE; |
209 |
|
|
} |
210 |
|
|
} |
211 |
|
|
} |
212 |
|
|
|
213 |
|
✗ |
return FALSE; |
214 |
|
|
} |
215 |
|
|
|
216 |
|
|
static void |
217 |
|
✗ |
add_shortcuts (CcKeyboardManager *self) |
218 |
|
|
{ |
219 |
|
|
GtkTreeModel *sections_model; |
220 |
|
|
GtkTreeIter sections_iter; |
221 |
|
|
gboolean can_continue; |
222 |
|
|
|
223 |
|
✗ |
sections_model = GTK_TREE_MODEL (self->sections_store); |
224 |
|
✗ |
can_continue = gtk_tree_model_get_iter_first (sections_model, §ions_iter); |
225 |
|
|
|
226 |
|
✗ |
while (can_continue) |
227 |
|
|
{ |
228 |
|
|
BindingGroupType group; |
229 |
|
|
GPtrArray *keys; |
230 |
|
✗ |
g_autofree gchar *id = NULL; |
231 |
|
✗ |
g_autofree gchar *title = NULL; |
232 |
|
|
gint i; |
233 |
|
|
|
234 |
|
✗ |
gtk_tree_model_get (sections_model, |
235 |
|
|
§ions_iter, |
236 |
|
|
SECTION_DESCRIPTION_COLUMN, &title, |
237 |
|
|
SECTION_GROUP_COLUMN, &group, |
238 |
|
|
SECTION_ID_COLUMN, &id, |
239 |
|
|
-1); |
240 |
|
|
|
241 |
|
|
/* Ignore separators */ |
242 |
|
✗ |
if (group == BINDING_GROUP_SEPARATOR) |
243 |
|
|
{ |
244 |
|
✗ |
can_continue = gtk_tree_model_iter_next (sections_model, §ions_iter); |
245 |
|
✗ |
continue; |
246 |
|
|
} |
247 |
|
|
|
248 |
|
✗ |
keys = g_hash_table_lookup (get_hash_for_group (self, group), id); |
249 |
|
|
|
250 |
|
✗ |
for (i = 0; i < keys->len; i++) |
251 |
|
|
{ |
252 |
|
✗ |
CcKeyboardItem *item = g_ptr_array_index (keys, i); |
253 |
|
|
|
254 |
|
✗ |
if (!cc_keyboard_item_is_hidden (item)) |
255 |
|
|
{ |
256 |
|
✗ |
g_signal_emit (self, signals[SHORTCUT_ADDED], |
257 |
|
|
0, |
258 |
|
|
item, |
259 |
|
|
id, |
260 |
|
|
title); |
261 |
|
|
} |
262 |
|
|
} |
263 |
|
|
|
264 |
|
✗ |
can_continue = gtk_tree_model_iter_next (sections_model, §ions_iter); |
265 |
|
|
} |
266 |
|
|
|
267 |
|
✗ |
g_signal_emit (self, signals[SHORTCUTS_LOADED], 0); |
268 |
|
✗ |
} |
269 |
|
|
|
270 |
|
|
static void |
271 |
|
✗ |
append_section (CcKeyboardManager *self, |
272 |
|
|
const gchar *title, |
273 |
|
|
const gchar *id, |
274 |
|
|
BindingGroupType group, |
275 |
|
|
const KeyListEntry *keys_list) |
276 |
|
|
{ |
277 |
|
|
GtkTreeIter iter; |
278 |
|
|
GHashTable *reverse_items; |
279 |
|
|
GHashTable *hash; |
280 |
|
|
GPtrArray *keys_array; |
281 |
|
|
gboolean is_new; |
282 |
|
|
gint i; |
283 |
|
|
|
284 |
|
✗ |
hash = get_hash_for_group (self, group); |
285 |
|
|
|
286 |
|
✗ |
if (!hash) |
287 |
|
✗ |
return; |
288 |
|
|
|
289 |
|
|
/* Add all CcKeyboardItems for this section */ |
290 |
|
✗ |
is_new = FALSE; |
291 |
|
✗ |
keys_array = g_hash_table_lookup (hash, id); |
292 |
|
✗ |
if (keys_array == NULL) |
293 |
|
|
{ |
294 |
|
✗ |
keys_array = g_ptr_array_new (); |
295 |
|
✗ |
is_new = TRUE; |
296 |
|
|
} |
297 |
|
|
|
298 |
|
✗ |
reverse_items = g_hash_table_new (g_str_hash, g_str_equal); |
299 |
|
|
|
300 |
|
✗ |
for (i = 0; keys_list != NULL && keys_list[i].name != NULL; i++) |
301 |
|
|
{ |
302 |
|
|
CcKeyboardItem *item; |
303 |
|
|
gboolean ret; |
304 |
|
|
|
305 |
|
✗ |
if (have_key_for_group (self, group, keys_list[i].name)) |
306 |
|
✗ |
continue; |
307 |
|
|
|
308 |
|
✗ |
item = cc_keyboard_item_new (keys_list[i].type); |
309 |
|
|
|
310 |
|
✗ |
switch (keys_list[i].type) |
311 |
|
|
{ |
312 |
|
✗ |
case CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH: |
313 |
|
✗ |
ret = cc_keyboard_item_load_from_gsettings_path (item, keys_list[i].name, FALSE); |
314 |
|
✗ |
break; |
315 |
|
|
|
316 |
|
✗ |
case CC_KEYBOARD_ITEM_TYPE_GSETTINGS: |
317 |
|
✗ |
ret = cc_keyboard_item_load_from_gsettings (item, |
318 |
|
✗ |
keys_list[i].description, |
319 |
|
✗ |
keys_list[i].schema, |
320 |
|
✗ |
keys_list[i].name); |
321 |
|
✗ |
if (ret && keys_list[i].reverse_entry != NULL) |
322 |
|
|
{ |
323 |
|
|
CcKeyboardItem *reverse_item; |
324 |
|
✗ |
reverse_item = g_hash_table_lookup (reverse_items, |
325 |
|
✗ |
keys_list[i].reverse_entry); |
326 |
|
✗ |
if (reverse_item != NULL) |
327 |
|
|
{ |
328 |
|
✗ |
cc_keyboard_item_add_reverse_item (item, |
329 |
|
|
reverse_item, |
330 |
|
✗ |
keys_list[i].is_reversed); |
331 |
|
|
} |
332 |
|
|
else |
333 |
|
|
{ |
334 |
|
✗ |
g_hash_table_insert (reverse_items, |
335 |
|
✗ |
keys_list[i].name, |
336 |
|
|
item); |
337 |
|
|
} |
338 |
|
|
} |
339 |
|
✗ |
break; |
340 |
|
|
|
341 |
|
✗ |
default: |
342 |
|
✗ |
g_assert_not_reached (); |
343 |
|
|
} |
344 |
|
|
|
345 |
|
✗ |
if (ret == FALSE) |
346 |
|
|
{ |
347 |
|
|
/* We don't actually want to popup a dialog - just skip this one */ |
348 |
|
✗ |
g_object_unref (item); |
349 |
|
✗ |
continue; |
350 |
|
|
} |
351 |
|
|
|
352 |
|
✗ |
cc_keyboard_item_set_hidden (item, keys_list[i].hidden); |
353 |
|
|
|
354 |
|
✗ |
g_ptr_array_add (keys_array, item); |
355 |
|
|
} |
356 |
|
|
|
357 |
|
✗ |
g_hash_table_destroy (reverse_items); |
358 |
|
|
|
359 |
|
|
/* Add the keys to the hash table */ |
360 |
|
✗ |
if (is_new) |
361 |
|
|
{ |
362 |
|
✗ |
g_hash_table_insert (hash, g_strdup (id), keys_array); |
363 |
|
|
|
364 |
|
|
/* Append the section to the left tree view */ |
365 |
|
✗ |
gtk_list_store_append (GTK_LIST_STORE (self->sections_store), &iter); |
366 |
|
✗ |
gtk_list_store_set (GTK_LIST_STORE (self->sections_store), |
367 |
|
|
&iter, |
368 |
|
|
SECTION_DESCRIPTION_COLUMN, title, |
369 |
|
|
SECTION_ID_COLUMN, id, |
370 |
|
|
SECTION_GROUP_COLUMN, group, |
371 |
|
|
-1); |
372 |
|
|
} |
373 |
|
|
} |
374 |
|
|
|
375 |
|
|
static void |
376 |
|
✗ |
append_sections_from_file (CcKeyboardManager *self, |
377 |
|
|
const gchar *path, |
378 |
|
|
const char *datadir, |
379 |
|
|
gchar **wm_keybindings) |
380 |
|
|
{ |
381 |
|
|
KeyList *keylist; |
382 |
|
|
KeyListEntry *keys; |
383 |
|
✗ |
KeyListEntry key = { 0, 0, 0, 0, 0, 0, 0 }; |
384 |
|
|
const char *title; |
385 |
|
|
int group; |
386 |
|
|
guint i; |
387 |
|
|
|
388 |
|
✗ |
keylist = parse_keylist_from_file (path); |
389 |
|
|
|
390 |
|
✗ |
if (keylist == NULL) |
391 |
|
✗ |
return; |
392 |
|
|
|
393 |
|
|
#define const_strv(s) ((const gchar* const*) s) |
394 |
|
|
|
395 |
|
|
/* If there's no keys to add, or the settings apply to a window manager |
396 |
|
|
* that's not the one we're running */ |
397 |
|
✗ |
if (keylist->entries->len == 0 || |
398 |
|
✗ |
(keylist->wm_name != NULL && !g_strv_contains (const_strv (wm_keybindings), keylist->wm_name)) || |
399 |
|
✗ |
keylist->name == NULL) |
400 |
|
|
{ |
401 |
|
✗ |
g_free (keylist->name); |
402 |
|
✗ |
g_free (keylist->package); |
403 |
|
✗ |
g_free (keylist->wm_name); |
404 |
|
✗ |
g_array_free (keylist->entries, TRUE); |
405 |
|
✗ |
g_free (keylist); |
406 |
|
✗ |
return; |
407 |
|
|
} |
408 |
|
|
|
409 |
|
|
#undef const_strv |
410 |
|
|
|
411 |
|
|
/* Empty KeyListEntry to end the array */ |
412 |
|
✗ |
key.name = NULL; |
413 |
|
✗ |
g_array_append_val (keylist->entries, key); |
414 |
|
|
|
415 |
|
✗ |
keys = (KeyListEntry *) g_array_free (keylist->entries, FALSE); |
416 |
|
✗ |
if (keylist->package) |
417 |
|
|
{ |
418 |
|
✗ |
g_autofree gchar *localedir = NULL; |
419 |
|
|
|
420 |
|
✗ |
localedir = g_build_filename (datadir, "locale", NULL); |
421 |
|
✗ |
bindtextdomain (keylist->package, localedir); |
422 |
|
|
|
423 |
|
✗ |
title = dgettext (keylist->package, keylist->name); |
424 |
|
|
} else { |
425 |
|
✗ |
title = _(keylist->name); |
426 |
|
|
} |
427 |
|
|
|
428 |
|
✗ |
if (keylist->group && strcmp (keylist->group, "system") == 0) |
429 |
|
✗ |
group = BINDING_GROUP_SYSTEM; |
430 |
|
|
else |
431 |
|
✗ |
group = BINDING_GROUP_APPS; |
432 |
|
|
|
433 |
|
✗ |
append_section (self, title, keylist->name, group, keys); |
434 |
|
|
|
435 |
|
✗ |
g_free (keylist->name); |
436 |
|
✗ |
g_free (keylist->package); |
437 |
|
✗ |
g_free (keylist->wm_name); |
438 |
|
✗ |
g_free (keylist->schema); |
439 |
|
✗ |
g_free (keylist->group); |
440 |
|
|
|
441 |
|
✗ |
for (i = 0; keys[i].name != NULL; i++) |
442 |
|
|
{ |
443 |
|
✗ |
KeyListEntry *entry = &keys[i]; |
444 |
|
✗ |
g_free (entry->schema); |
445 |
|
✗ |
g_free (entry->description); |
446 |
|
✗ |
g_free (entry->name); |
447 |
|
✗ |
g_free (entry->reverse_entry); |
448 |
|
|
} |
449 |
|
|
|
450 |
|
✗ |
g_free (keylist); |
451 |
|
✗ |
g_free (keys); |
452 |
|
|
} |
453 |
|
|
|
454 |
|
|
static void |
455 |
|
✗ |
append_sections_from_gsettings (CcKeyboardManager *self) |
456 |
|
|
{ |
457 |
|
✗ |
g_auto(GStrv) custom_paths = NULL; |
458 |
|
|
GArray *entries; |
459 |
|
✗ |
KeyListEntry key = { 0, 0, 0, 0, 0, 0, 0 }; |
460 |
|
|
int i; |
461 |
|
|
|
462 |
|
|
/* load custom shortcuts from GSettings */ |
463 |
|
✗ |
entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry)); |
464 |
|
|
|
465 |
|
✗ |
custom_paths = g_settings_get_strv (self->binding_settings, "custom-keybindings"); |
466 |
|
✗ |
for (i = 0; custom_paths[i]; i++) |
467 |
|
|
{ |
468 |
|
✗ |
key.name = g_strdup (custom_paths[i]); |
469 |
|
✗ |
if (!have_key_for_group (self, BINDING_GROUP_USER, key.name)) |
470 |
|
|
{ |
471 |
|
✗ |
key.type = CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH; |
472 |
|
✗ |
g_array_append_val (entries, key); |
473 |
|
|
} |
474 |
|
|
else |
475 |
|
✗ |
g_free (key.name); |
476 |
|
|
} |
477 |
|
|
|
478 |
|
✗ |
if (entries->len > 0) |
479 |
|
|
{ |
480 |
|
|
KeyListEntry *keys; |
481 |
|
|
int i; |
482 |
|
|
|
483 |
|
|
/* Empty KeyListEntry to end the array */ |
484 |
|
✗ |
key.name = NULL; |
485 |
|
✗ |
g_array_append_val (entries, key); |
486 |
|
|
|
487 |
|
✗ |
keys = (KeyListEntry *) entries->data; |
488 |
|
✗ |
append_section (self, _("Custom Shortcuts"), CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, keys); |
489 |
|
✗ |
for (i = 0; i < entries->len; ++i) |
490 |
|
|
{ |
491 |
|
✗ |
g_free (keys[i].name); |
492 |
|
|
} |
493 |
|
|
} |
494 |
|
|
else |
495 |
|
|
{ |
496 |
|
✗ |
append_section (self, _("Custom Shortcuts"), CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, NULL); |
497 |
|
|
} |
498 |
|
|
|
499 |
|
✗ |
g_array_free (entries, TRUE); |
500 |
|
✗ |
} |
501 |
|
|
|
502 |
|
|
#ifdef GDK_WINDOWING_X11 |
503 |
|
|
static char * |
504 |
|
✗ |
get_window_manager_property (GdkDisplay *display, |
505 |
|
|
Atom atom, |
506 |
|
|
Window window) |
507 |
|
|
{ |
508 |
|
|
Display *xdisplay; |
509 |
|
|
Atom utf8_string; |
510 |
|
|
int result; |
511 |
|
|
Atom actual_type; |
512 |
|
|
int actual_format; |
513 |
|
|
unsigned long n_items; |
514 |
|
|
unsigned long bytes_after; |
515 |
|
|
unsigned char *prop; |
516 |
|
|
char *value; |
517 |
|
|
|
518 |
|
✗ |
if (window == None) |
519 |
|
✗ |
return NULL; |
520 |
|
|
|
521 |
|
✗ |
xdisplay = gdk_x11_display_get_xdisplay (display); |
522 |
|
✗ |
utf8_string = XInternAtom (xdisplay, "UTF8_STRING", False); |
523 |
|
|
|
524 |
|
✗ |
gdk_x11_display_error_trap_push (display); |
525 |
|
|
|
526 |
|
✗ |
result = XGetWindowProperty (xdisplay, |
527 |
|
|
window, |
528 |
|
|
atom, |
529 |
|
|
0, |
530 |
|
|
G_MAXLONG, |
531 |
|
|
False, |
532 |
|
|
utf8_string, |
533 |
|
|
&actual_type, |
534 |
|
|
&actual_format, |
535 |
|
|
&n_items, |
536 |
|
|
&bytes_after, |
537 |
|
|
&prop); |
538 |
|
|
|
539 |
|
✗ |
gdk_x11_display_error_trap_pop_ignored (display); |
540 |
|
|
|
541 |
|
✗ |
if (result != Success || |
542 |
|
✗ |
actual_type != utf8_string || |
543 |
|
✗ |
actual_format != 8 || |
544 |
|
✗ |
n_items == 0) |
545 |
|
|
{ |
546 |
|
✗ |
XFree (prop); |
547 |
|
✗ |
return NULL; |
548 |
|
|
} |
549 |
|
|
|
550 |
|
✗ |
value = g_strndup ((const char *) prop, n_items); |
551 |
|
✗ |
XFree (prop); |
552 |
|
|
|
553 |
|
✗ |
if (!g_utf8_validate (value, -1, NULL)) |
554 |
|
|
{ |
555 |
|
✗ |
g_free (value); |
556 |
|
✗ |
return NULL; |
557 |
|
|
} |
558 |
|
|
|
559 |
|
✗ |
return value; |
560 |
|
|
} |
561 |
|
|
|
562 |
|
|
static Window |
563 |
|
✗ |
get_wm_window (GdkDisplay *display) |
564 |
|
|
{ |
565 |
|
|
Display *xdisplay; |
566 |
|
|
Atom wm_check; |
567 |
|
|
int result; |
568 |
|
|
Atom actual_type; |
569 |
|
|
int actual_format; |
570 |
|
|
unsigned long n_items; |
571 |
|
|
unsigned long bytes_after; |
572 |
|
|
unsigned char *prop; |
573 |
|
|
Window wm_window; |
574 |
|
|
|
575 |
|
✗ |
xdisplay = gdk_x11_display_get_xdisplay (display); |
576 |
|
✗ |
wm_check = XInternAtom (xdisplay, "_NET_SUPPORTING_WM_CHECK", False); |
577 |
|
|
|
578 |
|
✗ |
gdk_x11_display_error_trap_push (display); |
579 |
|
|
|
580 |
|
✗ |
result = XGetWindowProperty (xdisplay, |
581 |
|
|
XDefaultRootWindow (xdisplay), |
582 |
|
|
wm_check, |
583 |
|
|
0, |
584 |
|
|
G_MAXLONG, |
585 |
|
|
False, |
586 |
|
|
XA_WINDOW, |
587 |
|
|
&actual_type, |
588 |
|
|
&actual_format, |
589 |
|
|
&n_items, |
590 |
|
|
&bytes_after, |
591 |
|
|
&prop); |
592 |
|
|
|
593 |
|
✗ |
gdk_x11_display_error_trap_pop_ignored (display); |
594 |
|
|
|
595 |
|
✗ |
if (result != Success || |
596 |
|
✗ |
actual_type != XA_WINDOW || |
597 |
|
✗ |
n_items == 0) |
598 |
|
|
{ |
599 |
|
✗ |
XFree (prop); |
600 |
|
✗ |
return None; |
601 |
|
|
} |
602 |
|
|
|
603 |
|
✗ |
wm_window = *(Window *) prop; |
604 |
|
✗ |
XFree (prop); |
605 |
|
|
|
606 |
|
✗ |
return wm_window; |
607 |
|
|
} |
608 |
|
|
#endif |
609 |
|
|
|
610 |
|
|
static GStrv |
611 |
|
✗ |
get_current_keybindings (void) |
612 |
|
|
{ |
613 |
|
|
#ifdef GDK_WINDOWING_X11 |
614 |
|
|
GdkDisplay *display; |
615 |
|
|
Display *xdisplay; |
616 |
|
|
Atom keybindings_atom; |
617 |
|
|
Window wm_window; |
618 |
|
|
char *keybindings; |
619 |
|
|
GStrv results; |
620 |
|
|
|
621 |
|
✗ |
display = gdk_display_get_default (); |
622 |
|
✗ |
if (!GDK_IS_X11_DISPLAY (display)) |
623 |
|
✗ |
return NULL; |
624 |
|
|
|
625 |
|
✗ |
xdisplay = gdk_x11_display_get_xdisplay (display); |
626 |
|
✗ |
keybindings_atom = XInternAtom (xdisplay, "_GNOME_WM_KEYBINDINGS", False); |
627 |
|
|
|
628 |
|
✗ |
wm_window = get_wm_window (display); |
629 |
|
✗ |
keybindings = get_window_manager_property (display, |
630 |
|
|
keybindings_atom, |
631 |
|
|
wm_window); |
632 |
|
|
|
633 |
|
✗ |
if (keybindings != NULL) |
634 |
|
|
{ |
635 |
|
|
GStrv p; |
636 |
|
|
|
637 |
|
✗ |
results = g_strsplit (keybindings, ",", -1); |
638 |
|
|
|
639 |
|
✗ |
for (p = results; p && *p; p++) |
640 |
|
✗ |
g_strstrip (*p); |
641 |
|
|
|
642 |
|
✗ |
g_free (keybindings); |
643 |
|
|
} |
644 |
|
|
else |
645 |
|
|
{ |
646 |
|
|
Atom wm_atom; |
647 |
|
|
char *wm_name; |
648 |
|
|
|
649 |
|
✗ |
wm_atom = XInternAtom (xdisplay, "_NET_WM_NAME", False); |
650 |
|
✗ |
wm_name = get_window_manager_property (display, wm_atom, wm_window); |
651 |
|
|
|
652 |
|
✗ |
results = g_new0 (char *, 2); |
653 |
|
✗ |
results[0] = wm_name ? wm_name : g_strdup ("Unknown"); |
654 |
|
|
} |
655 |
|
|
|
656 |
|
✗ |
return results; |
657 |
|
|
#else |
658 |
|
|
return NULL; |
659 |
|
|
#endif |
660 |
|
|
} |
661 |
|
|
|
662 |
|
|
static void |
663 |
|
✗ |
reload_sections (CcKeyboardManager *self) |
664 |
|
|
{ |
665 |
|
|
GHashTable *loaded_files; |
666 |
|
|
GDir *dir; |
667 |
|
✗ |
gchar *default_wm_keybindings[] = { "Mutter", "GNOME Shell", NULL }; |
668 |
|
✗ |
g_auto(GStrv) wm_keybindings = NULL; |
669 |
|
|
const gchar * const * data_dirs; |
670 |
|
|
guint i; |
671 |
|
|
|
672 |
|
|
/* Clear previous models and hash tables */ |
673 |
|
✗ |
gtk_list_store_clear (GTK_LIST_STORE (self->sections_store)); |
674 |
|
|
|
675 |
|
✗ |
g_clear_pointer (&self->kb_system_sections, g_hash_table_destroy); |
676 |
|
✗ |
self->kb_system_sections = g_hash_table_new_full (g_str_hash, |
677 |
|
|
g_str_equal, |
678 |
|
|
g_free, |
679 |
|
|
(GDestroyNotify) free_key_array); |
680 |
|
|
|
681 |
|
✗ |
g_clear_pointer (&self->kb_apps_sections, g_hash_table_destroy); |
682 |
|
✗ |
self->kb_apps_sections = g_hash_table_new_full (g_str_hash, |
683 |
|
|
g_str_equal, |
684 |
|
|
g_free, |
685 |
|
|
(GDestroyNotify) free_key_array); |
686 |
|
|
|
687 |
|
✗ |
g_clear_pointer (&self->kb_user_sections, g_hash_table_destroy); |
688 |
|
✗ |
self->kb_user_sections = g_hash_table_new_full (g_str_hash, |
689 |
|
|
g_str_equal, |
690 |
|
|
g_free, |
691 |
|
|
(GDestroyNotify) free_key_array); |
692 |
|
|
|
693 |
|
|
/* Load WM keybindings */ |
694 |
|
✗ |
wm_keybindings = get_current_keybindings (); |
695 |
|
|
|
696 |
|
✗ |
if (wm_keybindings == NULL) |
697 |
|
✗ |
wm_keybindings = g_strdupv (default_wm_keybindings); |
698 |
|
|
|
699 |
|
✗ |
loaded_files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); |
700 |
|
|
|
701 |
|
✗ |
data_dirs = g_get_system_data_dirs (); |
702 |
|
✗ |
for (i = 0; data_dirs[i] != NULL; i++) |
703 |
|
|
{ |
704 |
|
✗ |
g_autofree gchar *dir_path = NULL; |
705 |
|
|
const gchar *name; |
706 |
|
|
|
707 |
|
✗ |
dir_path = g_build_filename (data_dirs[i], "gnome-control-center", "keybindings", NULL); |
708 |
|
|
|
709 |
|
✗ |
dir = g_dir_open (dir_path, 0, NULL); |
710 |
|
✗ |
if (!dir) |
711 |
|
✗ |
continue; |
712 |
|
|
|
713 |
|
✗ |
for (name = g_dir_read_name (dir) ; name ; name = g_dir_read_name (dir)) |
714 |
|
|
{ |
715 |
|
✗ |
g_autofree gchar *path = NULL; |
716 |
|
|
|
717 |
|
✗ |
if (g_str_has_suffix (name, ".xml") == FALSE) |
718 |
|
✗ |
continue; |
719 |
|
|
|
720 |
|
✗ |
if (g_hash_table_lookup (loaded_files, name) != NULL) |
721 |
|
|
{ |
722 |
|
✗ |
g_debug ("Not loading %s, it was already loaded from another directory", name); |
723 |
|
✗ |
continue; |
724 |
|
|
} |
725 |
|
|
|
726 |
|
✗ |
g_hash_table_insert (loaded_files, g_strdup (name), GINT_TO_POINTER (1)); |
727 |
|
✗ |
path = g_build_filename (dir_path, name, NULL); |
728 |
|
✗ |
append_sections_from_file (self, path, data_dirs[i], wm_keybindings); |
729 |
|
|
} |
730 |
|
|
|
731 |
|
✗ |
g_dir_close (dir); |
732 |
|
|
} |
733 |
|
|
|
734 |
|
✗ |
g_hash_table_destroy (loaded_files); |
735 |
|
|
|
736 |
|
|
/* Load custom keybindings */ |
737 |
|
✗ |
append_sections_from_gsettings (self); |
738 |
|
✗ |
} |
739 |
|
|
|
740 |
|
|
/* |
741 |
|
|
* Callbacks |
742 |
|
|
*/ |
743 |
|
|
static void |
744 |
|
✗ |
cc_keyboard_manager_finalize (GObject *object) |
745 |
|
|
{ |
746 |
|
✗ |
CcKeyboardManager *self = (CcKeyboardManager *)object; |
747 |
|
|
|
748 |
|
✗ |
g_clear_pointer (&self->kb_system_sections, g_hash_table_destroy); |
749 |
|
✗ |
g_clear_pointer (&self->kb_apps_sections, g_hash_table_destroy); |
750 |
|
✗ |
g_clear_pointer (&self->kb_user_sections, g_hash_table_destroy); |
751 |
|
✗ |
g_clear_object (&self->binding_settings); |
752 |
|
✗ |
g_clear_object (&self->sections_store); |
753 |
|
|
|
754 |
|
✗ |
G_OBJECT_CLASS (cc_keyboard_manager_parent_class)->finalize (object); |
755 |
|
✗ |
} |
756 |
|
|
|
757 |
|
|
static void |
758 |
|
✗ |
cc_keyboard_manager_get_property (GObject *object, |
759 |
|
|
guint prop_id, |
760 |
|
|
GValue *value, |
761 |
|
|
GParamSpec *pspec) |
762 |
|
|
{ |
763 |
|
✗ |
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
764 |
|
✗ |
} |
765 |
|
|
|
766 |
|
|
static void |
767 |
|
✗ |
cc_keyboard_manager_set_property (GObject *object, |
768 |
|
|
guint prop_id, |
769 |
|
|
const GValue *value, |
770 |
|
|
GParamSpec *pspec) |
771 |
|
|
{ |
772 |
|
✗ |
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
773 |
|
✗ |
} |
774 |
|
|
|
775 |
|
|
static void |
776 |
|
✗ |
cc_keyboard_manager_class_init (CcKeyboardManagerClass *klass) |
777 |
|
|
{ |
778 |
|
✗ |
GObjectClass *object_class = G_OBJECT_CLASS (klass); |
779 |
|
|
|
780 |
|
✗ |
object_class->finalize = cc_keyboard_manager_finalize; |
781 |
|
✗ |
object_class->get_property = cc_keyboard_manager_get_property; |
782 |
|
✗ |
object_class->set_property = cc_keyboard_manager_set_property; |
783 |
|
|
|
784 |
|
|
/** |
785 |
|
|
* CcKeyboardManager:shortcut-added: |
786 |
|
|
* |
787 |
|
|
* Emitted when a shortcut is added. |
788 |
|
|
*/ |
789 |
|
✗ |
signals[SHORTCUT_ADDED] = g_signal_new ("shortcut-added", |
790 |
|
|
CC_TYPE_KEYBOARD_MANAGER, |
791 |
|
|
G_SIGNAL_RUN_FIRST, |
792 |
|
|
0, NULL, NULL, NULL, |
793 |
|
|
G_TYPE_NONE, |
794 |
|
|
3, |
795 |
|
|
CC_TYPE_KEYBOARD_ITEM, |
796 |
|
|
G_TYPE_STRING, |
797 |
|
|
G_TYPE_STRING); |
798 |
|
|
|
799 |
|
|
/** |
800 |
|
|
* CcKeyboardManager:shortcut-changed: |
801 |
|
|
* |
802 |
|
|
* Emitted when a shortcut is added. |
803 |
|
|
*/ |
804 |
|
✗ |
signals[SHORTCUT_CHANGED] = g_signal_new ("shortcut-changed", |
805 |
|
|
CC_TYPE_KEYBOARD_MANAGER, |
806 |
|
|
G_SIGNAL_RUN_FIRST, |
807 |
|
|
0, NULL, NULL, NULL, |
808 |
|
|
G_TYPE_NONE, |
809 |
|
|
1, |
810 |
|
|
CC_TYPE_KEYBOARD_ITEM); |
811 |
|
|
|
812 |
|
|
|
813 |
|
|
/** |
814 |
|
|
* CcKeyboardManager:shortcut-removed: |
815 |
|
|
* |
816 |
|
|
* Emitted when a shortcut is removed. |
817 |
|
|
*/ |
818 |
|
✗ |
signals[SHORTCUT_REMOVED] = g_signal_new ("shortcut-removed", |
819 |
|
|
CC_TYPE_KEYBOARD_MANAGER, |
820 |
|
|
G_SIGNAL_RUN_FIRST, |
821 |
|
|
0, NULL, NULL, NULL, |
822 |
|
|
G_TYPE_NONE, |
823 |
|
|
1, |
824 |
|
|
CC_TYPE_KEYBOARD_ITEM); |
825 |
|
|
|
826 |
|
|
/** |
827 |
|
|
* CcKeyboardManager:shortcuts-loaded: |
828 |
|
|
* |
829 |
|
|
* Emitted after all shortcuts are loaded. |
830 |
|
|
*/ |
831 |
|
✗ |
signals[SHORTCUTS_LOADED] = g_signal_new ("shortcuts-loaded", |
832 |
|
|
CC_TYPE_KEYBOARD_MANAGER, |
833 |
|
|
G_SIGNAL_RUN_FIRST, |
834 |
|
|
0, NULL, NULL, NULL, |
835 |
|
|
G_TYPE_NONE, 0); |
836 |
|
✗ |
} |
837 |
|
|
|
838 |
|
|
static void |
839 |
|
✗ |
cc_keyboard_manager_init (CcKeyboardManager *self) |
840 |
|
|
{ |
841 |
|
|
/* Bindings */ |
842 |
|
✗ |
self->binding_settings = g_settings_new (BINDINGS_SCHEMA); |
843 |
|
|
|
844 |
|
|
/* Setup the section models */ |
845 |
|
✗ |
self->sections_store = gtk_list_store_new (SECTION_N_COLUMNS, |
846 |
|
|
G_TYPE_STRING, |
847 |
|
|
G_TYPE_STRING, |
848 |
|
|
G_TYPE_INT); |
849 |
|
✗ |
} |
850 |
|
|
|
851 |
|
|
|
852 |
|
|
CcKeyboardManager * |
853 |
|
✗ |
cc_keyboard_manager_new (void) |
854 |
|
|
{ |
855 |
|
✗ |
return g_object_new (CC_TYPE_KEYBOARD_MANAGER, NULL); |
856 |
|
|
} |
857 |
|
|
|
858 |
|
|
void |
859 |
|
✗ |
cc_keyboard_manager_load_shortcuts (CcKeyboardManager *self) |
860 |
|
|
{ |
861 |
|
✗ |
g_return_if_fail (CC_IS_KEYBOARD_MANAGER (self)); |
862 |
|
|
|
863 |
|
✗ |
reload_sections (self); |
864 |
|
✗ |
add_shortcuts (self); |
865 |
|
|
} |
866 |
|
|
|
867 |
|
|
/** |
868 |
|
|
* cc_keyboard_manager_create_custom_shortcut: |
869 |
|
|
* @self: a #CcKeyboardPanel |
870 |
|
|
* |
871 |
|
|
* Creates a new temporary keyboard shortcut. |
872 |
|
|
* |
873 |
|
|
* Returns: (transfer full): a #CcKeyboardItem |
874 |
|
|
*/ |
875 |
|
|
CcKeyboardItem* |
876 |
|
✗ |
cc_keyboard_manager_create_custom_shortcut (CcKeyboardManager *self) |
877 |
|
|
{ |
878 |
|
|
CcKeyboardItem *item; |
879 |
|
✗ |
g_autofree gchar *settings_path = NULL; |
880 |
|
|
|
881 |
|
✗ |
g_return_val_if_fail (CC_IS_KEYBOARD_MANAGER (self), NULL); |
882 |
|
|
|
883 |
|
✗ |
item = cc_keyboard_item_new (CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH); |
884 |
|
|
|
885 |
|
✗ |
settings_path = find_free_settings_path (self->binding_settings); |
886 |
|
✗ |
cc_keyboard_item_load_from_gsettings_path (item, settings_path, TRUE); |
887 |
|
|
|
888 |
|
✗ |
return item; |
889 |
|
|
} |
890 |
|
|
|
891 |
|
|
/** |
892 |
|
|
* cc_keyboard_manager_add_custom_shortcut: |
893 |
|
|
* @self: a #CcKeyboardPanel |
894 |
|
|
* @item: the #CcKeyboardItem to be added |
895 |
|
|
* |
896 |
|
|
* Effectively adds the custom shortcut. |
897 |
|
|
*/ |
898 |
|
|
void |
899 |
|
✗ |
cc_keyboard_manager_add_custom_shortcut (CcKeyboardManager *self, |
900 |
|
|
CcKeyboardItem *item) |
901 |
|
|
{ |
902 |
|
|
GPtrArray *keys_array; |
903 |
|
|
GHashTable *hash; |
904 |
|
|
GVariantBuilder builder; |
905 |
|
|
char **settings_paths; |
906 |
|
|
int i; |
907 |
|
|
|
908 |
|
✗ |
g_return_if_fail (CC_IS_KEYBOARD_MANAGER (self)); |
909 |
|
|
|
910 |
|
✗ |
hash = get_hash_for_group (self, BINDING_GROUP_USER); |
911 |
|
✗ |
keys_array = g_hash_table_lookup (hash, CUSTOM_SHORTCUTS_ID); |
912 |
|
|
|
913 |
|
✗ |
if (keys_array == NULL) |
914 |
|
|
{ |
915 |
|
✗ |
keys_array = g_ptr_array_new (); |
916 |
|
✗ |
g_hash_table_insert (hash, g_strdup (CUSTOM_SHORTCUTS_ID), keys_array); |
917 |
|
|
} |
918 |
|
|
|
919 |
|
✗ |
g_ptr_array_add (keys_array, item); |
920 |
|
|
|
921 |
|
✗ |
settings_paths = g_settings_get_strv (self->binding_settings, "custom-keybindings"); |
922 |
|
|
|
923 |
|
✗ |
g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); |
924 |
|
|
|
925 |
|
✗ |
for (i = 0; settings_paths[i]; i++) |
926 |
|
✗ |
g_variant_builder_add (&builder, "s", settings_paths[i]); |
927 |
|
|
|
928 |
|
✗ |
g_variant_builder_add (&builder, "s", cc_keyboard_item_get_gsettings_path (item)); |
929 |
|
|
|
930 |
|
✗ |
g_settings_set_value (self->binding_settings, "custom-keybindings", g_variant_builder_end (&builder)); |
931 |
|
|
|
932 |
|
✗ |
g_signal_emit (self, signals[SHORTCUT_ADDED], |
933 |
|
|
0, |
934 |
|
|
item, |
935 |
|
|
CUSTOM_SHORTCUTS_ID, |
936 |
|
|
_("Custom Shortcuts")); |
937 |
|
|
} |
938 |
|
|
|
939 |
|
|
/** |
940 |
|
|
* cc_keyboard_manager_remove_custom_shortcut: |
941 |
|
|
* @self: a #CcKeyboardPanel |
942 |
|
|
* @item: the #CcKeyboardItem to be added |
943 |
|
|
* |
944 |
|
|
* Removed the custom shortcut. |
945 |
|
|
*/ |
946 |
|
|
void |
947 |
|
✗ |
cc_keyboard_manager_remove_custom_shortcut (CcKeyboardManager *self, |
948 |
|
|
CcKeyboardItem *item) |
949 |
|
|
{ |
950 |
|
|
GPtrArray *keys_array; |
951 |
|
|
GVariantBuilder builder; |
952 |
|
|
GSettings *settings; |
953 |
|
|
char **settings_paths; |
954 |
|
|
int i; |
955 |
|
|
|
956 |
|
✗ |
g_return_if_fail (CC_IS_KEYBOARD_MANAGER (self)); |
957 |
|
|
|
958 |
|
|
/* Shortcut not a custom shortcut */ |
959 |
|
✗ |
g_assert (cc_keyboard_item_get_item_type (item) == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH); |
960 |
|
|
|
961 |
|
✗ |
settings = cc_keyboard_item_get_settings (item); |
962 |
|
✗ |
g_settings_delay (settings); |
963 |
|
✗ |
g_settings_reset (settings, "name"); |
964 |
|
✗ |
g_settings_reset (settings, "command"); |
965 |
|
✗ |
g_settings_reset (settings, "binding"); |
966 |
|
✗ |
g_settings_apply (settings); |
967 |
|
✗ |
g_settings_sync (); |
968 |
|
|
|
969 |
|
✗ |
settings_paths = g_settings_get_strv (self->binding_settings, "custom-keybindings"); |
970 |
|
✗ |
g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); |
971 |
|
|
|
972 |
|
✗ |
for (i = 0; settings_paths[i]; i++) |
973 |
|
✗ |
if (strcmp (settings_paths[i], cc_keyboard_item_get_gsettings_path (item)) != 0) |
974 |
|
✗ |
g_variant_builder_add (&builder, "s", settings_paths[i]); |
975 |
|
|
|
976 |
|
✗ |
g_settings_set_value (self->binding_settings, |
977 |
|
|
"custom-keybindings", |
978 |
|
|
g_variant_builder_end (&builder)); |
979 |
|
|
|
980 |
|
✗ |
g_strfreev (settings_paths); |
981 |
|
|
|
982 |
|
✗ |
keys_array = g_hash_table_lookup (get_hash_for_group (self, BINDING_GROUP_USER), CUSTOM_SHORTCUTS_ID); |
983 |
|
✗ |
g_ptr_array_remove (keys_array, item); |
984 |
|
|
|
985 |
|
✗ |
g_signal_emit (self, signals[SHORTCUT_REMOVED], 0, item); |
986 |
|
|
} |
987 |
|
|
|
988 |
|
|
/** |
989 |
|
|
* cc_keyboard_manager_get_collision: |
990 |
|
|
* @self: a #CcKeyboardManager |
991 |
|
|
* @item: (nullable): a keyboard shortcut |
992 |
|
|
* @combo: a #CcKeyCombo |
993 |
|
|
* |
994 |
|
|
* Retrieves the collision item for the given shortcut. |
995 |
|
|
* |
996 |
|
|
* Returns: (transfer none)(nullable): the collisioned shortcut |
997 |
|
|
*/ |
998 |
|
|
CcKeyboardItem* |
999 |
|
✗ |
cc_keyboard_manager_get_collision (CcKeyboardManager *self, |
1000 |
|
|
CcKeyboardItem *item, |
1001 |
|
|
CcKeyCombo *combo) |
1002 |
|
|
{ |
1003 |
|
|
CcUniquenessData data; |
1004 |
|
|
BindingGroupType i; |
1005 |
|
|
|
1006 |
|
✗ |
g_return_val_if_fail (CC_IS_KEYBOARD_MANAGER (self), NULL); |
1007 |
|
|
|
1008 |
|
✗ |
data.orig_item = item; |
1009 |
|
✗ |
data.new_keyval = combo->keyval; |
1010 |
|
✗ |
data.new_mask = combo->mask; |
1011 |
|
✗ |
data.new_keycode = combo->keycode; |
1012 |
|
✗ |
data.conflict_item = NULL; |
1013 |
|
|
|
1014 |
|
✗ |
if (combo->keyval == 0 && combo->keycode == 0) |
1015 |
|
✗ |
return NULL; |
1016 |
|
|
|
1017 |
|
|
/* Any number of shortcuts can be disabled */ |
1018 |
|
✗ |
for (i = BINDING_GROUP_SYSTEM; i <= BINDING_GROUP_USER && !data.conflict_item; i++) |
1019 |
|
|
{ |
1020 |
|
|
GHashTable *table; |
1021 |
|
|
|
1022 |
|
✗ |
table = get_hash_for_group (self, i); |
1023 |
|
|
|
1024 |
|
✗ |
if (!table) |
1025 |
|
✗ |
continue; |
1026 |
|
|
|
1027 |
|
✗ |
g_hash_table_find (table, (GHRFunc) check_for_uniqueness, &data); |
1028 |
|
|
} |
1029 |
|
|
|
1030 |
|
✗ |
return data.conflict_item; |
1031 |
|
|
} |
1032 |
|
|
|
1033 |
|
|
/** |
1034 |
|
|
* cc_keyboard_manager_reset_shortcut: |
1035 |
|
|
* @self: a #CcKeyboardManager |
1036 |
|
|
* @item: a #CcKeyboardItem |
1037 |
|
|
* |
1038 |
|
|
* Resets the keyboard shortcut managed by @item, and eventually |
1039 |
|
|
* disables any shortcut that conflicts with the new shortcut's |
1040 |
|
|
* value. |
1041 |
|
|
*/ |
1042 |
|
|
void |
1043 |
|
✗ |
cc_keyboard_manager_reset_shortcut (CcKeyboardManager *self, |
1044 |
|
|
CcKeyboardItem *item) |
1045 |
|
|
{ |
1046 |
|
|
GList *l; |
1047 |
|
|
|
1048 |
|
✗ |
g_return_if_fail (CC_IS_KEYBOARD_MANAGER (self)); |
1049 |
|
✗ |
g_return_if_fail (CC_IS_KEYBOARD_ITEM (item)); |
1050 |
|
|
|
1051 |
|
|
/* Disables any shortcut that conflicts with the new shortcut's value */ |
1052 |
|
✗ |
for (l = cc_keyboard_item_get_default_combos (item); l; l = l->next) |
1053 |
|
|
{ |
1054 |
|
✗ |
CcKeyCombo *combo = l->data; |
1055 |
|
|
CcKeyboardItem *collision; |
1056 |
|
|
|
1057 |
|
✗ |
collision = cc_keyboard_manager_get_collision (self, NULL, combo); |
1058 |
|
✗ |
if (collision) |
1059 |
|
✗ |
cc_keyboard_item_remove_key_combo (collision, combo); |
1060 |
|
|
} |
1061 |
|
|
|
1062 |
|
|
/* Resets the current item */ |
1063 |
|
✗ |
cc_keyboard_item_reset (item); |
1064 |
|
|
} |
1065 |
|
|
|