~ubuntu-branches/ubuntu/trusty/unity-control-center/trusty

« back to all changes in this revision

Viewing changes to panels/region/keyboard-shortcuts.c

  • Committer: Package Import Robot
  • Author(s): Robert Ancell
  • Date: 2014-01-08 16:29:18 UTC
  • Revision ID: package-import@ubuntu.com-20140108162918-g29dd08tr913y2qh
Tags: upstream-14.04.0
ImportĀ upstreamĀ versionĀ 14.04.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2010 Intel, Inc
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License as published by
 
6
 * the Free Software Foundation; either version 2 of the License, or
 
7
 * (at your option) any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
17
 *
 
18
 * Authors: Thomas Wood <thomas.wood@intel.com>
 
19
 *          Rodrigo Moya <rodrigo@gnome.org>
 
20
 */
 
21
 
 
22
#include <config.h>
 
23
 
 
24
#include <glib/gi18n.h>
 
25
 
 
26
#include "keyboard-shortcuts.h"
 
27
#include "wm-common.h"
 
28
 
 
29
#define BINDINGS_SCHEMA "org.gnome.settings-daemon.plugins.media-keys"
 
30
#define CUSTOM_KEYS_BASENAME "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings"
 
31
#define CUSTOM_SHORTCUTS_ID "custom"
 
32
 
 
33
typedef struct {
 
34
  /* The untranslated name, combine with ->package to translate */
 
35
  char *name;
 
36
  /* The group of keybindings (system or application) */
 
37
  char *group;
 
38
  /* The gettext package to use to translate the section title */
 
39
  char *package;
 
40
  /* Name of the window manager the keys would apply to */
 
41
  char *wm_name;
 
42
  /* The GSettings schema for the whole file, if any */
 
43
  char *schema;
 
44
  /* an array of KeyListEntry */
 
45
  GArray *entries;
 
46
} KeyList;
 
47
 
 
48
typedef struct
 
49
{
 
50
  CcRegionKeyboardItemType type;
 
51
  char *schema; /* GSettings schema name, if any */
 
52
  char *description; /* description for GSettings types */
 
53
  char *gettext_package;
 
54
  char *name; /* GSettings schema path, or GSettings key name depending on type */
 
55
} KeyListEntry;
 
56
 
 
57
static GSettings *binding_settings = NULL;
 
58
static GHashTable *kb_system_sections = NULL;
 
59
static GHashTable *kb_apps_sections = NULL;
 
60
static GHashTable *kb_user_sections = NULL;
 
61
 
 
62
static void
 
63
free_key_array (GPtrArray *keys)
 
64
{
 
65
  if (keys != NULL)
 
66
    {
 
67
      gint i;
 
68
 
 
69
      for (i = 0; i < keys->len; i++)
 
70
        {
 
71
          CcRegionKeyboardItem *item;
 
72
 
 
73
          item = g_ptr_array_index (keys, i);
 
74
 
 
75
          g_object_unref (item);
 
76
        }
 
77
 
 
78
      g_ptr_array_free (keys, TRUE);
 
79
    }
 
80
}
 
81
 
 
82
static GHashTable *
 
83
get_hash_for_group (BindingGroupType group)
 
84
{
 
85
  GHashTable *hash;
 
86
 
 
87
  switch (group)
 
88
    {
 
89
    case BINDING_GROUP_SYSTEM:
 
90
      hash = kb_system_sections;
 
91
      break;
 
92
    case BINDING_GROUP_APPS:
 
93
      hash = kb_apps_sections;
 
94
      break;
 
95
    case BINDING_GROUP_USER:
 
96
      hash = kb_user_sections;
 
97
      break;
 
98
    default:
 
99
      hash = NULL;
 
100
    }
 
101
  return hash;
 
102
}
 
103
 
 
104
static gboolean
 
105
have_key_for_group (int group, const gchar *name)
 
106
{
 
107
  GHashTableIter iter;
 
108
  GPtrArray *keys;
 
109
  gint i;
 
110
 
 
111
  g_hash_table_iter_init (&iter, get_hash_for_group (group));
 
112
  while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&keys))
 
