~cjcurran/gnome-control-center/make-new-panel

« back to all changes in this revision

Viewing changes to .pc/94_git_adding_shortcuts.patch/panels/keyboard/keyboard-shortcuts.c

  • Committer: Package Import Robot
  • Author(s): Sebastien Bacher
  • Date: 2012-01-23 15:11:19 UTC
  • Revision ID: package-import@ubuntu.com-20120123151119-mm1xi8mdh2m5brz0
Tags: 1:3.2.2-2ubuntu2
* debian/libgnome-control-center1.symbols: restore the symbols tracking, 
  since we build external capplets we better check they don't break
* debian/rules: don't build shlibs for a library which stopped being 
  distributed in GNOME3
* debian/patches/94_git_adding_shortcuts.patch:
  - let add custom shortcuts in any categorie
* debian/patches/95_git_ctrlw_shortcut.patch:
  - ctrl-W close capplets and go back to the overwiew (lp: #863549) 

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
#include <gconf/gconf-client.h>
 
26
#include "eggcellrendererkeys.h"
 
27
#include "keyboard-shortcuts.h"
 
28
#include "cc-keyboard-item.h"
 
29
#include "wm-common.h"
 
30
 
 
31
#define MAX_CUSTOM_SHORTCUTS 1000
 
32
#define GCONF_BINDING_DIR "/desktop/gnome/keybindings"
 
33
#define WID(builder, name) (GTK_WIDGET (gtk_builder_get_object (builder, name)))
 
34
 
 
35
typedef struct {
 
36
  char *name;
 
37
  /* The group of keybindings (system or application) */
 
38
  char *group;
 
39
  /* The gettext package to use to translate the section title */
 
40
  char *package;
 
41
  /* Name of the window manager the keys would apply to */
 
42
  char *wm_name;
 
43
  /* The GSettings schema for the whole file, if any */
 
44
  char *schema;
 
45
  /* an array of KeyListEntry */
 
46
  GArray *entries;
 
47
} KeyList;
 
48
 
 
49
typedef enum {
 
50
  COMPARISON_NONE = 0,
 
51
  COMPARISON_GT,
 
52
  COMPARISON_LT,
 
53
  COMPARISON_EQ
 
54
} Comparison;
 
55
 
 
56
typedef struct
 
57
{
 
58
  CcKeyboardItemType type;
 
59
  char *schema; /* GSettings schema name, if any */
 
60
  char *description; /* description for GSettings types */
 
61
  char *gettext_package; /* used for GConf type */
 
62
  char *name; /* GConf key, GConf directory, or GSettings key name depending on type */
 
63
  int value; /* Value for comparison */
 
64
  char *key; /* GConf key name for the comparison */
 
65
  Comparison comparison;
 
66
} KeyListEntry;
 
67
 
 
68
enum
 
69
{
 
70
  DETAIL_DESCRIPTION_COLUMN,
 
71
  DETAIL_KEYENTRY_COLUMN,
 
72
  DETAIL_N_COLUMNS
 
73
};
 
74
 
 
75
enum
 
76
{
 
77
  SECTION_DESCRIPTION_COLUMN,
 
78
  SECTION_GROUP_COLUMN,
 
79
  SECTION_N_COLUMNS
 
80
};
 
81
 
 
82
static guint maybe_block_accels_id = 0;
 
83
static gboolean block_accels = FALSE;
 
84
static GtkWidget *custom_shortcut_dialog = NULL;
 
85
static GtkWidget *custom_shortcut_name_entry = NULL;
 
86
static GtkWidget *custom_shortcut_command_entry = NULL;
 
87
static GHashTable *kb_system_sections = NULL;
 
88
static GHashTable *kb_apps_sections = NULL;
 
89
static GHashTable *kb_user_sections = NULL;
 
90
static guint workspace_num_notify_id = 0;
 
91
 
 
92
static void
 
93
free_key_array (GPtrArray *keys)
 
94
{
 
95
  if (keys != NULL)
 
96
    {
 
97
      gint i;
 
98
 
 
99
      for (i = 0; i < keys->len; i++)
 
100
        {
 
101
          CcKeyboardItem *item;
 
102
 
 
103
          item = g_ptr_array_index (keys, i);
 
104
 
 
105
          g_object_unref (item);
 
106
        }
 
107
 
 
108
      g_ptr_array_free (keys, TRUE);
 
109
    }
 
110
}
 
111
 
 
112
static GHashTable *
 
113
get_hash_for_group (BindingGroupType group)
 
114
{
 
115
  GHashTable *hash;
 
116
 
 
117
  switch (group)
 
118
    {
 
119
    case BINDING_GROUP_SYSTEM:
 
120
      hash = kb_system_sections;
 
121
      break;
 
122
    case BINDING_GROUP_APPS:
 
123
      hash = kb_apps_sections;
 
124
      break;
 
125
    case BINDING_GROUP_USER:
 
126
      hash = kb_user_sections;
 
127
      break;
 
128
    default:
 
129
      g_assert_not_reached ();
 
130
    }
 
131
  return hash;
 
132
}
 
133
 
 
134
static gboolean
 
135
have_key_for_group (int group, const gchar *name)
 
136
{
 
137
  GHashTableIter iter;
 
138
  GPtrArray *keys;
 
139
  gint i;
 
140
 
 
141
  g_hash_table_iter_init (&iter, get_hash_for_group (group));
 
142
  while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&keys))
 
143
    {
 
144
      for (i = 0; i < keys->len; i++)
 
145
        {
 
146
          CcKeyboardItem *item = g_ptr_array_index (keys, i);
 
147
 
 
148
          if (g_strcmp0 (name, item->gconf_key) == 0)
 
149
            return TRUE;
 
150
        }
 
151
    }
 
152
 
 
153
  return FALSE;
 
154
}
 
155
 
 
156
static gboolean
 
157
should_show_key (const KeyListEntry *entry)
 
158
{
 
159
  int value;
 
160
  GConfClient *client;
 
161
 
 
162
  if (entry->comparison == COMPARISON_NONE)
 
163
    return TRUE;
 
164
 
 
165
  g_return_val_if_fail (entry->key != NULL, FALSE);
 
166
 
 
167
  /* FIXME: We'll need to change that when metacity/mutter
 
168
   * uses GSettings */
 
169
  g_assert (entry->type == CC_KEYBOARD_ITEM_TYPE_GCONF);
 
170
 
 
171
  client = gconf_client_get_default();
 
172
  value = gconf_client_get_int (client, entry->key, NULL);
 
173
  g_object_unref (client);
 
174
 
 
175
  switch (entry->comparison) {
 
176
  case COMPARISON_NONE:
 
177
    /* For compiler warnings */
 
178
    g_assert_not_reached ();
 
179
    return FALSE;
 
180
  case COMPARISON_GT:
 
181
    if (value > entry->value)
 
182
      return TRUE;
 
183
    break;
 
184
  case COMPARISON_LT:
 
185
    if (value < entry->value)
 
186
      return TRUE;
 
187
    break;
 
188
  case COMPARISON_EQ:
 
189
    if (value == entry->value)
 
190
      return TRUE;
 
191
    break;
 
192
  }
 
193
 
 
194
  return FALSE;
 
195
}
 
196
 
 
197
static gboolean
 
198
keybinding_key_changed_foreach (GtkTreeModel   *model,
 
199
                                GtkTreePath    *path,
 
200
                                GtkTreeIter    *iter,
 
201
                                CcKeyboardItem *item)
 
202
{
 
203
  CcKeyboardItem *tmp_item;
 
204
 
 
205
  gtk_tree_model_get (item->model, iter,
 
206
                      DETAIL_KEYENTRY_COLUMN, &tmp_item,
 
207
                      -1);
 
208
 
 
209
  if (item == tmp_item)
 
210
    {
 
211
      gtk_tree_model_row_changed (item->model, path, iter);
 
212
      return TRUE;
 
213
    }
 
214
  return FALSE;
 
215
}
 
216
 
 
217
static void
 
218
item_changed (CcKeyboardItem *item,
 
219
              GParamSpec     *pspec,
 
220
              gpointer        user_data)
 
221
{
 
222
  /* update the model */
 
223
  gtk_tree_model_foreach (item->model, (GtkTreeModelForeachFunc) keybinding_key_changed_foreach, item);
 
224
}
 
225
 
 
226
static void
 
227
append_section (GtkBuilder         *builder,
 
228
                const gchar        *title,
 
229
                BindingGroupType    group,
 
230
                const KeyListEntry *keys_list)
 
231
{
 
232
  GPtrArray *keys_array;
 
233
  GtkTreeModel *sort_model;
 
234
  GtkTreeModel *model, *shortcut_model;
 
235
  GtkTreeIter iter;
 
236
  gint i;
 
237
  GHashTable *hash;
 
238
  gboolean is_new;
 
239
 
 
240
  hash = get_hash_for_group (group);
 
241
 
 
242
  sort_model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder, "section_treeview")));
 
243
  model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model));
 
244
 
 
245
  shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder, "shortcut_treeview")));
 
246
 
 
247
  /* Add all CcKeyboardItems for this section */
 
248
  is_new = FALSE;
 
249
  keys_array = g_hash_table_lookup (hash, title);
 
250
  if (keys_array == NULL)
 
251
    {
 
252
      keys_array = g_ptr_array_new ();
 
253
      is_new = TRUE;
 
254
    }
 
255
 
 
256
  for (i = 0; keys_list != NULL && keys_list[i].name != NULL; i++)
 
