GCC Code Coverage Report


Directory: ./
File: panels/keyboard/cc-keyboard-item.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 399 0.0%
Functions: 0 51 0.0%
Branches: 0 219 0.0%

Line Branch Exec Source
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2011, 2014 Red Hat, 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 */
19
20 #include "config.h"
21
22 #include <stdlib.h>
23 #include <stdio.h>
24
25 #include <gtk/gtk.h>
26 #include <gio/gio.h>
27 #include <glib/gi18n-lib.h>
28
29 #include "cc-util.h"
30 #include "keyboard-shortcuts.h"
31 #include "cc-keyboard-item.h"
32
33 #define CUSTOM_KEYS_SCHEMA "org.gnome.settings-daemon.plugins.media-keys.custom-keybinding"
34 #define SHORTCUT_DELIMITERS "+ "
35
36 struct _CcKeyboardItem
37 {
38 GObject parent_instance;
39
40 CcKeyboardItem *reverse_item;
41 gboolean is_reversed;
42 gboolean hidden;
43
44 CcKeyboardItemType type;
45
46 BindingGroupType group;
47 char *description;
48 gboolean editable;
49 GList *key_combos;
50 GList *default_combos;
51 gboolean can_set_multiple;
52
53 /* GSettings path */
54 char *gsettings_path;
55 gboolean desc_editable;
56 char *command;
57 gboolean cmd_editable;
58
59 /* GSettings */
60 char *schema;
61 char *key;
62 GSettings *settings;
63 };
64
65 enum
66 {
67 PROP_0,
68 PROP_DESCRIPTION,
69 PROP_EDITABLE,
70 PROP_TYPE,
71 PROP_IS_VALUE_DEFAULT,
72 PROP_COMMAND,
73 PROP_KEY_COMBOS
74 };
75
76 static void cc_keyboard_item_class_init (CcKeyboardItemClass *klass);
77 static void cc_keyboard_item_init (CcKeyboardItem *keyboard_item);
78 static void cc_keyboard_item_finalize (GObject *object);
79
80 G_DEFINE_TYPE (CcKeyboardItem, cc_keyboard_item, G_TYPE_OBJECT)
81
82 static const CcKeyCombo EMPTY_COMBO = { 0, 0, 0 };
83
84 static gboolean
85 combo_equal (CcKeyCombo *a, CcKeyCombo *b)
86 {
87 return (a->keyval == b->keyval
88 && a->keycode == b->keycode
89 && a->mask == b->mask);
90 }
91
92 static gboolean
93 combos_contains (GList *combos, CcKeyCombo *needle)
94 {
95 for (GList *l = combos; l != NULL; l = l->next)
96 {
97 if (combo_equal (l->data, needle))
98 return TRUE;
99 }
100
101 return FALSE;
102 }
103
104 static gboolean
105 combos_equal (GList *a, GList *b)
106 {
107 // Should be efficient enough for any sane number of bindings
108
109 for (GList *l = a; l != NULL; l = l->next)
110 {
111 if (!combos_contains (b, l->data))
112 return FALSE;
113 }
114
115 for (GList *l = b; l != NULL; l = l->next)
116 {
117 if (!combos_contains (a, l->data))
118 return FALSE;
119 }
120
121 return TRUE;
122 }
123
124 static gboolean
125 binding_from_string (const char *str,
126 CcKeyCombo *combo)
127 {
128 g_return_val_if_fail (combo != NULL, FALSE);
129 g_autofree guint *keycodes = NULL;
130
131 if (str == NULL || strcmp (str, "disabled") == 0)
132 {
133 memset (combo, 0, sizeof(CcKeyCombo));
134 return TRUE;
135 }
136
137 gtk_accelerator_parse_with_keycode (str,
138 gdk_display_get_default (),
139 &combo->keyval,
140 &keycodes,
141 &combo->mask);
142
143 combo->keycode = (keycodes ? keycodes[0] : 0);
144
145 if (combo->keyval == 0)
146 return FALSE;
147 else
148 return TRUE;
149 }
150
151 static void
152 _set_description (CcKeyboardItem *item,
153 const char *value)
154 {
155 g_free (item->description);
156 item->description = g_strdup (value);
157 }
158
159 const char *
160 cc_keyboard_item_get_description (CcKeyboardItem *item)
161 {
162 g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
163
164 return item->description;
165 }
166
167 gboolean
168 cc_keyboard_item_get_desc_editable (CcKeyboardItem *item)
169 {
170 g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), FALSE);
171
172 return item->desc_editable;
173 }
174
175 static void
176 _set_type (CcKeyboardItem *item,
177 gint value)
178 {
179 item->type = value;
180 }
181
182 static void
183 _set_command (CcKeyboardItem *item,
184 const char *value)
185 {
186 g_free (item->command);
187 item->command = g_strdup (value);
188 }
189
190 const char *
191 cc_keyboard_item_get_command (CcKeyboardItem *item)
192 {
193 g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
194
195 return item->command;
196 }
197
198 gboolean
199 cc_keyboard_item_get_cmd_editable (CcKeyboardItem *item)
200 {
201 g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), FALSE);
202
203 return item->cmd_editable;
204 }
205
206 static void
207 cc_keyboard_item_set_property (GObject *object,
208 guint prop_id,
209 const GValue *value,
210 GParamSpec *pspec)
211 {
212 CcKeyboardItem *self;
213
214 self = CC_KEYBOARD_ITEM (object);
215
216 switch (prop_id) {
217 case PROP_DESCRIPTION:
218 _set_description (self, g_value_get_string (value));
219 break;
220 case PROP_COMMAND:
221 _set_command (self, g_value_get_string (value));
222 break;
223 case PROP_TYPE:
224 _set_type (self, g_value_get_int (value));
225 break;
226 default:
227 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
228 break;
229 }
230 }
231
232 static void
233 cc_keyboard_item_get_property (GObject *object,
234 guint prop_id,
235 GValue *value,
236 GParamSpec *pspec)
237 {
238 CcKeyboardItem *self;
239
240 self = CC_KEYBOARD_ITEM (object);
241
242 switch (prop_id) {
243 case PROP_DESCRIPTION:
244 g_value_set_string (value, self->description);
245 break;
246 case PROP_EDITABLE:
247 g_value_set_boolean (value, self->editable);
248 break;
249 case PROP_COMMAND:
250 g_value_set_string (value, self->command);
251 break;
252 case PROP_IS_VALUE_DEFAULT:
253 g_value_set_boolean (value, cc_keyboard_item_is_value_default (self));
254 break;
255 case PROP_KEY_COMBOS:
256 g_value_set_pointer (value, self->key_combos);
257 break;
258 default:
259 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
260 break;
261 }
262 }
263
264 static void
265 cc_keyboard_item_class_init (CcKeyboardItemClass *klass)
266 {
267 GObjectClass *object_class = G_OBJECT_CLASS (klass);
268
269 object_class->get_property = cc_keyboard_item_get_property;
270 object_class->set_property = cc_keyboard_item_set_property;
271 object_class->finalize = cc_keyboard_item_finalize;
272
273 g_object_class_install_property (object_class,
274 PROP_DESCRIPTION,
275 g_param_spec_string ("description",
276 "description",
277 "description",
278 NULL,
279 G_PARAM_READWRITE));
280
281 g_object_class_install_property (object_class,
282 PROP_EDITABLE,
283 g_param_spec_boolean ("editable",
284 NULL,
285 NULL,
286 FALSE,
287 G_PARAM_READABLE));
288
289 g_object_class_install_property (object_class,
290 PROP_TYPE,
291 g_param_spec_int ("type",
292 NULL,
293 NULL,
294 CC_KEYBOARD_ITEM_TYPE_NONE,
295 CC_KEYBOARD_ITEM_TYPE_GSETTINGS,
296 CC_KEYBOARD_ITEM_TYPE_NONE,
297 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
298
299 g_object_class_install_property (object_class,
300 PROP_COMMAND,
301 g_param_spec_string ("command",
302 "command",
303 "command",
304 NULL,
305 G_PARAM_READWRITE));
306
307 g_object_class_install_property (object_class,
308 PROP_IS_VALUE_DEFAULT,
309 g_param_spec_boolean ("is-value-default",
310 "is value default",
311 "is value default",
312 TRUE,
313 G_PARAM_READABLE));
314
315 g_object_class_install_property (object_class,
316 PROP_KEY_COMBOS,
317 g_param_spec_pointer ("key-combos",
318 "key combos",
319 "key combos",
320 G_PARAM_READABLE));
321 }
322
323 static void
324 cc_keyboard_item_init (CcKeyboardItem *item)
325 {
326 }
327
328 static void
329 cc_keyboard_item_finalize (GObject *object)
330 {
331 CcKeyboardItem *item;
332
333 g_return_if_fail (object != NULL);
334 g_return_if_fail (CC_IS_KEYBOARD_ITEM (object));
335
336 item = CC_KEYBOARD_ITEM (object);
337
338 if (item->settings != NULL)
339 g_object_unref (item->settings);
340
341 /* Free memory */
342 g_free (item->gsettings_path);
343 g_free (item->description);
344 g_free (item->command);
345 g_free (item->schema);
346 g_free (item->key);
347 g_list_free_full (item->key_combos, g_free);
348 g_list_free_full (item->default_combos, g_free);
349
350 G_OBJECT_CLASS (cc_keyboard_item_parent_class)->finalize (object);
351 }
352
353 CcKeyboardItem *
354 cc_keyboard_item_new (CcKeyboardItemType type)
355 {
356 GObject *object;
357
358 object = g_object_new (CC_TYPE_KEYBOARD_ITEM,
359 "type", type,
360 NULL);
361
362 return CC_KEYBOARD_ITEM (object);
363 }
364
365 /* Copied from cc-keyboard-shortcut-dialog.c */
366 static gboolean
367 strv_contains_prefix_or_match (char **strv,
368 const char *prefix)
369 {
370 const struct {
371 const gchar *key;
372 const gchar *untranslated;
373 const gchar *synonym;
374 } key_aliases[] =
375 {
376 { "ctrl", "Ctrl", "ctrl" },
377 { "win", "Super", "super" },
378 { "option", NULL, "alt" },
379 { "command", NULL, "super" },
380 { "apple", NULL, "super" },
381 };
382
383 for (guint i = 0; strv[i]; i++)
384 {
385 if (g_str_has_prefix (strv[i], prefix))
386 return TRUE;
387 }
388
389 for (guint i = 0; i < G_N_ELEMENTS (key_aliases); i++)
390 {
391 g_autofree char *alias = NULL;
392 const char *synonym;
393
394 if (!g_str_has_prefix (key_aliases[i].key, prefix))
395 continue;
396
397 if (key_aliases[i].untranslated)
398 {
399 const char *translated_label;
400
401 /* Steal GTK+'s translation */
402 translated_label = g_dpgettext2 ("gtk40", "keyboard label", key_aliases[i].untranslated);
403 alias = g_utf8_strdown (translated_label, -1);
404 }
405
406 synonym = key_aliases[i].synonym;
407
408 /* If a translation or synonym of the key is in the accelerator, and we typed
409 * the key, also consider that a prefix */
410 if ((alias && g_strv_contains ((const char * const *) strv, alias)) ||
411 (synonym && g_strv_contains ((const char * const *) strv, synonym)))
412 return TRUE;
413 }
414
415 return FALSE;
416 }
417
418 /* Copied from cc-keyboard-shortcut-dialog.c */
419 static gboolean
420 search_match_shortcut (CcKeyboardItem *item,
421 const char *search)
422 {
423 g_auto(GStrv) shortcut_tokens = NULL, search_tokens = NULL;
424 g_autofree char *normalized_accel = NULL;
425 g_autofree char *accel = NULL;
426 GList *key_combos;
427 CcKeyCombo *combo;
428 gboolean match = TRUE;
429
430 key_combos = cc_keyboard_item_get_key_combos (item);
431 for (GList *l = key_combos; l != NULL; l = l->next)
432 {
433 combo = l->data;
434
435 if (is_empty_binding (combo))
436 continue;
437
438 accel = convert_keysym_state_to_string (combo);
439 normalized_accel = cc_util_normalize_casefold_and_unaccent (accel);
440
441 shortcut_tokens = g_strsplit_set (normalized_accel, SHORTCUT_DELIMITERS, -1);
442 search_tokens = g_strsplit_set (search, SHORTCUT_DELIMITERS, -1);
443
444 for (guint i = 0; search_tokens[i]; i++)
445 {
446 match = match && strv_contains_prefix_or_match (shortcut_tokens, search_tokens[i]);
447
448 if (!match)
449 break;
450 }
451
452 if (match)
453 return TRUE;
454 }
455
456 return FALSE;
457 }
458
459 gboolean
460 cc_keyboard_item_matches_string (CcKeyboardItem *self,
461 GStrv search_terms)
462 {
463 g_autofree char *name = NULL;
464
465 g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (self), FALSE);
466
467 if (!search_terms || !*search_terms || !self->description)
468 return TRUE;
469
470 name = cc_util_normalize_casefold_and_unaccent (self->description);
471
472 for (guint i = 0; search_terms[i]; i++)
473 {
474 gboolean match;
475
476 match = strstr (name, search_terms[i]) || search_match_shortcut (self, search_terms[i]);
477
478 if (!match)
479 return FALSE;
480 }
481
482 return TRUE;
483 }
484
485 static guint *
486 get_above_tab_keysyms (void)
487 {
488 guint keycode = 0x29 /* KEY_GRAVE */ + 8;
489 g_autofree guint *keyvals = NULL;
490 GArray *keysyms;
491 int n_entries, i, j;
492
493 keysyms = g_array_new (TRUE, FALSE, sizeof (guint));
494
495 if (!gdk_display_map_keycode (gdk_display_get_default (),
496 keycode,
497 NULL,
498 &keyvals,
499 &n_entries))
500 goto out;
501
502 for (i = 0; i < n_entries; i++)
503 {
504 gboolean found = FALSE;
505
506 for (j = 0; j < keysyms->len; j++)
507 if (g_array_index (keysyms, guint, j) == keyvals[i])
508 {
509 found = TRUE;
510 break;
511 }
512
513 if (!found)
514 g_array_append_val (keysyms, keyvals[i]);
515 }
516
517 out:
518 return (guint *)g_array_free (keysyms, FALSE);
519 }
520
521 /*
522 * translate_above_tab:
523 *
524 * @original_bindings: A list of accelerator strings
525 * @new_bindings: (out): Translated bindings if translation is needed
526 *
527 * Translate accelerator strings that contain the Above_Tab fake keysym
528 * used by mutter to strings that use the real keysyms that correspond
529 * to the key that is located physically above the tab key.
530 *
531 * Returns: %TRUE if strings were translated, %FALSE if @original_bindings
532 * can be used unmodified
533 */
534 static gboolean
535 translate_above_tab (char **original_bindings,
536 char ***new_bindings)
537 {
538 GPtrArray *replaced_bindings;
539 g_autofree guint *above_tab_keysyms = NULL;
540 gboolean needs_translation = FALSE;
541 char **str;
542
543 for (str = original_bindings; *str && !needs_translation; str++)
544 needs_translation = strstr (*str, "Above_Tab") != NULL;
545
546 if (!needs_translation)
547 return FALSE;
548
549 above_tab_keysyms = get_above_tab_keysyms ();
550
551 replaced_bindings = g_ptr_array_new ();
552
553 for (str = original_bindings; *str; str++)
554 {
555 if (strstr (*str, "Above_Tab") == NULL)
556 {
557 g_ptr_array_add (replaced_bindings, g_strdup (*str));
558 }
559 else
560 {
561 g_auto (GStrv) split_str = g_strsplit (*str, "Above_Tab", -1);
562 int i;
563
564 for (i = 0; above_tab_keysyms[i]; i++)
565 {
566 g_autofree char *sym = NULL;
567
568 sym = gtk_accelerator_name (above_tab_keysyms[i], 0);
569 g_ptr_array_add (replaced_bindings, g_strjoinv (sym, split_str));
570 }
571 }
572 g_ptr_array_add (replaced_bindings, NULL);
573 }
574
575 *new_bindings = (char **)g_ptr_array_free (replaced_bindings, FALSE);
576 return TRUE;
577 }
578
579 static GList *
580 variant_get_key_combos (GVariant *variant)
581 {
582 GList *combos = NULL;
583 char **translated_bindings, **str;
584 g_auto(GStrv) bindings = NULL;
585
586 if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING))
587 {
588 bindings = g_malloc0_n (2, sizeof(char *));
589 if (g_strcmp0 (g_variant_get_string (variant, NULL), "") != 0)
590 bindings[0] = g_variant_dup_string (variant, NULL);
591 }
592 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING_ARRAY))
593 {
594 bindings = g_variant_dup_strv (variant, NULL);
595 }
596
597 if (translate_above_tab (bindings, &translated_bindings))
598 {
599 g_strfreev (bindings);
600 bindings = translated_bindings;
601 }
602
603 for (str = bindings; *str; str++)
604 {
605 g_autofree CcKeyCombo *combo = g_new (CcKeyCombo, 1);
606
607 binding_from_string (*str, combo);
608
609 if (combo->keyval != 0 || combo->keycode != 0 || combo->mask != 0)
610 combos = g_list_prepend (combos, g_steal_pointer (&combo));
611 }
612
613 return g_list_reverse (combos);
614 }
615
616 static GList *
617 settings_get_key_combos (GSettings *settings,
618 const char *key,
619 gboolean use_default)
620 {
621 GList *key_combos;
622 g_autoptr(GVariant) variant = NULL;
623
624 if (use_default)
625 variant = g_settings_get_default_value (settings, key);
626 else
627 variant = g_settings_get_value (settings, key);
628 key_combos = variant_get_key_combos (variant);
629
630 return key_combos;
631 }
632
633 static void
634 binding_changed (CcKeyboardItem *item,
635 const char *key)
636 {
637 g_list_free_full (item->key_combos, g_free);
638 item->key_combos = settings_get_key_combos (item->settings, item->key, FALSE);
639
640 item->editable = g_settings_is_writable (item->settings, item->key);
641
642 g_object_notify (G_OBJECT (item), "key-combos");
643 g_object_notify (G_OBJECT (item), "is-value-default");
644 if (item->reverse_item)
645 g_object_notify (G_OBJECT (item->reverse_item), "is-value-default");
646 }
647
648 gboolean
649 cc_keyboard_item_load_from_gsettings_path (CcKeyboardItem *item,
650 const char *path,
651 gboolean reset)
652 {
653 g_autoptr(GVariant) variant = NULL;
654
655 item->schema = g_strdup (CUSTOM_KEYS_SCHEMA);
656 item->gsettings_path = g_strdup (path);
657 item->key = g_strdup ("binding");
658 item->settings = g_settings_new_with_path (item->schema, path);
659 item->editable = g_settings_is_writable (item->settings, item->key);
660 item->desc_editable = g_settings_is_writable (item->settings, "name");
661 item->cmd_editable = g_settings_is_writable (item->settings, "command");
662
663 variant = g_settings_get_value (item->settings, item->key);
664 item->can_set_multiple = g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING_ARRAY);
665
666 if (reset)
667 {
668 g_settings_reset (item->settings, "name");
669 g_settings_reset (item->settings, "command");
670 g_settings_reset (item->settings, "binding");
671 }
672
673 g_settings_bind (item->settings, "name",
674 G_OBJECT (item), "description", G_SETTINGS_BIND_DEFAULT);
675 g_settings_bind (item->settings, "command",
676 G_OBJECT (item), "command", G_SETTINGS_BIND_DEFAULT);
677
678 g_list_free_full (item->key_combos, g_free);
679 item->key_combos = settings_get_key_combos (item->settings, item->key, FALSE);
680
681 g_signal_connect_object (G_OBJECT (item->settings), "changed::binding",
682 G_CALLBACK (binding_changed), item, G_CONNECT_SWAPPED);
683
684 return TRUE;
685 }
686
687 gboolean
688 cc_keyboard_item_load_from_gsettings (CcKeyboardItem *item,
689 const char *description,
690 const char *schema,
691 const char *key)
692 {
693 g_autofree char *signal_name = NULL;
694 g_autoptr(GVariant) variant = NULL;
695
696 item->schema = g_strdup (schema);
697 item->key = g_strdup (key);
698 item->description = g_strdup (description);
699
700 item->settings = g_settings_new (item->schema);
701 item->editable = g_settings_is_writable (item->settings, item->key);
702
703 g_list_free_full (item->key_combos, g_free);
704 item->key_combos = settings_get_key_combos (item->settings, item->key, FALSE);
705
706 g_list_free_full (item->default_combos, g_free);
707 item->default_combos = settings_get_key_combos (item->settings, item->key, TRUE);
708
709 variant = g_settings_get_value (item->settings, item->key);
710 item->can_set_multiple = g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING_ARRAY);
711
712 signal_name = g_strdup_printf ("changed::%s", item->key);
713 g_signal_connect_object (G_OBJECT (item->settings), signal_name,
714 G_CALLBACK (binding_changed), item, G_CONNECT_SWAPPED);
715
716 return TRUE;
717 }
718
719 gboolean
720 cc_keyboard_item_equal (CcKeyboardItem *a,
721 CcKeyboardItem *b)
722 {
723 if (a->type != b->type)
724 return FALSE;
725 switch (a->type)
726 {
727 case CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH:
728 return g_str_equal (a->gsettings_path, b->gsettings_path);
729 case CC_KEYBOARD_ITEM_TYPE_GSETTINGS:
730 return (g_str_equal (a->schema, b->schema) &&
731 g_str_equal (a->key, b->key));
732 default:
733 g_assert_not_reached ();
734 }
735
736 }
737
738 void
739 cc_keyboard_item_add_reverse_item (CcKeyboardItem *item,
740 CcKeyboardItem *reverse_item,
741 gboolean is_reversed)
742 {
743 g_return_if_fail (item->key != NULL);
744
745 item->reverse_item = reverse_item;
746 if (reverse_item->reverse_item == NULL)
747 {
748 reverse_item->reverse_item = item;
749 reverse_item->is_reversed = !is_reversed;
750 }
751 else
752 g_warn_if_fail (reverse_item->is_reversed == !!is_reversed);
753
754 item->is_reversed = !!is_reversed;
755 }
756
757 CcKeyboardItem *
758 cc_keyboard_item_get_reverse_item (CcKeyboardItem *item)
759 {
760 return item->reverse_item;
761 }
762
763
764 void
765 cc_keyboard_item_set_hidden (CcKeyboardItem *item, gboolean hidden)
766 {
767 item->hidden = !!hidden;
768 }
769
770
771 gboolean
772 cc_keyboard_item_is_hidden (CcKeyboardItem *item)
773 {
774 return item->hidden;
775 }
776
777 /**
778 * cc_keyboard_item_is_value_default:
779 * @self: a #CcKeyboardItem
780 *
781 * Retrieves whether the shortcut is the default value or not.
782 *
783 * Returns: %TRUE if the shortcut is the default value, %FALSE otherwise.
784 */
785 gboolean
786 cc_keyboard_item_is_value_default (CcKeyboardItem *self)
787 {
788 g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (self), FALSE);
789
790 /*
791 * When the shortcut is custom, we don't treat it as modified
792 * since we don't know what would be its default value.
793 */
794 if (self->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH)
795 return TRUE;
796
797 return combos_equal (self->default_combos, self->key_combos);
798 }
799
800 /**
801 * cc_keyboard_item_reset:
802 * @self: a #CcKeyboardItem
803 *
804 * Reset the keyboard binding to the default value.
805 */
806 void
807 cc_keyboard_item_reset (CcKeyboardItem *self)
808 {
809 CcKeyboardItem *reverse;
810
811 g_return_if_fail (CC_IS_KEYBOARD_ITEM (self));
812
813 reverse = self->reverse_item;
814
815 g_settings_reset (self->settings, self->key);
816 g_object_notify (G_OBJECT (self), "is-value-default");
817
818 /* Also reset the reverse item */
819 if (reverse)
820 {
821 g_settings_reset (reverse->settings, reverse->key);
822 g_object_notify (G_OBJECT (reverse), "is-value-default");
823 }
824 }
825
826 GList *
827 cc_keyboard_item_get_key_combos (CcKeyboardItem *item)
828 {
829 g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
830 return item->key_combos;
831 }
832
833 GList *
834 cc_keyboard_item_get_default_combos (CcKeyboardItem *item)
835 {
836 g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
837 return item->default_combos;
838 }
839
840 CcKeyCombo
841 cc_keyboard_item_get_primary_combo (CcKeyboardItem *item)
842 {
843 g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), EMPTY_COMBO);
844 return (item->key_combos) ? *((CcKeyCombo*)item->key_combos->data) : EMPTY_COMBO;
845 }
846
847 const gchar *
848 cc_keyboard_item_get_key (CcKeyboardItem *item)
849 {
850 g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
851 return item->key;
852 }
853
854 CcKeyboardItemType
855 cc_keyboard_item_get_item_type (CcKeyboardItem *item)
856 {
857 g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), CC_KEYBOARD_ITEM_TYPE_NONE);
858 return item->type;
859 }
860
861 const gchar *
862 cc_keyboard_item_get_gsettings_path (CcKeyboardItem *item)
863 {
864 g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
865 return item->gsettings_path;
866 }
867
868 GSettings *
869 cc_keyboard_item_get_settings (CcKeyboardItem *item)
870 {
871 g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
872 return item->settings;
873 }
874
875 gboolean
876 cc_keyboard_item_can_set_multiple (CcKeyboardItem *item)
877 {
878 return item->can_set_multiple;
879 }
880
881 static gchar*
882 combo_get_accelerator (CcKeyCombo *combo)
883 {
884 return gtk_accelerator_name_with_keycode (NULL,
885 combo->keyval,
886 combo->keycode,
887 combo->mask);
888 }
889
890 static void
891 cc_keyboard_item_add_key_combo_inner (CcKeyboardItem *self,
892 CcKeyCombo *combo)
893 {
894 g_auto(GStrv) strv = NULL;
895 int i;
896
897 if (!self->can_set_multiple)
898 {
899 g_settings_set_string (self->settings, self->key, combo_get_accelerator (combo));
900 }
901 else
902 {
903 strv = g_new0 (gchar*, g_list_length (self->key_combos) + 2);
904
905 i = 0;
906 for (GList *l = self->key_combos; l != NULL; l = l->next, i++)
907 {
908 if (combo_equal (l->data, combo))
909 // This combo is already in the list
910 return;
911 strv[i] = combo_get_accelerator (l->data);
912 }
913 strv[i] = combo_get_accelerator (combo);
914
915 g_settings_set_strv (self->settings, self->key, (const gchar **)strv);
916 }
917
918 binding_changed (self, self->key);
919 }
920
921 void
922 cc_keyboard_item_add_key_combo (CcKeyboardItem *self,
923 CcKeyCombo *combo)
924 {
925 CcKeyCombo reverse_combo;
926
927 if (self->reverse_item)
928 {
929 reverse_combo.keyval = combo->keyval;
930 reverse_combo.keycode = combo->keycode;
931 reverse_combo.mask = combo->mask ^ GDK_SHIFT_MASK;
932 cc_keyboard_item_add_key_combo_inner (self->reverse_item, &reverse_combo);
933 }
934
935 cc_keyboard_item_add_key_combo_inner (self, combo);
936 }
937
938 static void
939 cc_keyboard_item_remove_key_combo_inner (CcKeyboardItem *self,
940 CcKeyCombo *combo)
941 {
942 g_auto(GStrv) strv = NULL;
943 gboolean found;
944 int i;
945
946 strv = g_new0 (gchar*, g_list_length (self->key_combos) + 1);
947
948 found = FALSE;
949 i = 0;
950 for (GList *l = self->key_combos; l != NULL; l = l->next, i++)
951 {
952 if (combo_equal (l->data, combo))
953 {
954 i--;
955 found = TRUE;
956 }
957 else
958 {
959 strv[i] = combo_get_accelerator (l->data);
960 }
961 }
962
963 if (found)
964 {
965 if (self->can_set_multiple)
966 g_settings_set_strv (self->settings, self->key, (const gchar **)strv);
967 else
968 g_settings_set_string (self->settings, self->key, "");
969 }
970
971 binding_changed (self, self->key);
972 }
973
974 void
975 cc_keyboard_item_remove_key_combo (CcKeyboardItem *self,
976 CcKeyCombo *combo)
977 {
978 CcKeyCombo reverse_combo;
979
980 if (self->reverse_item)
981 {
982 reverse_combo.keyval = combo->keyval;
983 reverse_combo.keycode = combo->keycode;
984 reverse_combo.mask = combo->mask ^ GDK_SHIFT_MASK;
985 cc_keyboard_item_remove_key_combo_inner (self->reverse_item, &reverse_combo);
986 }
987
988 cc_keyboard_item_remove_key_combo_inner (self, combo);
989 }
990
991 void cc_keyboard_item_disable (CcKeyboardItem *self)
992 {
993 if (!self->can_set_multiple)
994 {
995 g_settings_set_string (self->settings, self->key, "");
996 if (self->reverse_item)
997 g_settings_set_string (self->reverse_item->settings, self->reverse_item->key, "");
998 }
999 else
1000 {
1001 g_settings_set_strv (self->settings, self->key, NULL);
1002 if (self->reverse_item)
1003 g_settings_set_strv (self->reverse_item->settings, self->reverse_item->key, NULL);
1004 }
1005
1006 binding_changed (self, self->key);
1007 }
1008