113
    {
 
114
      for (i = 0; i < keys->len; i++)
 
115
        {
 
116
          CcRegionKeyboardItem *item = g_ptr_array_index (keys, i);
 
117
 
 
118
          if (item->type == CC_REGION_KEYBOARD_ITEM_TYPE_GSETTINGS &&
 
119
              g_strcmp0 (name, item->key) == 0)
 
120
            {
 
121
                  return TRUE;
 
122
            }
 
123
 
 
124
          return FALSE;
 
125
        }
 
126
    }
 
127
 
 
128
  return FALSE;
 
129
}
 
130
 
 
131
static void
 
132
append_section (const gchar        *id,
 
133
                BindingGroupType    group,
 
134
                const KeyListEntry *keys_list)
 
135
{
 
136
  GPtrArray *keys_array;
 
137
  gint i;
 
138
  GHashTable *hash;
 
139
  gboolean is_new;
 
140
 
 
141
  hash = get_hash_for_group (group);
 
142
  if (!hash)
 
143
    return;
 
144
 
 
145
  /* Add all CcRegionKeyboardItems for this section */
 
146
  is_new = FALSE;
 
147
  keys_array = g_hash_table_lookup (hash, id);
 
148
  if (keys_array == NULL)
 
149
    {
 
150
      keys_array = g_ptr_array_new ();
 
151
      is_new = TRUE;
 
152
    }
 
153
 
 
154
  for (i = 0; keys_list != NULL && keys_list[i].name != NULL; i++)
 
155
    {
 
156
      CcRegionKeyboardItem *item;
 
157
      gboolean ret;
 
158
 
 
159
      if (have_key_for_group (group, keys_list[i].name))
 
160
        continue;
 
161
 
 
162
      item = cc_region_keyboard_item_new (keys_list[i].type);
 
163
      switch (keys_list[i].type)
 
164
        {
 
165
        case CC_REGION_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH:
 
166
          ret = cc_region_keyboard_item_load_from_gsettings_path (item, keys_list[i].name, FALSE);
 
167
          break;
 
168
        case CC_REGION_KEYBOARD_ITEM_TYPE_GSETTINGS:
 
169
          ret = cc_region_keyboard_item_load_from_gsettings (item,
 
170
                                                             keys_list[i].description,
 
171
                                                             keys_list[i].schema,
 
172
                                                             keys_list[i].name);
 
173
          break;
 
174
        default:
 
175
          g_assert_not_reached ();
 
176
        }
 
177
 
 
178
      if (ret == FALSE)
 
179
        {
 
180
          /* We don't actually want to popup a dialog - just skip this one */
 
181
          g_object_unref (item);
 
182
          continue;
 
183
        }
 
184
 
 
185
      item->group = group;
 
186
 
 
187
      g_ptr_array_add (keys_array, item);
 
188
    }
 
189
 
 
190
  /* Add the keys to the hash table */
 
191
  if (is_new)
 
192
    g_hash_table_insert (hash, g_strdup (id), keys_array);
 
193
}
 
194
 
 
195
static void
 
196
parse_start_tag (GMarkupParseContext *ctx,
 
197
                 const gchar         *element_name,
 
198
                 const gchar        **attr_names,
 
199
                 const gchar        **attr_values,
 
200
                 gpointer             user_data,
 
201
                 GError             **error)
 