257
    {
 
258
      CcKeyboardItem *item;
 
259
      gboolean ret;
 
260
 
 
261
      if (!should_show_key (&keys_list[i]))
 
262
        continue;
 
263
 
 
264
      if (have_key_for_group (group, keys_list[i].name)) /* FIXME broken for GSettings */
 
265
        continue;
 
266
 
 
267
      item = cc_keyboard_item_new (keys_list[i].type);
 
268
      switch (keys_list[i].type)
 
269
        {
 
270
        case CC_KEYBOARD_ITEM_TYPE_GCONF:
 
271
          ret = cc_keyboard_item_load_from_gconf (item, keys_list[i].gettext_package, keys_list[i].name);
 
272
          break;
 
273
        case CC_KEYBOARD_ITEM_TYPE_GCONF_DIR:
 
274
          ret = cc_keyboard_item_load_from_gconf_dir (item, keys_list[i].name);
 
275
          break;
 
276
        case CC_KEYBOARD_ITEM_TYPE_GSETTINGS:
 
277
          ret = cc_keyboard_item_load_from_gsettings (item,
 
278
                                                      keys_list[i].description,
 
279
                                                      keys_list[i].schema,
 
280
                                                      keys_list[i].name);
 
281
          break;
 
282
        default:
 
283
          g_assert_not_reached ();
 
284
        }
 
285
 
 
286
      if (ret == FALSE)
 
287
        {
 
288
          /* We don't actually want to popup a dialog - just skip this one */
 
289
          g_object_unref (item);
 
290
          continue;
 
291
        }
 
292
 
 
293
      item->model = shortcut_model;
 
294
      item->group = group;
 
295
 
 
296
      g_signal_connect (G_OBJECT (item), "notify",
 
297
                        G_CALLBACK (item_changed), NULL);
 
298
 
 
299
      g_ptr_array_add (keys_array, item);
 
300
    }
 
301
 
 
302
  /* Add the keys to the hash table */
 
303
  if (is_new)
 
304
    {
 
305
      static gboolean have_sep = FALSE;
 
306
 
 
307
      g_hash_table_insert (hash, g_strdup (title), keys_array);
 
308
 
 
309
      /* Append the section to the left tree view */
 
310
      gtk_list_store_append (GTK_LIST_STORE (model), &iter);
 
311
      gtk_list_store_set (GTK_LIST_STORE (model), &iter,
 
312
                          SECTION_DESCRIPTION_COLUMN, title,
 
313
                          SECTION_GROUP_COLUMN, group,
 
314
                          -1);
 
315
      if (!have_sep && group != BINDING_GROUP_SYSTEM)
 
316
        {
 
317
          have_sep = TRUE;
 
318
          /* Append the section to the left tree view */
 
319
          gtk_list_store_append (GTK_LIST_STORE (model), &iter);
 
320
          gtk_list_store_set (GTK_LIST_STORE (model), &iter,
 
321
                              SECTION_DESCRIPTION_COLUMN, NULL,
 
322
                              SECTION_GROUP_COLUMN, BINDING_GROUP_SYSTEM,
 
323
                              -1);
 
324
        }
 
325
    }
 
326
}
 
327
 
 
328
static void
 
329
parse_start_tag (GMarkupParseContext *ctx,
 
330
                 const gchar         *element_name,
 
331
                 const gchar        **attr_names,
 
332
                 const gchar        **attr_values,
 
333
                 gpointer             user_data,
 
334
                 GError             **error)
 
335
{
 
336
  KeyList *keylist = (KeyList *) user_data;
 
337
  KeyListEntry key;
 
338
  const char *name, *gconf_key, *schema, *description, *package;
 
339
  int value;
 
340
  Comparison comparison;
 
341
 
 
342
  name = NULL;
 
343
  schema = NULL;
 
344
  package = NULL;
 
345
 
 
346
  /* The top-level element, names the section in the tree */
 
347
  if (g_str_equal (element_name, "KeyListEntries"))
 
348
    {
 
349
      const char *wm_name = NULL;
 
350
      const char *group = NULL;
 
351
 
 
352
      while (*attr_names && *attr_values)
 
353
        {
 
354
          if (g_str_equal (*attr_names, "name"))
 
355
            {
 
356
              if (**attr_values)
 
357
                name = *attr_values;
 
358
            } else if (g_str_equal (*attr_names, "group")) {
 
359
              if (**attr_values)
 
360
                group = *attr_values;
 
361
            } else if (g_str_equal (*attr_names, "wm_name")) {
 
362
              if (**attr_values)
 
363
                wm_name = *attr_values;
 
364
            } else if (g_str_equal (*attr_names, "schema")) {
 
365
              if (**attr_values)
 
366
                schema = *attr_values;
 
367
            } else if (g_str_equal (*attr_names, "package")) {
 
368
              if (**attr_values)
 
369
                package = *attr_values;
 
370
            }
 
371
          ++attr_names;
 
372
          ++attr_values;
 
373
        }
 
374
 
 
375
      if (name)
 
376
        {
 
377
          if (keylist->name)
 
378
            g_warning ("Duplicate section name");
 
379
          g_free (keylist->name);
 
380
          keylist->name = g_strdup (name);
 
381
        }
 
382
      if (wm_name)
 
383
        {
 
384
          if (keylist->wm_name)
 
385
            g_warning ("Duplicate window manager name");
 
386
          g_free (keylist->wm_name);
 
387
          keylist->wm_name = g_strdup (wm_name);
 
388
        }
 
389
      if (package)
 
390
        {
 
391
          if (keylist->package)
 
392
            g_warning ("Duplicate gettext package name");
 
393
          g_free (keylist->package);
 
394
          keylist->package = g_strdup (package);
 
395
          bind_textdomain_codeset (keylist->package, "UTF-8");
 
396
        }
 
397
      if (group)
 
398
        {
 
399
          if (keylist->group)
 
400
            g_warning ("Duplicate group");
 
401
          g_free (keylist->group);
 
402
          keylist->group = g_strdup (group);
 
403
        }
 
404
      if (schema)
 
405
        {
 
406
          if (keylist->schema)
 
407
            g_warning ("Duplicate schema");
 
408
          g_free (keylist->schema);
 
409
          keylist->schema = g_strdup (schema);
 
410
        }
 
411
      return;
 
412
    }
 
413
 
 
414
  if (!g_str_equal (element_name, "KeyListEntry")
 
415
      || attr_names == NULL
 
416
      || attr_values == NULL)
 
417
    return;
 
418
 
 
419
  value = 0;
 
420
  comparison = COMPARISON_NONE;
 
421
  gconf_key = NULL;
 
422
  schema = NULL;
 
423
  description = NULL;
 
424
 
 
425
  while (*attr_names && *attr_values)
 
426
    {
 
427
      if (g_str_equal (*attr_names, "name"))
 
428
        {
 
429
          /* skip if empty */
 
430
          if (**attr_values)
 
431
            name = *attr_values;
 
432
        } else if (g_str_equal (*attr_names, "value")) {
 
433
          if (**attr_values) {
 
434
            value = (int) g_ascii_strtoull (*attr_values, NULL, 0);
 
435
          }
 
436
        } else if (g_str_equal (*attr_names, "key")) {
 
437
          if (**attr_values) {
 
438
            gconf_key = *attr_values;
 
439
          }
 
440
        } else if (g_str_equal (*attr_names, "schema")) {
 
441
          if (**attr_values) {
 
442
           schema = *attr_values;
 
443
          }
 
444
        } else if (g_str_equal (*attr_names, "description")) {
 
445
          if (**attr_values) {
 
446
            if (keylist->package)
 
447
              {
 
448
                description = dgettext (keylist->package, *attr_values);
 
449
              }
 
450
            else
 
451
              {
 
452
                description = _(*attr_values);
 
453
              }
 
454
          }
 
455
        } else if (g_str_equal (*attr_names, "comparison")) {
 
456
          if (**attr_values) {
 
457
            if (g_str_equal (*attr_values, "gt")) {
 
458
              comparison = COMPARISON_GT;
 
459
            } else if (g_str_equal (*attr_values, "lt")) {
 
460
              comparison = COMPARISON_LT;
 
461
            } else if (g_str_equal (*attr_values, "eq")) {
 
462
              comparison = COMPARISON_EQ;
 
463
            }
 
464
          }
 
465
        }
 
466
 
 
467
      ++attr_names;
 
468
      ++attr_values;
 
469
    }
 
470
 
 
471
  if (name == NULL)
 
472
    return;
 
473
 
 
474
  key.name = g_strdup (name);
 
475
  if (schema != NULL ||
 
476
      keylist->schema != NULL)
 
477
    key.type = CC_KEYBOARD_ITEM_TYPE_GSETTINGS;
 
478
  else
 
479
    key.type = CC_KEYBOARD_ITEM_TYPE_GCONF;
 
480
  key.value = value;
 
481
  key.key = g_strdup (gconf_key);
 
482
  key.description = g_strdup (description);
 
483
  key.gettext_package = g_strdup (keylist->package);
 
484
  key.schema = schema ? g_strdup (schema) : g_strdup (keylist->schema);
 
485
  key.comparison = comparison;
 
486
  g_array_append_val (keylist->entries, key);
 
487
}
 
488
 
 
489
static gboolean
 
490
strv_contains (char **strv,
 
491
               char  *str)
 
492
{
 
493
  char **p = strv;
 
494
  for (p = strv; *p; p++)
 
495
    if (strcmp (*p, str) == 0)
 
496
      return TRUE;
 
497
 
 
498
  return FALSE;
 
499
}
 
500
 
 
501
static void
 
502
append_sections_from_file (GtkBuilder *builder, const gchar *path, const char *datadir, gchar **wm_keybindings)
 
503
{
 
504
  GError *err = NULL;
 
505
  char *buf;
 
506
  gsize buf_len;
 
507
  KeyList *keylist;
 
508
  KeyListEntry key, *keys;
 
509
  const char *title;
 
510
  int group;
 
511
  guint i;
 
512
  GMarkupParseContext *ctx;
 
513
  GMarkupParser parser = { parse_start_tag, NULL, NULL, NULL, NULL };
 
514
 
 
515
  /* Parse file */
 
516
  if (!g_file_get_contents (path, &buf, &buf_len, &err))
 
517
    return;
 
518
 
 
519
  keylist = g_new0 (KeyList, 1);
 
520
  keylist->entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
 
521
  ctx = g_markup_parse_context_new (&parser, 0, keylist, NULL);
 
522
 
 
523
  if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err))
 
524
    {
 
525
      g_warning ("Failed to parse '%s': '%s'", path, err->message);
 
526
      g_error_free (err);
 
527
      g_free (keylist->name);
 
528
      g_free (keylist->package);
 
529
      g_free (keylist->wm_name);
 
530
      for (i = 0; i < keylist->entries->len; i++)
 
531
        g_free (((KeyListEntry *) &(keylist->entries->data[i]))->name);
 
532
      g_array_free (keylist->entries, TRUE);
 
533
      g_free (keylist);
 
534
      keylist = NULL;
 
535
    }
 