202
{
 
203
  KeyList *keylist = (KeyList *) user_data;
 
204
  KeyListEntry key;
 
205
  const char *name, *schema, *description, *package;
 
206
 
 
207
  name = NULL;
 
208
  schema = NULL;
 
209
  package = NULL;
 
210
 
 
211
  /* The top-level element, names the section in the tree */
 
212
  if (g_str_equal (element_name, "KeyListEntries"))
 
213
    {
 
214
      const char *wm_name = NULL;
 
215
      const char *group = NULL;
 
216
 
 
217
      while (*attr_names && *attr_values)
 
218
        {
 
219
          if (g_str_equal (*attr_names, "name"))
 
220
            {
 
221
              if (**attr_values)
 
222
                name = *attr_values;
 
223
            } else if (g_str_equal (*attr_names, "group")) {
 
224
              if (**attr_values)
 
225
                group = *attr_values;
 
226
            } else if (g_str_equal (*attr_names, "wm_name")) {
 
227
              if (**attr_values)
 
228
                wm_name = *attr_values;
 
229
            } else if (g_str_equal (*attr_names, "schema")) {
 
230
              if (**attr_values)
 
231
                schema = *attr_values;
 
232
            } else if (g_str_equal (*attr_names, "package")) {
 
233
              if (**attr_values)
 
234
                package = *attr_values;
 
235
            }
 
236
          ++attr_names;
 
237
          ++attr_values;
 
238
        }
 
239
 
 
240
      if (name)
 
241
        {
 
242
          if (keylist->name)
 
243
            g_warning ("Duplicate section name");
 
244
          g_free (keylist->name);
 
245
          keylist->name = g_strdup (name);
 
246
        }
 
247
      if (wm_name)
 
248
        {
 
249
          if (keylist->wm_name)
 
250
            g_warning ("Duplicate window manager name");
 
251
          g_free (keylist->wm_name);
 
252
          keylist->wm_name = g_strdup (wm_name);
 
253
        }
 
254
      if (package)
 
255
        {
 
256
          if (keylist->package)
 
257
            g_warning ("Duplicate gettext package name");
 
258
          g_free (keylist->package);
 
259
          keylist->package = g_strdup (package);
 
260
          bind_textdomain_codeset (keylist->package, "UTF-8");
 
261
        }
 
262
      if (group)
 
263
        {
 
264
          if (keylist->group)
 
265
            g_warning ("Duplicate group");
 
266
          g_free (keylist->group);
 
267
          keylist->group = g_strdup (group);
 
268
        }
 
269
      if (schema)
 
270
        {
 
271
          if (keylist->schema)
 
272
            g_warning ("Duplicate schema");
 
273
          g_free (keylist->schema);
 
274
          keylist->schema = g_strdup (schema);
 
275
        }
 
276
      return;
 
277
    }
 
278
 
 
279
  if (!g_str_equal (element_name, "KeyListEntry")
 
280
      || attr_names == NULL
 
281
      || attr_values == NULL)
 
282
    return;
 
283
 
 
284
  schema = NULL;
 
285
  description = NULL;
 
286
 
 
287
  while (*attr_names && *attr_values)
 
288
    {
 
289
      if (g_str_equal (*attr_names, "name"))
 
290
        {
 
291
          /* skip if empty */
 
292
          if (**attr_values)
 
293
            name = *attr_values;
 
294
        } else if (g_str_equal (*attr_names, "schema")) {
 
295
          if (**attr_values) {
 
296
           schema = *attr_values;
 
297
          }
 
298
        } else if (g_str_equal (*attr_names, "description")) {
 
299
          if (**attr_values) {
 
300
            if (keylist->package)
 
301
              {
 
302
                description = dgettext (keylist->package, *attr_values);
 
303
              }
 
304
            else
 
305
              {
 
306
                description = _(*attr_values);
 
307
              }
 
308
          }
 
309
        }
 
310
 
 
311
      ++attr_names;
 
312
      ++attr_values;
 
313
    }
 
314
 
 
315
  if (name == NULL)
 
316
    return;
 
317
 
 
318
  if (schema == NULL &&
 
319
      keylist->schema == NULL) {
 
320
    g_debug ("Ignored GConf keyboard shortcut '%s'", name);
 
321
    return;
 
322
  }
 
323
 
 
324
  key.name = g_strdup (name);
 
325
  key.type = CC_REGION_KEYBOARD_ITEM_TYPE_GSETTINGS;
 
326
  key.description = g_strdup (description);
 
327
  key.gettext_package = g_strdup (keylist->package);
 
328
  key.schema = schema ? g_strdup (schema) : g_strdup (keylist->schema);
 
329
  g_array_append_val (keylist->entries, key);
 
330
}
 
331
 
 
332
static gboolean
 
333
strv_contains (char **strv,
 
334
               char  *str)
 
335
{
 
336
  char **p = strv;
 
337
  for (p = strv; *p; p++)
 
338
    if (strcmp (*p, str) == 0)
 
339
      return TRUE;
 
340
 
 
341
  return FALSE;
 
342
}
 
343
 
 
344
static void
 
345
append_sections_from_file (const gchar *path, const char *datadir, gchar **wm_keybindings)
 