536
  g_markup_parse_context_free (ctx);
 
537
  g_free (buf);
 
538
 
 
539
  if (keylist == NULL)
 
540
    return;
 
541
 
 
542
  /* If there's no keys to add, or the settings apply to a window manager
 
543
   * that's not the one we're running */
 
544
  if (keylist->entries->len == 0
 
545
      || (keylist->wm_name != NULL && !strv_contains (wm_keybindings, keylist->wm_name))
 
546
      || keylist->name == NULL)
 
547
    {
 
548
      g_free (keylist->name);
 
549
      g_free (keylist->package);
 
550
      g_free (keylist->wm_name);
 
551
      g_array_free (keylist->entries, TRUE);
 
552
      g_free (keylist);
 
553
      return;
 
554
    }
 
555
 
 
556
  /* Empty KeyListEntry to end the array */
 
557
  key.name = NULL;
 
558
  key.key = NULL;
 
559
  key.value = 0;
 
560
  key.comparison = COMPARISON_NONE;
 
561
  g_array_append_val (keylist->entries, key);
 
562
 
 
563
  keys = (KeyListEntry *) g_array_free (keylist->entries, FALSE);
 
564
  if (keylist->package)
 
565
    {
 
566
      char *localedir;
 
567
 
 
568
      localedir = g_build_filename (datadir, "locale", NULL);
 
569
      bindtextdomain (keylist->package, localedir);
 
570
      g_free (localedir);
 
571
 
 
572
      title = dgettext (keylist->package, keylist->name);
 
573
    } else {
 
574
      title = _(keylist->name);
 
575
    }
 
576
  if (keylist->group && strcmp (keylist->group, "system") == 0)
 
577
    group = BINDING_GROUP_SYSTEM;
 
578
  else
 
579
    group = BINDING_GROUP_APPS;
 
580
 
 
581
  append_section (builder, title, group, keys);
 
582
 
 
583
  g_free (keylist->name);
 
584
  g_free (keylist->package);
 
585
  g_free (keylist->wm_name);
 
586
  g_free (keylist->schema);
 
587
  g_free (keylist->group);
 
588
 
 
589
  for (i = 0; keys[i].name != NULL; i++) {
 
590
    KeyListEntry *entry = &keys[i];
 
591
    g_free (entry->schema);
 
592
    g_free (entry->description);
 
593
    g_free (entry->gettext_package);
 
594
    g_free (entry->name);
 
595
    g_free (entry->key);
 
596
  }
 
597
 
 
598
  g_free (keylist);
 
599
}
 
600
 
 
601
static void
 
602
append_sections_from_gconf (GtkBuilder *builder, const gchar *gconf_path)
 
603
{
 
604
  GConfClient *client;
 
605
  GSList *custom_list, *l;
 
606
  GArray *entries;
 
607
  KeyListEntry key;
 
608
 
 
609
  /* load custom shortcuts from GConf */
 
610
  entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
 
611
 
 
612
  key.key = NULL;
 
613
  key.value = 0;
 
614
  key.comparison = COMPARISON_NONE;
 
615
 
 
616
  client = gconf_client_get_default ();
 
617
  custom_list = gconf_client_all_dirs (client, gconf_path, NULL);
 
618
 
 
619
  for (l = custom_list; l != NULL; l = l->next)
 
620
    {
 
621
      key.name = g_strdup (l->data);
 
622
      if (!have_key_for_group (BINDING_GROUP_USER, key.name))
 
623
        {
 
624
          key.type = CC_KEYBOARD_ITEM_TYPE_GCONF_DIR;
 
625
          g_array_append_val (entries, key);
 
626
        }
 
627
      else
 
628
        g_free (key.name);
 
629
 
 
630
      g_free (l->data);
 
631
    }
 
632
 
 
633
  g_slist_free (custom_list);
 
634
  g_object_unref (client);
 
635
 
 
636
  if (entries->len > 0)
 
637
    {
 
638
      KeyListEntry *keys;
 
639
      int i;
 
640
 
 
641
      /* Empty KeyListEntry to end the array */
 
642
      key.name = NULL;
 
643
      g_array_append_val (entries, key);
 
644
 
 
645
      keys = (KeyListEntry *) entries->data;
 
646
      append_section (builder, _("Custom Shortcuts"), BINDING_GROUP_USER, keys);
 
647
      for (i = 0; i < entries->len; ++i)
 
648
        {
 
649
          g_free (keys[i].name);
 
650
        }
 
651
    }
 
652
  else
 
653
    {
 
654
      append_section (builder, _("Custom Shortcuts"), BINDING_GROUP_USER, NULL);
 
655
    }
 
656
 
 
657
  g_array_free (entries, TRUE);
 
658
}
 
659
 
 
660
static void
 
661
reload_sections (GtkBuilder *builder)
 
662
{
 
663
  gchar **wm_keybindings;
 
664
  GDir *dir;
 
665
  const gchar *name;
 
666
  GtkTreeModel *sort_model;
 
667
  GtkTreeModel *section_model;
 
668
  GtkTreeModel *shortcut_model;
 
669
  const gchar * const * data_dirs;
 
670
  guint i;
 
671
  GtkTreeView *section_treeview;
 
672
  GtkTreeSelection *selection;
 
673
  GtkTreeIter iter;
 
674
 
 
675
  section_treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "section_treeview"));
 
676
  sort_model = gtk_tree_view_get_model (section_treeview);
 
677
  section_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model));
 
678
 
 
679
  shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder, "shortcut_treeview")));
 
680
  /* FIXME: get current selection and keep it after refreshing */
 
681
 
 
682
  /* Clear previous models and hash tables */
 
683
  gtk_list_store_clear (GTK_LIST_STORE (section_model));
 
684
  gtk_list_store_clear (GTK_LIST_STORE (shortcut_model));
 
685
  if (kb_system_sections != NULL)
 
686
    g_hash_table_destroy (kb_system_sections);
 
687
  kb_system_sections = g_hash_table_new_full (g_str_hash,
 
688
                                              g_str_equal,
 
689
                                              g_free,
 
690
                                              (GDestroyNotify) free_key_array);
 
691
 
 
692
  if (kb_apps_sections != NULL)
 
693
    g_hash_table_destroy (kb_apps_sections);
 
694
  kb_apps_sections = g_hash_table_new_full (g_str_hash,
 
695
                                            g_str_equal,
 
696
                                            g_free,
 
697
                                            (GDestroyNotify) free_key_array);
 
698
 
 
699
  if (kb_user_sections != NULL)
 
700
    g_hash_table_destroy (kb_user_sections);
 
701
  kb_user_sections = g_hash_table_new_full (g_str_hash,
 
702
                                            g_str_equal,
 
703
                                            g_free,
 
704
                                            (GDestroyNotify) free_key_array);
 
705
 
 
706
  /* Load WM keybindings */
 
707
  wm_keybindings = wm_common_get_current_keybindings ();
 
708
 
 
709
  data_dirs = g_get_system_data_dirs ();
 
710
  for (i = 0; data_dirs[i] != NULL; i++)
 
711
    {
 
712
      char *dir_path;
 
713
 
 
714
      dir_path = g_build_filename (data_dirs[i], "gnome-control-center", "keybindings", NULL);
 
715
 
 
716
      dir = g_dir_open (dir_path, 0, NULL);
 
717
      if (!dir)
 
718
        {
 
719
          g_free (dir_path);
 
720
          continue;
 
721
        }
 
722
 
 
723
      for (name = g_dir_read_name (dir) ; name ; name = g_dir_read_name (dir))
 
724
        {
 
725
          if (g_str_has_suffix (name, ".xml"))
 
726
            {
 
727
              gchar *path;
 
728
 
 
729
              path = g_build_filename (dir_path, name, NULL);
 
730
              append_sections_from_file (builder, path, data_dirs[i], wm_keybindings);
 
731
 
 
732
              g_free (path);
 
733
            }
 
734
        }
 
735
      g_free (dir_path);
 
736
      g_dir_close (dir);
 
737
    }
 
738
 
 
739
  g_strfreev (wm_keybindings);
 
740
 
 
741
  /* Load custom keybindings */
 
742
  append_sections_from_gconf (builder, GCONF_BINDING_DIR);
 
743
 
 
744
  /* Select the first item */
 
745
  gtk_tree_model_get_iter_first (sort_model, &iter);
 
746
  selection = gtk_tree_view_get_selection (section_treeview);
 
747
  gtk_tree_selection_select_iter (selection, &iter);
 
748
}
 
749
 
 
750
static void
 
751
accel_set_func (GtkTreeViewColumn *tree_column,
 
752
                GtkCellRenderer   *cell,
 
753
                GtkTreeModel      *model,
 
754
                GtkTreeIter       *iter,
 
755
                gpointer           data)
 
756
{
 
757
  CcKeyboardItem *item;
 
758
 
 
759
  gtk_tree_model_get (model, iter,
 
760
                      DETAIL_KEYENTRY_COLUMN, &item,
 
761
                      -1);
 
762
 
 
763
  if (item == NULL)
 
764
    g_object_set (cell,
 
765
                  "visible", FALSE,
 
766
                  NULL);
 
767
  else if (! item->editable)
 
768
    g_object_set (cell,
 
769
                  "visible", TRUE,
 
770
                  "editable", FALSE,
 
771
                  "accel_key", item->keyval,
 
772
                  "accel_mask", item->mask,
 
773
                  "keycode", item->keycode,
 
774
                  "style", PANGO_STYLE_ITALIC,
 
775
                  NULL);
 
776
  else
 
777
    g_object_set (cell,
 
778
                  "visible", TRUE,
 
779
                  "editable", TRUE,
 
780
                  "accel_key", item->keyval,
 
781
                  "accel_mask", item->mask,
 
782
                  "keycode", item->keycode,
 
783
                  "style", PANGO_STYLE_NORMAL,
 
784
                  NULL);
 
785
}
 
786
 
 
787
static void
 
788
description_set_func (GtkTreeViewColumn *tree_column,
 
789
                      GtkCellRenderer   *cell,
 
790
                      GtkTreeModel      *model,
 
791
                      GtkTreeIter       *iter,
 
792
                      gpointer           data)
 
793
{
 
794
  CcKeyboardItem *item;
 
795
 
 
796
  gtk_tree_model_get (model, iter,
 
797
                      DETAIL_KEYENTRY_COLUMN, &item,
 
798
                      -1);
 
799
 
 
800
  if (item != NULL)
 
801
    g_object_set (cell,
 
802
                  "editable", FALSE,
 
803
                  "text", item->description != NULL ?
 
804
                          item->description : _("<Unknown Action>"),
 
805
                  NULL);
 
806
  else
 
807
    g_object_set (cell,
 
808
                  "editable", FALSE, NULL);
 
809
}
 
810
 
 
811
static void
 
812
shortcut_selection_changed (GtkTreeSelection *selection, gpointer data)
 
813
{
 
814
  GtkWidget *button = data;
 
815
  GtkTreeModel *model;
 
816
  GtkTreeIter iter;
 
817
  CcKeyboardItem *item;
 
818
  gboolean can_remove;
 
819
 
 
820
  can_remove = FALSE;
 
821
  if (gtk_tree_selection_get_selected (selection, &model, &iter))
 
822
    {
 
823
      gtk_tree_model_get (model, &iter, DETAIL_KEYENTRY_COLUMN, &item, -1);
 
824
      if (item && item->command != NULL && item->editable)
 
825
        can_remove = TRUE;
 
826
    }
 
827
 
 
828
  gtk_widget_set_sensitive (button, can_remove);
 
829
}
 
830
 
 
831
static void
 
832
section_selection_changed (GtkTreeSelection *selection, gpointer data)
 
833
{
 
834
  GtkTreeIter iter;
 
835
  GtkTreeModel *model;
 
836
  GtkBuilder *builder = GTK_BUILDER (data);
 
837
 
 
838
  if (gtk_tree_selection_get_selected (selection, &model, &iter))
 
839
    {
 
840
      GPtrArray *keys;
 
841
      GtkWidget *shortcut_treeview;
 
842
      GtkTreeModel *shortcut_model;
 
843
      gchar *description;
 
844
      BindingGroupType group;
 
845
      gint i;
 
846
 
 
847
      gtk_tree_model_get (model, &iter,
 
848
                          SECTION_DESCRIPTION_COLUMN, &description,
 
849
                          SECTION_GROUP_COLUMN, &group, -1);
 
850
 
 
851
      keys = g_hash_table_lookup (get_hash_for_group (group), description);
 
852
      if (keys == NULL)
 
853
        {
 
854
          g_warning ("Can't find section %s in sections hash table!!!", description);
 
855
          return;
 
856
        }
 
857
 
 
858
      gtk_widget_set_sensitive (WID (builder, "add-toolbutton"),
 
859
                                group == BINDING_GROUP_USER);
 
860
      gtk_widget_set_sensitive (WID (builder, "remove-toolbutton"), FALSE);
 
861
 
 
862
      /* Fill the shortcut treeview with the keys for the selected section */
 
863
      shortcut_treeview = GTK_WIDGET (gtk_builder_get_object (builder, "shortcut_treeview"));
 
864
      shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (shortcut_treeview));
 
865
      gtk_list_store_clear (GTK_LIST_STORE (shortcut_model));
 
866
 
 
867
      for (i = 0; i < keys->len; i++)
 
868
        {
 
869
          GtkTreeIter new_row;
 
870
          CcKeyboardItem *item = g_ptr_array_index (keys, i);
 
871
 
 
872
          gtk_list_store_append (GTK_LIST_STORE (shortcut_model), &new_row);
 
873
          gtk_list_store_set (GTK_LIST_STORE (shortcut_model), &new_row,
 
874
                              DETAIL_DESCRIPTION_COLUMN, item->description,
 
875
                              DETAIL_KEYENTRY_COLUMN, item,
 
876
                              -1);
 
877
        }
 
878
    }
 
879
}
 
880
 
 
881
typedef struct
 
882
{
 
883
  GtkTreeView *tree_view;
 
884
  GtkTreePath *path;
 
885
  GtkTreeViewColumn *column;
 
886
} IdleData;
 
887
 
 
888
static gboolean
 
889
edit_custom_shortcut (CcKeyboardItem *item)
 
890
{
 
891
  gint result;
 
892
  const gchar *description, *command;
 
893
  gboolean ret;
 
894
 
 
895
  gtk_entry_set_text (GTK_ENTRY (custom_shortcut_name_entry), item->description ? item->description : "");
 
896
  gtk_widget_set_sensitive (custom_shortcut_name_entry, item->desc_editable);
 
897
  gtk_widget_grab_focus (custom_shortcut_name_entry);
 
898
  gtk_entry_set_text (GTK_ENTRY (custom_shortcut_command_entry), item->command ? item->command : "");
 
899
  gtk_widget_set_sensitive (custom_shortcut_command_entry, item->cmd_editable);
 
900
 
 
901
  gtk_window_present (GTK_WINDOW (custom_shortcut_dialog));
 
902
  result = gtk_dialog_run (GTK_DIALOG (custom_shortcut_dialog));
 
903
  switch (result)
 
904
    {
 
905
    case GTK_RESPONSE_OK:
 
906
      description = gtk_entry_get_text (GTK_ENTRY (custom_shortcut_name_entry));
 
907
      command = gtk_entry_get_text (GTK_ENTRY (custom_shortcut_command_entry));
 
908
      g_object_set (G_OBJECT (item),
 
909
                    "description", description,
 
910
                    "command", command,
 
911
                    NULL);
 
912
      ret = TRUE;
 
913
      break;
 
914
    default:
 
915
      ret = FALSE;
 
916
      break;
 
917
    }
 
918
 
 
919
  gtk_widget_hide (custom_shortcut_dialog);
 
920
 
 
921
  return ret;
 
922
}
 
923
 
 
924
static gboolean
 
925
remove_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
 
926
{
 
927
  GConfClient *client;
 
928
  gchar *base;
 
929
  CcKeyboardItem *item;
 
930
  GPtrArray *keys_array;
 
931
  GError *err = NULL;
 
932
 
 
933
  gtk_tree_model_get (model, iter,
 
934
                      DETAIL_KEYENTRY_COLUMN, &item,
 
935
                      -1);
 
936
 
 
937
  /* not a custom shortcut */
 
938
  g_assert (item->type == CC_KEYBOARD_ITEM_TYPE_GCONF_DIR);
 
939
 
 
940
  client = gconf_client_get_default ();
 
941
 
 
942
  base = g_strdup (item->gconf_key_dir);
 
943
  g_object_unref (item);
 
944
 
 
945
  if (gconf_client_recursive_unset (client, base, 0, &err) == FALSE)
 
946
    {
 
947
      g_warning ("Failed to unset GConf directory '%s': %s", base, err->message);
 
948
      g_error_free (err);
 
949
    }
 
950
 
 
951
  g_free (base);
 
952
  /* suggest sync now so the unset directory actually gets dropped;
 
953
   * if we don't do this we may end up with 'zombie' shortcuts when
 
954
   * restarting the app */
 
955
  gconf_client_suggest_sync (client, NULL);
 
956
  g_object_unref (client);
 
957
 
 
958
  keys_array = g_hash_table_lookup (get_hash_for_group (BINDING_GROUP_USER), _("Custom Shortcuts"));
 
959
  g_ptr_array_remove (keys_array, item);
 
960
 
 
961
  gtk_list_store_remove (GTK_LIST_STORE (model), iter);
 
962
 
 
963
  return TRUE;
 
964
}
 
965
 
 
966
static void
 
967
update_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
 
968
{
 
969
  CcKeyboardItem *item;
 
970
 
 
971
  gtk_tree_model_get (model, iter,
 
972
                      DETAIL_KEYENTRY_COLUMN, &item,
 
973
                      -1);
 
974
 
 
975
  g_assert (item->type == CC_KEYBOARD_ITEM_TYPE_GCONF_DIR);
 
976
 
 
977
  edit_custom_shortcut (item);
 
978
  if (item->command == NULL || item->command[0] == '\0')
 
979
    {
 
980
      remove_custom_shortcut (model, iter);
 
981
    }
 
982
  else
 
983
    {
 
984
      GConfClient *client;
 
985
 
 
986
      gtk_list_store_set (GTK_LIST_STORE (model), iter,
 
987
                          DETAIL_KEYENTRY_COLUMN, item, -1);
 
988
      client = gconf_client_get_default ();
 
989
      if (item->description != NULL)
 
990
        gconf_client_set_string (client, item->desc_gconf_key, item->description, NULL);
 
991
      else
 
992
        gconf_client_unset (client, item->desc_gconf_key, NULL);
 
993
      gconf_client_set_string (client, item->cmd_gconf_key, item->command, NULL);
 
994
      g_object_unref (client);
 
995
    }
 
996
}
 
997
 
 
998
static gboolean
 
999
real_start_editing_cb (IdleData *idle_data)
 
1000
{
 
1001
  gtk_widget_grab_focus (GTK_WIDGET (idle_data->tree_view));
 
1002
  gtk_tree_view_set_cursor (idle_data->tree_view,
 
1003
                            idle_data->path,
 
1004
                            idle_data->column,
 
1005
                            TRUE);
 
1006
  gtk_tree_path_free (idle_data->path);
 
1007
  g_free (idle_data);
 
1008
  return FALSE;
 
1009
}
 
1010
 
 
1011
static gboolean
 
1012
start_editing_cb (GtkTreeView    *tree_view,
 
1013
                  GdkEventButton *event,
 
1014
                  gpointer        user_data)
 