346
{
 
347
  GError *err = NULL;
 
348
  char *buf;
 
349
  gsize buf_len;
 
350
  KeyList *keylist;
 
351
  KeyListEntry key, *keys;
 
352
  int group;
 
353
  guint i;
 
354
  GMarkupParseContext *ctx;
 
355
  GMarkupParser parser = { parse_start_tag, NULL, NULL, NULL, NULL };
 
356
 
 
357
  /* Parse file */
 
358
  if (!g_file_get_contents (path, &buf, &buf_len, &err))
 
359
    return;
 
360
 
 
361
  keylist = g_new0 (KeyList, 1);
 
362
  keylist->entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
 
363
  ctx = g_markup_parse_context_new (&parser, 0, keylist, NULL);
 
364
 
 
365
  if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err))
 
366
    {
 
367
      g_warning ("Failed to parse '%s': '%s'", path, err->message);
 
368
      g_error_free (err);
 
369
      g_free (keylist->name);
 
370
      g_free (keylist->package);
 
371
      g_free (keylist->wm_name);
 
372
      for (i = 0; i < keylist->entries->len; i++)
 
373
        g_free (((KeyListEntry *) &(keylist->entries->data[i]))->name);
 
374
      g_array_free (keylist->entries, TRUE);
 
375
      g_free (keylist);
 
376
      keylist = NULL;
 
377
    }
 
378
  g_markup_parse_context_free (ctx);
 
379
  g_free (buf);
 
380
 
 
381
  if (keylist == NULL)
 
382
    return;
 
383
 
 
384
  /* If there's no keys to add, or the settings apply to a window manager
 
385
   * that's not the one we're running */
 
386
  if (keylist->entries->len == 0
 
387
      || (keylist->wm_name != NULL && !strv_contains (wm_keybindings, keylist->wm_name))
 
388
      || keylist->name == NULL)
 
389
    {
 
390
      g_free (keylist->name);
 
391
      g_free (keylist->package);
 
392
      g_free (keylist->wm_name);
 
393
      g_array_free (keylist->entries, TRUE);
 
394
      g_free (keylist);
 
395
      return;
 
396
    }
 
397
 
 
398
  /* Empty KeyListEntry to end the array */
 
399
  key.name = NULL;
 
400
  g_array_append_val (keylist->entries, key);
 
401
 
 
402
  keys = (KeyListEntry *) g_array_free (keylist->entries, FALSE);
 
403
  if (keylist->package)
 
404
    {
 
405
      char *localedir;
 
406
 
 
407
      localedir = g_build_filename (datadir, "locale", NULL);
 
408
      bindtextdomain (keylist->package, localedir);
 
409
      g_free (localedir);
 
410
    }
 
411
  if (keylist->group && strcmp (keylist->group, "system") == 0)
 
412
    group = BINDING_GROUP_SYSTEM;
 
413
  else
 
414
    group = BINDING_GROUP_APPS;
 
415
 
 
416
  append_section (keylist->name, group, keys);
 
417
 
 
418
  g_free (keylist->name);
 
419
  g_free (keylist->package);
 
420
  g_free (keylist->wm_name);
 
421
  g_free (keylist->schema);
 
422
  g_free (keylist->group);
 
423
 
 
424
  for (i = 0; keys[i].name != NULL; i++) {
 
425
    KeyListEntry *entry = &keys[i];
 
426
    g_free (entry->schema);
 
427
    g_free (entry->description);
 
428
    g_free (entry->gettext_package);
 
429
    g_free (entry->name);
 
430
  }
 
431
 
 
432
  g_free (keylist);
 
433
}
 
434
 
 
435
static void
 
436
append_sections_from_gsettings (void)
 