1015
{
 
1016
  GtkTreePath *path;
 
1017
  GtkTreeViewColumn *column;
 
1018
 
 
1019
  if (event->window != gtk_tree_view_get_bin_window (tree_view))
 
1020
    return FALSE;
 
1021
 
 
1022
  if (gtk_tree_view_get_path_at_pos (tree_view,
 
1023
                                     (gint) event->x,
 
1024
                                     (gint) event->y,
 
1025
                                     &path, &column,
 
1026
                                     NULL, NULL))
 
1027
    {
 
1028
      IdleData *idle_data;
 
1029
      GtkTreeModel *model;
 
1030
      GtkTreeIter iter;
 
1031
      CcKeyboardItem *item;
 
1032
 
 
1033
      if (gtk_tree_path_get_depth (path) == 1)
 
1034
        {
 
1035
          gtk_tree_path_free (path);
 
1036
          return FALSE;
 
1037
        }
 
1038
 
 
1039
      model = gtk_tree_view_get_model (tree_view);
 
1040
      gtk_tree_model_get_iter (model, &iter, path);
 
1041
      gtk_tree_model_get (model, &iter,
 
1042
                          DETAIL_KEYENTRY_COLUMN, &item,
 
1043
                         -1);
 
1044
 
 
1045
      /* if only the accel can be edited on the selected row
 
1046
       * always select the accel column */
 
1047
      if (item->desc_editable &&
 
1048
          column == gtk_tree_view_get_column (tree_view, 0))
 
1049
        {
 
1050
          gtk_widget_grab_focus (GTK_WIDGET (tree_view));
 
1051
          gtk_tree_view_set_cursor (tree_view, path,
 
1052
                                    gtk_tree_view_get_column (tree_view, 0),
 
1053
                                    FALSE);
 
1054
          update_custom_shortcut (model, &iter);
 
1055
        }
 
1056
      else
 
1057
        {
 
1058
          idle_data = g_new (IdleData, 1);
 
1059
          idle_data->tree_view = tree_view;
 
1060
          idle_data->path = path;
 
1061
          idle_data->column = item->desc_editable ? column :
 
1062
                              gtk_tree_view_get_column (tree_view, 1);
 
1063
          g_idle_add ((GSourceFunc) real_start_editing_cb, idle_data);
 
1064
          block_accels = TRUE;
 
1065
        }
 
1066
      g_signal_stop_emission_by_name (tree_view, "button_press_event");
 
1067
    }
 
1068
  return TRUE;
 
1069
}
 
1070
 
 
1071
static void
 
1072
start_editing_kb_cb (GtkTreeView *treeview,
 
1073
                          GtkTreePath *path,
 
1074
                          GtkTreeViewColumn *column,
 
1075
                          gpointer user_data)
 
1076
{
 
1077
  GtkTreeModel *model;
 
1078
  GtkTreeIter iter;
 
1079
  CcKeyboardItem *item;
 
1080
 
 
1081
  model = gtk_tree_view_get_model (treeview);
 
1082
  gtk_tree_model_get_iter (model, &iter, path);
 
1083
  gtk_tree_model_get (model, &iter,
 
1084
                      DETAIL_KEYENTRY_COLUMN, &item,
 
1085
                      -1);
 
1086
 
 
1087
  if (item == NULL)
 
1088
    {
 
1089
      /* This is a section heading - expand or collapse */
 
1090
      if (gtk_tree_view_row_expanded (treeview, path))
 
1091
        gtk_tree_view_collapse_row (treeview, path);
 
1092
      else
 
1093
        gtk_tree_view_expand_row (treeview, path, FALSE);
 
1094
      return;
 
1095
    }
 
1096
 
 
1097
  /* if only the accel can be edited on the selected row
 
1098
   * always select the accel column */
 
1099
  if (item->desc_editable &&
 
1100
      column == gtk_tree_view_get_column (treeview, 0))
 
1101
    {
 
1102
      gtk_widget_grab_focus (GTK_WIDGET (treeview));
 
1103
      gtk_tree_view_set_cursor (treeview, path,
 
1104
                                gtk_tree_view_get_column (treeview, 0),
 
1105
                                FALSE);
 
1106
      update_custom_shortcut (model, &iter);
 
1107
    }
 
1108
  else
 
1109
    {
 
1110
       gtk_widget_grab_focus (GTK_WIDGET (treeview));
 
1111
       gtk_tree_view_set_cursor (treeview,
 
1112
                                 path,
 
1113
                                 gtk_tree_view_get_column (treeview, 1),
 
1114
                                 TRUE);
 
1115
    }
 
1116
}
 
1117
 
 
1118
static void
 
1119
description_edited_callback (GtkCellRendererText *renderer,
 
1120
                             gchar               *path_string,
 
1121
                             gchar               *new_text,
 
1122
                             gpointer             user_data)
 
1123
{
 
1124
  GConfClient *client;
 
1125
  GtkTreeView *view = GTK_TREE_VIEW (user_data);
 
1126
  GtkTreeModel *model;
 
1127
  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
 
1128
  GtkTreeIter iter;
 
1129
  CcKeyboardItem *item;
 
1130
 
 
1131
  model = gtk_tree_view_get_model (view);
 
1132
  gtk_tree_model_get_iter (model, &iter, path);
 
1133
  gtk_tree_path_free (path);
 
1134
 
 
1135
  gtk_tree_model_get (model, &iter,
 
1136
                      DETAIL_KEYENTRY_COLUMN, &item,
 
1137
                      -1);
 
1138
 
 
1139
  /* sanity check */
 
1140
  if (item == NULL || item->desc_gconf_key == NULL)
 
1141
    return;
 
1142
 
 
1143
  g_assert (item->type == CC_KEYBOARD_ITEM_TYPE_GCONF_DIR);
 
1144
 
 
1145
  client = gconf_client_get_default ();
 
1146
  if (!gconf_client_set_string (client, item->desc_gconf_key, new_text, NULL))
 
1147
    item->desc_editable = FALSE;
 
1148
 
 
1149
  g_object_unref (client);
 
1150
}
 
1151
 
 
1152
static const guint forbidden_keyvals[] = {
 
1153
  /* Navigation keys */
 
1154
  GDK_KEY_Home,
 
1155
  GDK_KEY_Left,
 
1156
  GDK_KEY_Up,
 
1157
  GDK_KEY_Right,
 
1158
  GDK_KEY_Down,
 
1159
  GDK_KEY_Page_Up,
 
1160
  GDK_KEY_Page_Down,
 
1161
  GDK_KEY_End,
 
1162
  GDK_KEY_Tab,
 
1163
 
 
1164
  /* Return */
 
1165
  GDK_KEY_KP_Enter,
 
1166
  GDK_KEY_Return,
 
1167
 
 
1168
  GDK_KEY_space,
 
1169
  GDK_KEY_Mode_switch
 
1170
};
 
1171
 
 
1172
static char*
 
1173
binding_name (guint                   keyval,
 
1174
              guint                   keycode,
 
1175
              EggVirtualModifierType  mask,
 
1176
              gboolean                translate)
 
1177
{
 
1178
  if (keyval != 0 || keycode != 0)
 
1179
    return translate ?
 
1180
        egg_virtual_accelerator_label (keyval, keycode, mask) :
 
1181
        egg_virtual_accelerator_name (keyval, keycode, mask);
 
1182
  else
 
1183
    return g_strdup (translate ? _("Disabled") : "");
 
1184
}
 
1185
 
 
1186
static gboolean
 
1187
keyval_is_forbidden (guint keyval)
 
1188
{
 
1189
  guint i;
 
1190
 
 
1191
  for (i = 0; i < G_N_ELEMENTS(forbidden_keyvals); i++) {
 
1192
    if (keyval == forbidden_keyvals[i])
 
1193
      return TRUE;
 
1194
  }
 
1195
 
 
1196
  return FALSE;
 
1197
}
 
1198
 
 
1199
static void
 
1200
show_error (GtkWindow *parent,
 
1201
            GError *err)
 
1202
{
 
1203
  GtkWidget *dialog;
 
1204
 
 
1205
  dialog = gtk_message_dialog_new (parent,
 
1206
                                   GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
 
1207
                                   GTK_MESSAGE_WARNING,
 
1208
                                   GTK_BUTTONS_OK,
 
1209
                                   _("Error saving the new shortcut"));
 
1210
 
 
1211
  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
 
1212
                                            "%s", err->message);
 
1213
  gtk_dialog_run (GTK_DIALOG (dialog));
 
1214
  gtk_widget_destroy (dialog);
 
1215
}
 
1216
 
 
1217
typedef struct {
 
1218
  CcKeyboardItem *orig_item;
 
1219
  CcKeyboardItem *conflict_item;
 
1220
  guint new_keyval;
 
1221
  EggVirtualModifierType new_mask;
 
1222
  guint new_keycode;
 
1223
} CcUniquenessData;
 
1224
 
 
1225
static gboolean
 
1226
compare_keys_for_uniqueness (CcKeyboardItem   *element,
 
1227
                             CcUniquenessData *data)
 
1228
{
 
1229
  CcKeyboardItem *orig_item;
 
1230
 
 
1231
  orig_item = data->orig_item;
 
1232
 
 
1233
  /* no conflict for : blanks, different modifiers, or ourselves */
 
1234
  if (element == NULL || data->new_mask != element->mask ||
 
1235
      cc_keyboard_item_equal (orig_item, element))
 
1236
    return FALSE;
 
1237
 
 
1238
  if (data->new_keyval != 0) {
 
1239
      if (data->new_keyval != element->keyval)
 
1240
          return FALSE;
 
1241
  } else if (element->keyval != 0 || data->new_keycode != element->keycode)
 
1242
    return FALSE;
 
1243
 
 
1244
  data->conflict_item = element;
 
1245
 
 
1246
  return TRUE;
 
1247
}
 
1248
 
 
1249
static gboolean
 
1250
cb_check_for_uniqueness (gpointer          key,
 
1251
                         GPtrArray        *keys_array,
 
1252
                         CcUniquenessData *data)
 
1253
{
 
1254
  guint i;
 
1255
 
 
1256
  for (i = 0; i < keys_array->len; i++)
 
1257
    {
 
1258
      CcKeyboardItem *item;
 
1259
 
 
1260
      item = keys_array->pdata[i];
 
1261
      if (compare_keys_for_uniqueness (item, data))
 
1262
        return TRUE;
 
1263
    }
 
1264
  return FALSE;
 
1265
}
 
1266
 
 
1267
static void
 
1268
accel_edited_callback (GtkCellRendererText   *cell,
 
1269
                       const char            *path_string,
 
1270
                       guint                  keyval,
 
1271
                       EggVirtualModifierType mask,
 
1272
                       guint                  keycode,
 
1273
                       GtkTreeView           *view)
 
1274
{
 
1275
  GtkTreeModel *model;
 
1276
  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
 
1277
  GtkTreeIter iter;
 
1278
  CcUniquenessData data;
 
1279
  CcKeyboardItem *item;
 
1280
  char *str;
 
1281
 
 
1282
  block_accels = FALSE;
 
1283
 
 
1284
  model = gtk_tree_view_get_model (view);
 
1285
  gtk_tree_model_get_iter (model, &iter, path);
 
1286
  gtk_tree_path_free (path);
 
1287
  gtk_tree_model_get (model, &iter,
 
1288
                      DETAIL_KEYENTRY_COLUMN, &item,
 
1289
                      -1);
 
1290
 
 
1291
  /* sanity check */
 
1292
  if (item == NULL)
 
1293
    return;
 
1294
 
 
1295
  /* CapsLock isn't supported as a keybinding modifier, so keep it from confusing us */
 
1296
  mask &= ~EGG_VIRTUAL_LOCK_MASK;
 
1297
 
 
1298
  data.orig_item = item;
 
1299
  data.new_keyval = keyval;
 
1300
  data.new_mask = mask;
 
1301
  data.new_keycode = keycode;
 
1302
  data.conflict_item = NULL;
 
1303
 
 
1304
  if (keyval != 0 || keycode != 0) /* any number of shortcuts can be disabled */
 
1305
    {
 
1306
      BindingGroupType i;
 
1307
 
 
1308
      for (i = BINDING_GROUP_SYSTEM; i <= BINDING_GROUP_USER && data.conflict_item == NULL; i++)
 
1309
        {
 
1310
          GHashTable *table;
 
1311
 
 
1312
          table = get_hash_for_group (i);
 
1313
          g_hash_table_find (table, (GHRFunc) cb_check_for_uniqueness, &data);
 
1314
        }
 
1315
    }
 
1316
 
 
1317
  /* Check for unmodified keys */
 
1318
  if (mask == 0 && keycode != 0)
 
1319
    {
 
1320
      if ((keyval >= GDK_KEY_a && keyval <= GDK_KEY_z)
 
1321
           || (keyval >= GDK_KEY_A && keyval <= GDK_KEY_Z)
 
1322
           || (keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9)
 
1323
           || (keyval >= GDK_KEY_kana_fullstop && keyval <= GDK_KEY_semivoicedsound)
 
1324
           || (keyval >= GDK_KEY_Arabic_comma && keyval <= GDK_KEY_Arabic_sukun)
 
1325
           || (keyval >= GDK_KEY_Serbian_dje && keyval <= GDK_KEY_Cyrillic_HARDSIGN)
 
1326
           || (keyval >= GDK_KEY_Greek_ALPHAaccent && keyval <= GDK_KEY_Greek_omega)
 
1327
           || (keyval >= GDK_KEY_hebrew_doublelowline && keyval <= GDK_KEY_hebrew_taf)
 
1328
           || (keyval >= GDK_KEY_Thai_kokai && keyval <= GDK_KEY_Thai_lekkao)
 
1329
           || (keyval >= GDK_KEY_Hangul && keyval <= GDK_KEY_Hangul_Special)
 
1330
           || (keyval >= GDK_KEY_Hangul_Kiyeog && keyval <= GDK_KEY_Hangul_J_YeorinHieuh)
 
1331
           || keyval_is_forbidden (keyval)) {
 
1332
        GtkWidget *dialog;
 
1333
        char *name;
 
1334
 
 
1335
        name = binding_name (keyval, keycode, mask, TRUE);
 
1336
 
 
1337
        dialog =
 
1338
          gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
 
1339
                                  GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
 
1340
                                  GTK_MESSAGE_WARNING,
 
1341
                                  GTK_BUTTONS_CANCEL,
 
1342
                                  _("The shortcut \"%s\" cannot be used because it will become impossible to type using this key.\n"
 
1343
                                  "Please try with a key such as Control, Alt or Shift at the same time."),
 
1344
                                  name);
 
1345
 
 
1346
        g_free (name);
 
1347
        gtk_dialog_run (GTK_DIALOG (dialog));
 
1348
        gtk_widget_destroy (dialog);
 
1349
 
 
1350
        /* set it back to its previous value. */
 
1351
        egg_cell_renderer_keys_set_accelerator
 
1352
          (EGG_CELL_RENDERER_KEYS (cell),
 
1353
           item->keyval, item->keycode, item->mask);
 
1354
        return;
 
1355
      }
 
1356
    }
 
1357
 
 
1358
  /* flag to see if the new accelerator was in use by something */
 
1359
  if (data.conflict_item != NULL)
 
1360
    {
 
1361
      GtkWidget *dialog;
 
1362
      char *name;
 
1363
      int response;
 
1364
 
 
1365
      name = binding_name (keyval, keycode, mask, TRUE);
 
1366
 
 
1367
      dialog =
 
1368
        gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
 
1369
                                GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
 
1370
                                GTK_MESSAGE_WARNING,
 
1371
                                GTK_BUTTONS_CANCEL,
 
1372
                                _("The shortcut \"%s\" is already used for\n\"%s\""),
 
1373
                                name, data.conflict_item->description);
 
1374
      g_free (name);
 
1375
 
 
1376
      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
 
1377
          _("If you reassign the shortcut to \"%s\", the \"%s\" shortcut "
 
1378
            "will be disabled."),
 
1379
          item->description,
 
1380
          data.conflict_item->description);
 
1381
 
 
1382
      gtk_dialog_add_button (GTK_DIALOG (dialog),
 
1383
                             _("_Reassign"),
 
1384
                             GTK_RESPONSE_ACCEPT);
 
1385
 
 
1386
      gtk_dialog_set_default_response (GTK_DIALOG (dialog),
 
1387
                                       GTK_RESPONSE_ACCEPT);
 
1388
 
 
1389
      response = gtk_dialog_run (GTK_DIALOG (dialog));
 
1390
      gtk_widget_destroy (dialog);
 
1391
 
 
1392
      if (response == GTK_RESPONSE_ACCEPT)
 
1393
        {
 
1394
          g_object_set (G_OBJECT (data.conflict_item), "binding", "", NULL);
 
1395
 
 
1396
          str = binding_name (keyval, keycode, mask, FALSE);
 
1397
          g_object_set (G_OBJECT (item), "binding", str, NULL);
 
1398
 
 
1399
          g_free (str);
 
1400
        }
 
1401
      else
 
1402
        {
 
1403
          /* set it back to its previous value. */
 
1404
          egg_cell_renderer_keys_set_accelerator (EGG_CELL_RENDERER_KEYS (cell),
 
1405
                                                  item->keyval,
 
1406
                                                  item->keycode,
 
1407
                                                  item->mask);
 
1408
        }
 
1409
 
 
1410
      return;
 
1411
    }
 
1412
 
 
1413
  str = binding_name (keyval, keycode, mask, FALSE);
 
1414
  g_object_set (G_OBJECT (item), "binding", str, NULL);
 
1415
 
 
1416
  g_free (str);
 
1417
}
 
1418
 
 
1419
static void
 
1420
accel_cleared_callback (GtkCellRendererText *cell,
 
1421
                        const char          *path_string,
 
1422
                        gpointer             data)
 
1423
{
 
1424
  GtkTreeView *view = (GtkTreeView *) data;
 
1425
  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
 
1426
  CcKeyboardItem *item;
 
1427
  GtkTreeIter iter;
 
1428
  GtkTreeModel *model;
 
1429
 
 
1430
  block_accels = FALSE;
 
1431
 
 
1432
  model = gtk_tree_view_get_model (view);
 
1433
  gtk_tree_model_get_iter (model, &iter, path);
 
1434
  gtk_tree_path_free (path);
 
1435
  gtk_tree_model_get (model, &iter,
 
1436
                      DETAIL_KEYENTRY_COLUMN, &item,
 
1437
                      -1);
 
1438
 
 
1439
  /* sanity check */
 
1440
  if (item == NULL)
 
1441
    return;
 
1442
 
 
1443
  /* Unset the key */
 
1444
  g_object_set (G_OBJECT (item), "binding", "", NULL);
 
1445
}
 
1446
 
 
1447
static void
 
1448
key_entry_controlling_key_changed (GConfClient *client,
 
1449
                                   guint        cnxn_id,
 
1450
                                   GConfEntry  *entry,
 
1451
                                   gpointer     user_data)
 
1452
{
 
1453
  reload_sections (user_data);
 
1454
}
 
1455
 
 
1456
/* this handler is used to keep accels from activating while the user
 
1457
 * is assigning a new shortcut so that he won't accidentally trigger one
 
1458
 * of the widgets */
 
1459
static gboolean
 
1460
maybe_block_accels (GtkWidget *widget,
 
1461
                    GdkEventKey *event,
 
1462
                    gpointer user_data)
 
1463
{
 
1464
  if (block_accels)
 
1465
  {
 
1466
    return gtk_window_propagate_key_event (GTK_WINDOW (widget), event);
 
1467
  }
 
1468
  return FALSE;
 
1469
}
 
1470
 
 
1471
static gchar *
 
1472
find_free_gconf_key (GError **error)
 
1473
{
 
1474
  GConfClient *client;
 
1475
 
 
1476
  gchar *dir;
 
1477
  int i;
 
1478
 
 
1479
  client = gconf_client_get_default ();
 
1480
 
 
1481
  for (i = 0; i < MAX_CUSTOM_SHORTCUTS; i++)
 
1482
    {
 
1483
      dir = g_strdup_printf ("%s/custom%d", GCONF_BINDING_DIR, i);
 
1484
      if (!gconf_client_dir_exists (client, dir, NULL))
 
1485
        break;
 
1486
      g_free (dir);
 
1487
    }
 
1488
 
 
1489
  if (i == MAX_CUSTOM_SHORTCUTS)
 
1490
    {
 
1491
      dir = NULL;
 
1492
      g_set_error_literal (error,
 
1493
                           g_quark_from_string ("Keyboard Shortcuts"),
 
1494
                           0,
 
1495
                           _("Too many custom shortcuts"));
 
1496
    }
 
1497
 
 
1498
  g_object_unref (client);
 
1499
 
 
1500
  return dir;
 
1501
}
 
1502
 
 
1503
static void
 
1504
add_custom_shortcut (GtkTreeView  *tree_view,
 
1505
                     GtkTreeModel *model)
 