437
{
 
438
  char **custom_paths;
 
439
  GArray *entries;
 
440
  KeyListEntry key;
 
441
  int i;
 
442
 
 
443
  /* load custom shortcuts from GSettings */
 
444
  entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
 
445
 
 
446
  custom_paths = g_settings_get_strv (binding_settings, "custom-keybindings");
 
447
  for (i = 0; custom_paths[i]; i++)
 
448
    {
 
449
      key.name = g_strdup (custom_paths[i]);
 
450
      if (!have_key_for_group (BINDING_GROUP_USER, key.name))
 
451
        {
 
452
          key.type = CC_REGION_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH;
 
453
          g_array_append_val (entries, key);
 
454
        }
 
455
      else
 
456
        g_free (key.name);
 
457
    }
 
458
  g_strfreev (custom_paths);
 
459
 
 
460
  if (entries->len > 0)
 
461
    {
 
462
      KeyListEntry *keys;
 
463
      int i;
 
464
 
 
465
      /* Empty KeyListEntry to end the array */
 
466
      key.name = NULL;
 
467
      g_array_append_val (entries, key);
 
468
 
 
469
      keys = (KeyListEntry *) entries->data;
 
470
      append_section (CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, keys);
 
471
      for (i = 0; i < entries->len; ++i)
 
472
        {
 
473
          g_free (keys[i].name);
 
474
        }
 
475
    }
 
476
  else
 
477
    {
 
478
      append_section (CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, NULL);
 
479
    }
 
480
 
 
481
  g_array_free (entries, TRUE);
 
482
}
 
483
 
 
484
static void
 
485
reload_sections (void)
 
486
{
 
487
  gchar **wm_keybindings;
 
488
  GDir *dir;
 
489
  const gchar * const * data_dirs;
 
490
  guint i;
 
491
  GHashTable *loaded_files;
 
492
  const char *section_to_set;
 
493
 
 
494
  /* Clear previous hash tables */
 
495
  if (kb_system_sections != NULL)
 
496
    g_hash_table_destroy (kb_system_sections);
 
497
  kb_system_sections = g_hash_table_new_full (g_str_hash,
 
498
                                              g_str_equal,
 
499
                                              g_free,
 
500
                                              (GDestroyNotify) free_key_array);
 
501
 
 
502
  if (kb_apps_sections != NULL)
 
503
    g_hash_table_destroy (kb_apps_sections);
 
504
  kb_apps_sections = g_hash_table_new_full (g_str_hash,
 
505
                                            g_str_equal,
 
506
                                            g_free,
 
507
                                            (GDestroyNotify) free_key_array);
 
508
 
 
509
  if (kb_user_sections != NULL)
 
510
    g_hash_table_destroy (kb_user_sections);
 
511
  kb_user_sections = g_hash_table_new_full (g_str_hash,
 
512
                                            g_str_equal,
 
513
                                            g_free,
 
514
                                            (GDestroyNotify) free_key_array);
 
515
 
 
516
  /* Load WM keybindings */
 
517
  wm_keybindings = wm_common_get_current_keybindings ();
 
518
 
 
519
  loaded_files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
520
 
 
521
  data_dirs = g_get_system_data_dirs ();
 
522
  for (i = 0; data_dirs[i] != NULL; i++)
 
523
    {
 
524
      char *dir_path;
 
525
      const gchar *name;
 
526
 
 
527
      dir_path = g_build_filename (data_dirs[i], "unity-control-center", "keybindings", NULL);
 
528
 
 
529
      dir = g_dir_open (dir_path, 0, NULL);
 
530
      if (!dir)
 
531
        {
 
532
          g_free (dir_path);
 
533
          continue;
 
534
        }
 
535
 
 
536
      for (name = g_dir_read_name (dir) ; name ; name = g_dir_read_name (dir))
 
537
        {
 
538
          gchar *path;
 
539
 
 
540
          if (g_str_has_suffix (name, ".xml") == FALSE)
 
541
            continue;
 
542
 
 
543
          if (g_hash_table_lookup (loaded_files, name) != NULL)
 
544
            {
 
545
              g_debug ("Not loading %s, it was already loaded from another directory", name);
 
546
              continue;
 
547
            }
 
548
 
 
549
          g_hash_table_insert (loaded_files, g_strdup (name), GINT_TO_POINTER (1));
 
550
          path = g_build_filename (dir_path, name, NULL);
 
551
          append_sections_from_file (path, data_dirs[i], wm_keybindings);
 
552
          g_free (path);
 
553
        }
 
554
      g_free (dir_path);
 
555
      g_dir_close (dir);
 
556
    }
 
557
 
 
558
  g_hash_table_destroy (loaded_files);
 
559
  g_strfreev (wm_keybindings);
 
560
 
 
561
  /* Load custom keybindings */
 
562
  append_sections_from_gsettings ();
 
563
}
 