1506
{
 
1507
  CcKeyboardItem *item;
 
1508
  GtkTreePath *path;
 
1509
  gchar *dir;
 
1510
  GError *error;
 
1511
 
 
1512
  error = NULL;
 
1513
  dir = find_free_gconf_key (&error);
 
1514
  if (dir == NULL)
 
1515
    {
 
1516
      show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tree_view))), error);
 
1517
 
 
1518
      g_error_free (error);
 
1519
      return;
 
1520
    }
 
1521
 
 
1522
  /* FIXME this is way ugly */
 
1523
  item = cc_keyboard_item_new (CC_KEYBOARD_ITEM_TYPE_GCONF_DIR);
 
1524
  item->gconf_key_dir = g_strdup (dir);
 
1525
  item->gconf_key = g_strconcat (dir, "/binding", NULL);
 
1526
  item->editable = TRUE;
 
1527
  item->model = model;
 
1528
  item->desc_gconf_key = g_strconcat (dir, "/name", NULL);
 
1529
  item->description = g_strdup ("");
 
1530
  item->desc_editable = TRUE;
 
1531
  item->cmd_gconf_key = g_strconcat (dir, "/action", NULL);
 
1532
  item->command = g_strdup ("");
 
1533
  item->cmd_editable = TRUE;
 
1534
 
 
1535
  if (edit_custom_shortcut (item) &&
 
1536
      item->command && item->command[0])
 
1537
    {
 
1538
      GPtrArray *keys_array;
 
1539
      GtkTreeIter iter;
 
1540
      GHashTable *hash;
 
1541
      GConfClient *client;
 
1542
 
 
1543
      /* store in gconf */
 
1544
      client = gconf_client_get_default ();
 
1545
      gconf_client_set_string (client, item->gconf_key, "", NULL);
 
1546
      gconf_client_set_string (client, item->desc_gconf_key, item->description, NULL);
 
1547
      gconf_client_set_string (client, item->cmd_gconf_key, item->command, NULL);
 
1548
      g_object_unref (client);
 
1549
      g_object_unref (item);
 
1550
 
 
1551
      /* Now setup the actual item we'll be adding */
 
1552
      item = cc_keyboard_item_new (CC_KEYBOARD_ITEM_TYPE_GCONF_DIR);
 
1553
      cc_keyboard_item_load_from_gconf_dir (item, dir);
 
1554
 
 
1555
      hash = get_hash_for_group (BINDING_GROUP_USER);
 
1556
      keys_array = g_hash_table_lookup (hash, _("Custom Shortcuts"));
 
1557
      if (keys_array == NULL)
 
1558
        {
 
1559
          keys_array = g_ptr_array_new ();
 
1560
          g_hash_table_insert (hash, g_strdup (_("Custom Shortcuts")), keys_array);
 
1561
        }
 
1562
 
 
1563
      g_ptr_array_add (keys_array, item);
 
1564
 
 
1565
      gtk_list_store_append (GTK_LIST_STORE (model), &iter);
 
1566
      gtk_list_store_set (GTK_LIST_STORE (model), &iter, DETAIL_KEYENTRY_COLUMN, item, -1);
 
1567
 
 
1568
      /* make the new shortcut visible */
 
1569
      path = gtk_tree_model_get_path (model, &iter);
 
1570
      gtk_tree_view_expand_to_path (tree_view, path);
 
1571
      gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0, 0);
 
1572
      gtk_tree_path_free (path);
 
1573
    }
 
1574
  else
 
1575
    {
 
1576
      g_object_unref (item);
 
1577
    }
 
1578
}
 
1579
 
 
1580
static void
 
1581
add_button_clicked (GtkWidget  *button,
 
1582
                    GtkBuilder *builder)
 
1583
{
 
1584
  GtkTreeView *treeview;
 
1585
  GtkTreeModel *model;
 
1586
 
 
1587
  treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
 
1588
                                                    "shortcut_treeview"));
 
1589
  model = gtk_tree_view_get_model (treeview);
 
1590
 
 
1591
  add_custom_shortcut (treeview, model);
 
1592
}
 
1593
 
 
1594
static void
 
1595
remove_button_clicked (GtkWidget  *button,
 
1596
                       GtkBuilder *builder)
 
1597
{
 
1598
  GtkTreeView *treeview;
 
1599
  GtkTreeModel *model;
 
1600
  GtkTreeSelection *selection;
 
1601
  GtkTreeIter iter;
 
1602
 
 
1603
  treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
 
1604
                                                    "shortcut_treeview"));
 
1605
  model = gtk_tree_view_get_model (treeview);
 
1606
 
 
1607
  selection = gtk_tree_view_get_selection (treeview);
 
1608
  if (gtk_tree_selection_get_selected (selection, NULL, &iter))
 
1609
    {
 
1610
      remove_custom_shortcut (model, &iter);
 
1611
    }
 
1612
}
 
1613
 
 
1614
static int
 
1615
keyentry_sort_func (GtkTreeModel *model,
 
1616
                    GtkTreeIter  *a,
 
1617
                    GtkTreeIter  *b,
 
1618
                    gpointer      user_data)
 
1619
{
 
1620
  CcKeyboardItem *item_a;
 
1621
  CcKeyboardItem *item_b;
 
1622
  int retval;
 
1623
 
 
1624
  item_a = NULL;
 
1625
  gtk_tree_model_get (model, a,
 
1626
                      DETAIL_KEYENTRY_COLUMN, &item_a,
 
1627
                      -1);
 
1628
 
 
1629
  item_b = NULL;
 
1630
  gtk_tree_model_get (model, b,
 
1631
                      DETAIL_KEYENTRY_COLUMN, &item_b,
 
1632
                      -1);
 
1633
 
 
1634
  if (item_a && item_b)
 
1635
    {
 
1636
      if ((item_a->keyval || item_a->keycode) &&
 
1637
          (item_b->keyval || item_b->keycode))
 
1638
        {
 
1639
          gchar *name_a, *name_b;
 
1640
 
 
1641
          name_a = binding_name (item_a->keyval,
 
1642
                                 item_a->keycode,
 
1643
                                 item_a->mask,
 
1644
                                 TRUE);
 
1645
 
 
1646
          name_b = binding_name (item_b->keyval,
 
1647
                                 item_b->keycode,
 
1648
                                 item_b->mask,
 
1649
                                 TRUE);
 
1650
 
 
1651
          retval = g_utf8_collate (name_a, name_b);
 
1652
 
 
1653
          g_free (name_a);
 
1654
          g_free (name_b);
 
1655
        }
 
1656
      else if (item_a->keyval || item_a->keycode)
 
1657
        retval = -1;
 
1658
      else if (item_b->keyval || item_b->keycode)
 
1659
        retval = 1;
 
1660
      else
 
1661
        retval = 0;
 
1662
    }
 
1663
  else if (item_a)
 
1664
    retval = -1;
 
1665
  else if (item_b)
 
1666
    retval = 1;
 
1667
  else
 
1668
    retval = 0;
 
1669
 
 
1670
  return retval;
 
1671
}
 
1672
 
 
1673
static int
 
1674
section_sort_item  (GtkTreeModel *model,
 
1675
                    GtkTreeIter  *a,
 
1676
                    GtkTreeIter  *b,
 
1677
                    gpointer      data)
 
1678
{
 
1679
  char *a_desc;
 
1680
  int   a_group;
 
1681
  char *b_desc;
 
1682
  int   b_group;
 
1683
  int   ret;
 
1684
 
 
1685
  gtk_tree_model_get (model, a,
 
1686
                      SECTION_DESCRIPTION_COLUMN, &a_desc,
 
1687
                      SECTION_GROUP_COLUMN, &a_group,
 
1688
                      -1);
 
1689
  gtk_tree_model_get (model, b,
 
1690
                      SECTION_DESCRIPTION_COLUMN, &b_desc,
 
1691
                      SECTION_GROUP_COLUMN, &b_group,
 
1692
                      -1);
 
1693
 
 
1694
  if (a_group == b_group)
 
1695
    {
 
1696
      /* separators go after the section */
 
1697
      if (a_desc == NULL)
 
1698
        ret = 1;
 
1699
      else if (b_desc == NULL)
 
1700
        ret = -1;
 
1701
      else
 
1702
        ret = g_utf8_collate (a_desc, b_desc);
 
1703
    }
 
1704
  else
 
1705
    {
 
1706
      if (a_group < b_group)
 
1707
        ret = -1;
 
1708
      else
 
1709
        ret = 1;
 
1710
    }
 
1711
 
 
1712
  g_free (a_desc);
 
1713
  g_free (b_desc);
 
1714
 
 
1715
  return ret;
 
1716
}
 
1717
 
 
1718
static gboolean
 
1719
sections_separator_func (GtkTreeModel *model,
 
1720
                         GtkTreeIter  *iter,
 
1721
                         gpointer      data)
 
1722
{
 
1723
  char    *description;
 
1724
  gboolean is_separator;
 
1725
 
 
1726
  description = NULL;
 
1727
  is_separator = FALSE;
 
1728
 
 
1729
  gtk_tree_model_get (model, iter, SECTION_DESCRIPTION_COLUMN, &description, -1);
 
1730
  if (description == NULL)
 
1731
    is_separator = TRUE;
 
1732
  g_free (description);
 
1733
 
 
1734
  return is_separator;
 
1735
}
 
1736
 
 
1737
static void
 
1738
setup_dialog (CcPanel *panel, GtkBuilder *builder)
 
1739
{
 
1740
  GConfClient *client;
 
1741
  GtkCellRenderer *renderer;
 
1742
  GtkTreeViewColumn *column;
 
1743
  GtkWidget *widget;
 
1744
  GtkTreeView *treeview;
 
1745
  GtkTreeSelection *selection;
 
1746
  GSList *allowed_keys;
 
1747
  CcShell *shell;
 
1748
  GtkListStore *model;
 
1749
  GtkTreeModelSort *sort_model;
 
1750
  GtkStyleContext *context;
 
1751
 
 
1752
  gtk_widget_set_size_request (GTK_WIDGET (panel), -1, 400);
 
1753
 
 
1754
  /* Setup the section treeview */
 
1755
  treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "section_treeview"));
 
1756
  gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (treeview),
 
1757
                                        sections_separator_func,
 
1758
                                        panel,
 
1759
                                        NULL);
 
1760
 
 
1761
  renderer = gtk_cell_renderer_text_new ();
 
1762
  column = gtk_tree_view_column_new_with_attributes (_("Section"),
 
1763
                                                     renderer,
 
1764
                                                     "text", SECTION_DESCRIPTION_COLUMN,
 
1765
                                                     NULL);
 
1766
  g_object_set (renderer,
 
1767
                "width-chars", 20,
 
1768
                "ellipsize", PANGO_ELLIPSIZE_END,
 
1769
                NULL);
 
1770
 
 
1771
  gtk_tree_view_append_column (treeview, column);
 
1772
 
 
1773
  model = gtk_list_store_new (SECTION_N_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
 
1774
  sort_model = GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (model)));
 
1775
  gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (sort_model));
 
1776
  g_object_unref (model);
 
1777
 
 
1778
  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model),
 
1779
                                   SECTION_DESCRIPTION_COLUMN,
 
1780
                                   section_sort_item,
 
1781
                                   panel,
 
1782
                                   NULL);
 
1783
 
 
1784
  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model),
 
1785
                                        SECTION_DESCRIPTION_COLUMN,
 
1786
                                        GTK_SORT_ASCENDING);
 
1787
  g_object_unref (sort_model);
 
1788
 
 
1789
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
 
1790
 
 
1791
  gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
 
1792
 
 
1793
  g_signal_connect (selection, "changed",
 
1794
                    G_CALLBACK (section_selection_changed), builder);
 
1795
  section_selection_changed (selection, builder);
 
1796
 
 
1797
  /* Setup the shortcut treeview */
 
1798
  treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
 
1799
                                                    "shortcut_treeview"));
 
1800
 
 
1801
  client = gconf_client_get_default ();
 
1802
 
 
1803
  g_signal_connect (treeview, "button_press_event",
 
1804
                    G_CALLBACK (start_editing_cb), builder);
 
1805
  g_signal_connect (treeview, "row-activated",
 
1806
                    G_CALLBACK (start_editing_kb_cb), NULL);
 
1807
 
 
1808
  renderer = gtk_cell_renderer_text_new ();
 
1809
  g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
 
1810
 
 
1811
  g_signal_connect (renderer, "edited",
 
1812
                    G_CALLBACK (description_edited_callback),
 
1813
                    treeview);
 
1814
 
 
1815
  column = gtk_tree_view_column_new_with_attributes (_("Action"),
 
1816
                                                     renderer,
 
1817
                                                     "text", DETAIL_DESCRIPTION_COLUMN,
 
1818
                                                     NULL);
 
1819
  gtk_tree_view_column_set_cell_data_func (column, renderer, description_set_func, NULL, NULL);
 
1820
  gtk_tree_view_column_set_resizable (column, FALSE);
 
1821
  gtk_tree_view_column_set_expand (column, TRUE);
 
1822
 
 
1823
  gtk_tree_view_append_column (treeview, column);
 
1824
  gtk_tree_view_column_set_sort_column_id (column, DETAIL_DESCRIPTION_COLUMN);
 
1825
 
 
1826
  renderer = (GtkCellRenderer *) g_object_new (EGG_TYPE_CELL_RENDERER_KEYS,
 
1827
                                               "accel_mode", EGG_CELL_RENDERER_KEYS_MODE_X,
 
1828
                                               NULL);
 
1829
 
 
1830
  g_signal_connect (renderer, "accel_edited",
 
1831
                    G_CALLBACK (accel_edited_callback),
 
1832
                    treeview);
 
1833
 
 
1834
  g_signal_connect (renderer, "accel_cleared",
 
1835
                    G_CALLBACK (accel_cleared_callback),
 
1836
                    treeview);
 
1837
 
 
1838
  column = gtk_tree_view_column_new_with_attributes (_("Shortcut"), renderer, NULL);
 
1839
  gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
 
1840
  gtk_tree_view_column_set_resizable (column, FALSE);
 
1841
  gtk_tree_view_column_set_expand (column, FALSE);
 
1842
 
 
1843
  gtk_tree_view_append_column (treeview, column);
 
1844
  gtk_tree_view_column_set_sort_column_id (column, DETAIL_KEYENTRY_COLUMN);
 
1845
 
 
1846
  gconf_client_add_dir (client, GCONF_BINDING_DIR, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
 
1847
  gconf_client_add_dir (client, "/apps/metacity/general", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
 
1848
  workspace_num_notify_id = gconf_client_notify_add (client,
 
1849
                                                     "/apps/metacity/general/num_workspaces",
 
1850
                                                     (GConfClientNotifyFunc) key_entry_controlling_key_changed,
 
1851
                                                     builder, NULL, NULL);
 
1852
 
 
1853
  model = gtk_list_store_new (DETAIL_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);
 
1854
  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model),
 
1855
                                   DETAIL_KEYENTRY_COLUMN,
 
1856
                                   keyentry_sort_func,
 
1857
                                   NULL, NULL);
 
1858
  gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (model));
 
1859
  g_object_unref (model);
 
1860
 
 
1861
  widget = GTK_WIDGET (gtk_builder_get_object (builder, "actions_swindow"));
 
1862
  context = gtk_widget_get_style_context (widget);
 
1863
  gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
 
1864
  widget = GTK_WIDGET (gtk_builder_get_object (builder, "shortcut-toolbar"));
 
1865
  context = gtk_widget_get_style_context (widget);
 
1866
  gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
 
1867
 
 
1868
  /* set up the dialog */
 
1869
  shell = cc_panel_get_shell (CC_PANEL (panel));
 
1870
  widget = cc_shell_get_toplevel (shell);
 
1871
 
 
1872
  maybe_block_accels_id = g_signal_connect (widget, "key_press_event",
 
1873
                                            G_CALLBACK (maybe_block_accels), NULL);
 
1874
 
 
1875
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
 
1876
  g_signal_connect (selection, "changed",
 
1877
                    G_CALLBACK (shortcut_selection_changed),
 
1878
                    WID (builder, "remove-toolbutton"));
 
1879
 
 
1880
  /* FIXME those use GSettings now */
 
1881
  allowed_keys = gconf_client_get_list (client,
 
1882
                                        GCONF_BINDING_DIR "/allowed_keys",
 
1883
                                        GCONF_VALUE_STRING,
 
1884
                                        NULL);
 
1885
  if (allowed_keys != NULL)
 
1886
    {
 
1887
      g_slist_foreach (allowed_keys, (GFunc)g_free, NULL);
 
1888
      g_slist_free (allowed_keys);
 
1889
      gtk_widget_set_sensitive (WID (builder, "add-toolbutton"),
 
1890
                                FALSE);
 
1891
    }
 
1892
 
 
1893
  g_object_unref (client);
 
1894
 
 
1895
  /* setup the custom shortcut dialog */
 
1896
  custom_shortcut_dialog = WID (builder,
 
1897
                                "custom-shortcut-dialog");
 
1898
  custom_shortcut_name_entry = WID (builder,
 
1899
                                    "custom-shortcut-name-entry");
 
1900
  custom_shortcut_command_entry = WID (builder,
 
1901
                                       "custom-shortcut-command-entry");
 
1902
  g_signal_connect (WID (builder, "add-toolbutton"),
 
1903
                    "clicked", G_CALLBACK (add_button_clicked), builder);
 
1904
  g_signal_connect (WID (builder, "remove-toolbutton"),
 
1905
                    "clicked", G_CALLBACK (remove_button_clicked), builder);
 
1906
 
 
1907
  gtk_dialog_set_default_response (GTK_DIALOG (custom_shortcut_dialog),
 
1908
                                   GTK_RESPONSE_OK);
 
1909
 
 
1910
  gtk_window_set_transient_for (GTK_WINDOW (custom_shortcut_dialog),
 
1911
                                GTK_WINDOW (widget));
 
1912
 
 
1913
  gtk_window_set_resizable (GTK_WINDOW (custom_shortcut_dialog), FALSE);
 
1914
}
 
1915
 
 
1916
static void
 
1917
on_window_manager_change (const char *wm_name, GtkBuilder *builder)
 
1918
{
 
1919
  reload_sections (builder);
 
1920
}
 
1921
 
 
1922
void
 
1923
keyboard_shortcuts_init (CcPanel *panel, GtkBuilder *builder)
 
1924
{
 
1925
  wm_common_register_window_manager_change ((GFunc) on_window_manager_change,
 
1926
                                            builder);
 
1927
  setup_dialog (panel, builder);
 
1928
  reload_sections (builder);
 
1929
}
 
1930
 
 
1931
void
 
1932
keyboard_shortcuts_dispose (CcPanel *panel)
 
1933
{
 
1934
  if (maybe_block_accels_id != 0)
 
1935
    {
 
1936
      CcShell *shell;
 
1937
      GtkWidget *toplevel;
 
1938
 
 
1939
      shell = cc_panel_get_shell (CC_PANEL (panel));
 
1940
      toplevel = cc_shell_get_toplevel (shell);
 
1941
 
 
1942
      g_signal_handler_disconnect (toplevel, maybe_block_accels_id);
 
1943
      maybe_block_accels_id = 0;
 
1944
 
 
1945
      if (kb_system_sections != NULL)
 
1946
        {
 
1947
          g_hash_table_destroy (kb_system_sections);
 
1948
          kb_system_sections = NULL;
 
1949
        }
 
1950
      if (kb_apps_sections != NULL)
 
1951
        {
 
1952
          g_hash_table_destroy (kb_apps_sections);
 
1953
          kb_apps_sections = NULL;
 
1954
        }
 
1955
      if (kb_user_sections != NULL)
 
1956
        {
 
1957
          g_hash_table_destroy (kb_user_sections);
 
1958
          kb_user_sections = NULL;
 
1959
        }
 
1960
      if (workspace_num_notify_id != 0)
 
1961
        {
 
1962
          GConfClient *client;
 
1963
          client = gconf_client_get_default ();
 
1964
          gconf_client_notify_remove (client, workspace_num_notify_id);
 
1965
          workspace_num_notify_id = 0;
 
1966
          g_object_unref (client);
 
1967
        }
 
1968
    }
 
1969
}