564
 
 
565
static const guint forbidden_keyvals[] = {
 
566
  /* Navigation keys */
 
567
  GDK_KEY_Home,
 
568
  GDK_KEY_Left,
 
569
  GDK_KEY_Up,
 
570
  GDK_KEY_Right,
 
571
  GDK_KEY_Down,
 
572
  GDK_KEY_Page_Up,
 
573
  GDK_KEY_Page_Down,
 
574
  GDK_KEY_End,
 
575
 
 
576
  /* Return */
 
577
  GDK_KEY_KP_Enter,
 
578
  GDK_KEY_Return,
 
579
 
 
580
  GDK_KEY_Mode_switch
 
581
};
 
582
 
 
583
static char*
 
584
binding_name (guint                   keyval,
 
585
              guint                   keycode,
 
586
              GdkModifierType         mask,
 
587
              gboolean                translate)
 
588
{
 
589
  if (keyval != 0 || keycode != 0)
 
590
    return translate ?
 
591
        gtk_accelerator_get_label_with_keycode (NULL, keyval, keycode, mask) :
 
592
        gtk_accelerator_name_with_keycode (NULL, keyval, keycode, mask);
 
593
  else
 
594
    return g_strdup (translate ? _("Disabled") : "");
 
595
}
 
596
 
 
597
static gboolean
 
598
keyval_is_forbidden (guint keyval)
 
599
{
 
600
  guint i;
 
601
 
 
602
  for (i = 0; i < G_N_ELEMENTS(forbidden_keyvals); i++) {
 
603
    if (keyval == forbidden_keyvals[i])
 
604
      return TRUE;
 
605
  }
 
606
 
 
607
  return FALSE;
 
608
}
 
609
 
 
610
typedef struct {
 
611
  CcRegionKeyboardItem *orig_item;
 
612
  CcRegionKeyboardItem *conflict_item;
 
613
  guint new_keyval;
 
614
  GdkModifierType new_mask;
 
615
  guint new_keycode;
 
616
} CcUniquenessData;
 
617
 
 
618
static gboolean
 
619
compare_keys_for_uniqueness (CcRegionKeyboardItem *element,
 
620
                             CcUniquenessData     *data)
 
621
{
 
622
  CcRegionKeyboardItem *orig_item;
 
623
 
 
624
  orig_item = data->orig_item;
 
625
 
 
626
  /* no conflict for : blanks, different modifiers, or ourselves */
 
627
  if (element == NULL || data->new_mask != element->mask ||
 
628
      cc_region_keyboard_item_equal (orig_item, element))
 
629
    return FALSE;
 
630
 
 
631
  if (data->new_keyval != 0) {
 
632
      if (data->new_keyval != element->keyval)
 
633
          return FALSE;
 
634
  } else if (element->keyval != 0 || data->new_keycode != element->keycode)
 
635
    return FALSE;
 
636
 
 
637
  data->conflict_item = element;
 
638
 
 
639
  return TRUE;
 
640
}
 
641
 
 
642
static gboolean
 
643
cb_check_for_uniqueness (gpointer          key,
 
644
                         GPtrArray        *keys_array,
 
645
                         CcUniquenessData *data)
 
646
{
 
647
  guint i;
 
648
 
 
649
  for (i = 0; i < keys_array->len; i++)
 
650
    {
 
651
      CcRegionKeyboardItem *item;
 
652
 
 
653
      item = keys_array->pdata[i];
 
654
      if (compare_keys_for_uniqueness (item, data))
 
655
        return TRUE;
 
656
    }
 
657
  return FALSE;
 
658
}
 
659
 
 
660
gboolean
 
661
keyboard_shortcuts_accel_edited (CcRegionKeyboardItem *item,
 
662
                                 guint                 keyval,
 
663
                                 guint                 keycode,
 
664
                                 GdkModifierType       mask,
 
665
                                 GtkWidget            *toplevel)
 
666
{
 
667
  CcUniquenessData data;
 
668
 
 
669
  /* sanity check */
 
670
  if (item == NULL)
 
671
    return FALSE;
 
672
 
 
673
  /* CapsLock isn't supported as a keybinding modifier, so keep it from confusing us */
 
674
  mask &= ~GDK_LOCK_MASK;
 
675
 
 
676
  data.orig_item = item;
 
677
  data.new_keyval = keyval;
 
678
  data.new_mask = mask;
 
679
  data.new_keycode = keycode;
 
680
  data.conflict_item = NULL;
 
681
 
 
682
  if (keyval != 0 || keycode != 0) /* any number of shortcuts can be disabled */
 
683
    {
 
684
      BindingGroupType i;
 
685
 
 
686
      for (i = BINDING_GROUP_SYSTEM; i <= BINDING_GROUP_USER && data.conflict_item == NULL; i++)
 
687
        {
 
688
          GHashTable *table;
 
689
 
 
690
          table = get_hash_for_group (i);
 
691
          if (!table)
 
692
            continue;
 
693
          g_hash_table_find (table, (GHRFunc) cb_check_for_uniqueness, &data);
 
694
        }
 
695
    }
 
696
 
 
697
  /* Check for unmodified keys */
 
698
  if ((mask == 0 || mask == GDK_SHIFT_MASK) && keycode != 0)
 
699
    {
 
700
      if ((keyval >= GDK_KEY_a && keyval <= GDK_KEY_z)
 
701
           || (keyval >= GDK_KEY_A && keyval <= GDK_KEY_Z)
 
702
           || (keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9)
 
703
           || (keyval >= GDK_KEY_kana_fullstop && keyval <= GDK_KEY_semivoicedsound)
 
704
           || (keyval >= GDK_KEY_Arabic_comma && keyval <= GDK_KEY_Arabic_sukun)
 
705
           || (keyval >= GDK_KEY_Serbian_dje && keyval <= GDK_KEY_Cyrillic_HARDSIGN)
 
706
           || (keyval >= GDK_KEY_Greek_ALPHAaccent && keyval <= GDK_KEY_Greek_omega)
 
707
           || (keyval >= GDK_KEY_hebrew_doublelowline && keyval <= GDK_KEY_hebrew_taf)
 
708
           || (keyval >= GDK_KEY_Thai_kokai && keyval <= GDK_KEY_Thai_lekkao)
 
709
           || (keyval >= GDK_KEY_Hangul && keyval <= GDK_KEY_Hangul_Special)
 
710
           || (keyval >= GDK_KEY_Hangul_Kiyeog && keyval <= GDK_KEY_Hangul_J_YeorinHieuh)
 
711
           || (keyval == GDK_KEY_Tab && mask == 0)
 
712
           || (keyval == GDK_KEY_space && mask == 0)
 
713
           || keyval_is_forbidden (keyval)) {
 
714
        GtkWidget *dialog;
 
715
        char *name;
 
716
 
 
717
        name = binding_name (keyval, keycode, mask, TRUE);
 
718
 
 
719
        dialog =
 
720
          gtk_message_dialog_new (GTK_WINDOW (toplevel),
 
721
                                  GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
 
722
                                  GTK_MESSAGE_WARNING,
 
723
                                  GTK_BUTTONS_CANCEL,
 
724
                                  _("The shortcut \"%s\" cannot be used because it will become impossible to type using this key.\n"
 
725
                                  "Please try with a key such as Control, Alt or Shift at the same time."),
 
726
                                  name);
 
727
 
 
728
        g_free (name);
 
729
        gtk_dialog_run (GTK_DIALOG (dialog));
 
730
        gtk_widget_destroy (dialog);
 
731
 
 
732
        return FALSE;
 
733
      }
 
734
    }
 
735
 
 
736
  /* flag to see if the new accelerator was in use by something */
 
737
  if (data.conflict_item != NULL)
 
738
    {
 
739
      GtkWidget *dialog;
 
740
      char *name;
 
741
      int response;
 
742
 
 
743
      name = binding_name (keyval, keycode, mask, TRUE);
 
744
 
 
745
      dialog =
 
746
        gtk_message_dialog_new (GTK_WINDOW (toplevel),
 
747
                                GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
 
748
                                GTK_MESSAGE_WARNING,
 
749
                                GTK_BUTTONS_CANCEL,
 
750
                                _("The shortcut \"%s\" is already used for\n\"%s\""),
 
751
                                name, data.conflict_item->description);
 
752
      g_free (name);
 
753
 
 
754
      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
 
755
          _("If you reassign the shortcut to \"%s\", the \"%s\" shortcut "
 
756
            "will be disabled."),
 
757
          item->description,
 
758
          data.conflict_item->description);
 
759
 
 
760
      gtk_dialog_add_button (GTK_DIALOG (dialog),
 
761
                             _("_Reassign"),
 
762
                             GTK_RESPONSE_ACCEPT);
 
763
 
 
764
      gtk_dialog_set_default_response (GTK_DIALOG (dialog),
 
765
                                       GTK_RESPONSE_ACCEPT);
 
766
 
 
767
      response = gtk_dialog_run (GTK_DIALOG (dialog));
 
768
      gtk_widget_destroy (dialog);
 
769
 
 
770
      if (response == GTK_RESPONSE_ACCEPT)
 
771
        g_object_set (G_OBJECT (data.conflict_item), "binding", "", NULL);
 
772
      else
 
773
        return FALSE;
 
774
    }
 
775
 
 
776
  return TRUE;
 
777
}
 
778
 
 
779
static void
 
780
on_window_manager_change (const char *wm_name,
 
781
                          gpointer    user_data)
 
782
{
 
783
  reload_sections ();
 
784
}
 
785
 
 
786
void
 
787
keyboard_shortcuts_init (void)
 
788
{
 
789
  wm_common_register_window_manager_change ((GFunc) on_window_manager_change, NULL);
 
790
  binding_settings = g_settings_new (BINDINGS_SCHEMA);
 
791
  reload_sections ();
 
792
}
 
793
 
 
794
void
 
795
keyboard_shortcuts_dispose (void)
 
796
{
 
797
  if (kb_system_sections != NULL)
 
798
    {
 
799
      g_hash_table_destroy (kb_system_sections);
 
800
      kb_system_sections = NULL;
 
801
    }
 
802
  if (kb_apps_sections != NULL)
 
803
    {
 
804
      g_hash_table_destroy (kb_apps_sections);
 
805
      kb_apps_sections = NULL;
 
806
    }
 
807
  if (kb_user_sections != NULL)
 
808
    {
 
809
      g_hash_table_destroy (kb_user_sections);
 
810
      kb_user_sections = NULL;
 
811
    }
 
812
 
 
813
  g_clear_object (&binding_settings);
 
814
}
 
815
 
 
816
static CcRegionKeyboardItem *
 
817
get_item_in_group (BindingGroupType  group,
 
818
                   const gchar      *schema,
 
819
                   const gchar      *key)
 
820
{
 
821
  GHashTable *hash_table = get_hash_for_group (group);
 
822
  GHashTableIter iter;
 
823
  gpointer value;
 
824
 
 
825
  g_hash_table_iter_init (&iter, hash_table);
 
826
  while (g_hash_table_iter_next (&iter, NULL, &value))
 
827
    {
 
828
      GPtrArray *array = value;
 
829
      guint i;
 
830
 
 
831
      for (i = 0; i < array->len; i++)
 
832
        {
 
833
          CcRegionKeyboardItem *item = array->pdata[i];
 
834
 
 
835
          if (g_strcmp0 (item->schema, schema) == 0 &&
 
836
              g_strcmp0 (item->key, key) == 0)
 
837
            return item;
 
838
        }
 
839
    }
 
840
 
 
841
  return NULL;
 
842
}
 
843
 
 
844
CcRegionKeyboardItem *
 
845
keyboard_shortcuts_get_item (const gchar *schema,
 
846
                             const gchar *key)
 
847
{
 
848
  CcRegionKeyboardItem *item = get_item_in_group (BINDING_GROUP_SYSTEM, schema, key);
 
849
 
 
850
  if (item != NULL)
 
851
    return item;
 
852
 
 
853
  item = get_item_in_group (BINDING_GROUP_APPS, schema, key);
 
854
 
 
855
  if (item != NULL)
 
856
    return item;
 
857
 
 
858
  item = get_item_in_group (BINDING_GROUP_USER, schema, key);
 
859
 
 
860
  return item;
 
861
}