[gnome-control-center/wip/gbsneto/new-keyboard-panel: 13/27] keyboard: make it a template class



commit d940d7bb5d8f3a7aceb3d4e91d16cbf0bf366c41
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Fri Jul 22 15:26:10 2016 -0300

    keyboard: make it a template class
    
    To allow a much easier porting to the new layout, the keyboard
    panel is now a template class. That has various implications on
    the code organization:
    
     - The keyboard-shortcuts.c was responsible for filling the shortcuts.
       Because it relied on the GtkBuilder of the panel, most of its code
       was moved to the CcKeyboardPanel class.
     - The unused code from the keyboard panel class had to be removed in
       order to make it work again.
     - All the hash tables and widgets are now part of the CcKeyboardPanel
       structure.
     - The interface elements have a single entry point.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=769063

 panels/keyboard/cc-keyboard-item.h      |    1 +
 panels/keyboard/cc-keyboard-panel.c     | 1852 +++++++++++++++++++++++++++++--
 panels/keyboard/cc-keyboard-panel.h     |    1 +
 panels/keyboard/gnome-keyboard-panel.ui |  280 +++---
 panels/keyboard/keyboard-shortcuts.c    | 1825 +-----------------------------
 panels/keyboard/keyboard-shortcuts.h    |   14 +-
 6 files changed, 1965 insertions(+), 2008 deletions(-)
---
diff --git a/panels/keyboard/cc-keyboard-item.h b/panels/keyboard/cc-keyboard-item.h
index 5d18187..8792ba0 100644
--- a/panels/keyboard/cc-keyboard-item.h
+++ b/panels/keyboard/cc-keyboard-item.h
@@ -21,6 +21,7 @@
 #define __CC_KEYBOARD_ITEM_H
 
 #include <glib-object.h>
+#include <gtk/gtk.h>
 
 G_BEGIN_DECLS
 
diff --git a/panels/keyboard/cc-keyboard-panel.c b/panels/keyboard/cc-keyboard-panel.c
index 38fe4ba..992cfc8 100644
--- a/panels/keyboard/cc-keyboard-panel.c
+++ b/panels/keyboard/cc-keyboard-panel.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2010 Intel, Inc
+ * Copyright (C) 2016 Endless, Inc
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -15,21 +16,61 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  *
  * Author: Thomas Wood <thomas wood intel com>
+ *         Georges Basile Stavracas Neto <gbsneto gnome org>
  *
  */
 
+#include <glib/gi18n.h>
+
+#include "cc-keyboard-item.h"
+#include "cc-keyboard-option.h"
 #include "cc-keyboard-panel.h"
 #include "cc-keyboard-resources.h"
 
 #include "keyboard-shortcuts.h"
+#include "wm-common.h"
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#define BINDINGS_SCHEMA       "org.gnome.settings-daemon.plugins.media-keys"
+#define CUSTOM_SHORTCUTS_ID   "custom"
 
 struct _CcKeyboardPanel
 {
   CcPanel             parent;
 
-  GtkBuilder         *builder;
+  /* Treeviews */
+  GtkWidget          *section_treeview;
+  GtkWidget          *shortcut_treeview;
+
+  /* Toolbar widgets */
+  GtkWidget          *add_toolbutton;
+  GtkWidget          *remove_toolbutton;
+  GtkWidget          *shortcut_toolbar;
+
+  /* Custom shortcut dialog */
+  GtkWidget          *custom_shortcut_command_entry;
+  GtkWidget          *custom_shortcut_dialog;
+  GtkWidget          *custom_shortcut_name_entry;
+  GtkWidget          *custom_shortcut_ok_button;
+
+  GHashTable         *kb_system_sections;
+  GHashTable         *kb_apps_sections;
+  GHashTable         *kb_user_sections;
+
+  GSettings          *binding_settings;
+
+  GRegex             *pictures_regex;
+
+  gpointer            wm_changed_id;
+  gchar              *section_to_set;
 };
 
+static gboolean      cc_keyboard_panel_set_section               (CcKeyboardPanel    *self,
+                                                                  const char         *section);
+
 CC_PANEL_REGISTER (CcKeyboardPanel, cc_keyboard_panel)
 
 enum {
@@ -37,101 +78,1702 @@ enum {
   PROP_PARAMETERS
 };
 
-enum {
-  TYPING_PAGE,
-  SHORTCUTS_PAGE
-};
+static GHashTable*
+get_hash_for_group (CcKeyboardPanel  *self,
+                    BindingGroupType  group)
+{
+  GHashTable *hash;
+
+  switch (group)
+    {
+    case BINDING_GROUP_SYSTEM:
+      hash = self->kb_system_sections;
+      break;
+    case BINDING_GROUP_APPS:
+      hash = self->kb_apps_sections;
+      break;
+    case BINDING_GROUP_USER:
+      hash = self->kb_user_sections;
+      break;
+    default:
+      hash = NULL;
+    }
+
+  return hash;
+}
+
+static gboolean
+have_key_for_group (CcKeyboardPanel *self,
+                    int              group,
+                    const gchar     *name)
+{
+  GHashTableIter iter;
+  GPtrArray *keys;
+  gint i;
+
+  g_hash_table_iter_init (&iter, get_hash_for_group (self, group));
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &keys))
+    {
+      for (i = 0; i < keys->len; i++)
+        {
+          CcKeyboardItem *item = g_ptr_array_index (keys, i);
+
+          if (item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS &&
+              g_strcmp0 (name, item->key) == 0)
+            {
+              return TRUE;
+            }
+
+          return FALSE;
+        }
+    }
+
+  return FALSE;
+}
 
 static void
-cc_keyboard_panel_set_page (CcKeyboardPanel *panel,
-                            const gchar     *page,
-                            const gchar     *section)
+free_key_array (GPtrArray *keys)
 {
-  GtkWidget *notebook;
-  gint page_num;
+  if (keys != NULL)
+    {
+      gint i;
+
+      for (i = 0; i < keys->len; i++)
+        {
+          CcKeyboardItem *item;
 
-  if (g_strcmp0 (page, "typing") == 0)
-    page_num = TYPING_PAGE;
-  else if (g_strcmp0 (page, "shortcuts") == 0)
-    page_num = SHORTCUTS_PAGE;
-  else {
-    g_warning ("Could not switch to non-existent page '%s'", page);
+          item = g_ptr_array_index (keys, i);
+
+          g_object_unref (item);
+        }
+
+      g_ptr_array_free (keys, TRUE);
+    }
+}
+
+static char*
+binding_name (guint                   keyval,
+              guint                   keycode,
+              GdkModifierType         mask,
+              gboolean                translate)
+{
+  if (keyval != 0 || keycode != 0)
+    {
+      return translate ? gtk_accelerator_get_label_with_keycode (NULL, keyval, keycode, mask) :
+                         gtk_accelerator_name_with_keycode (NULL, keyval, keycode, mask);
+    }
+  else
+    {
+      return g_strdup (translate ? _("Disabled") : NULL);
+    }
+}
+
+
+static gboolean
+keybinding_key_changed_foreach (GtkTreeModel   *model,
+                                GtkTreePath    *path,
+                                GtkTreeIter    *iter,
+                                CcKeyboardItem *item)
+{
+  CcKeyboardItem *tmp_item;
+
+  gtk_tree_model_get (item->model,
+                      iter,
+                      DETAIL_KEYENTRY_COLUMN, &tmp_item,
+                      -1);
+
+  if (item == tmp_item)
+    {
+      gtk_tree_model_row_changed (item->model, path, iter);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+item_changed (CcKeyboardItem *item,
+              GParamSpec     *pspec,
+              gpointer        user_data)
+{
+  /* update the model */
+  gtk_tree_model_foreach (item->model,
+                          (GtkTreeModelForeachFunc) keybinding_key_changed_foreach,
+                          item);
+}
+
+static void
+append_section (CcKeyboardPanel    *self,
+                const gchar        *title,
+                const gchar        *id,
+                BindingGroupType    group,
+                const KeyListEntry *keys_list)
+{
+  GtkTreeModel *sort_model;
+  GtkTreeModel *model, *shortcut_model;
+  GtkTreeIter iter;
+  GHashTable *reverse_items;
+  GHashTable *hash;
+  GPtrArray *keys_array;
+  gboolean is_new;
+  gint i;
+
+  hash = get_hash_for_group (self, group);
+
+  if (!hash)
     return;
-  }
 
-  notebook = GTK_WIDGET (gtk_builder_get_object (panel->builder, "keyboard_notebook"));
-  gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), page_num);
+  sort_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->section_treeview));
+  model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model));
+
+  shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->shortcut_treeview));
+
+  /* Add all CcKeyboardItems for this section */
+  is_new = FALSE;
+  keys_array = g_hash_table_lookup (hash, id);
+  if (keys_array == NULL)
+    {
+      keys_array = g_ptr_array_new ();
+      is_new = TRUE;
+    }
+
+  reverse_items = g_hash_table_new (g_str_hash, g_str_equal);
 
-  if (page_num == SHORTCUTS_PAGE &&
-      section != NULL) {
-    keyboard_shortcuts_set_section (CC_PANEL (panel), section);
-  }
+  for (i = 0; keys_list != NULL && keys_list[i].name != NULL; i++)
+    {
+      CcKeyboardItem *item;
+      gboolean ret;
+
+      if (have_key_for_group (self, group, keys_list[i].name))
+        continue;
+
+      item = cc_keyboard_item_new (keys_list[i].type);
+      switch (keys_list[i].type)
+        {
+        case CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH:
+          ret = cc_keyboard_item_load_from_gsettings_path (item, keys_list[i].name, FALSE);
+          break;
+
+        case CC_KEYBOARD_ITEM_TYPE_GSETTINGS:
+          ret = cc_keyboard_item_load_from_gsettings (item,
+                                                      keys_list[i].description,
+                                                      keys_list[i].schema,
+                                                      keys_list[i].name);
+          if (ret && keys_list[i].reverse_entry != NULL)
+            {
+              CcKeyboardItem *reverse_item;
+              reverse_item = g_hash_table_lookup (reverse_items,
+                                                  keys_list[i].reverse_entry);
+              if (reverse_item != NULL)
+                {
+                  cc_keyboard_item_add_reverse_item (item,
+                                                     reverse_item,
+                                                     keys_list[i].is_reversed);
+                }
+              else
+                {
+                  g_hash_table_insert (reverse_items,
+                                       keys_list[i].name,
+                                       item);
+                }
+            }
+          break;
+
+        default:
+          g_assert_not_reached ();
+        }
+
+      if (ret == FALSE)
+        {
+          /* We don't actually want to popup a dialog - just skip this one */
+          g_object_unref (item);
+          continue;
+        }
+
+      cc_keyboard_item_set_hidden (item, keys_list[i].hidden);
+      item->model = shortcut_model;
+
+      g_signal_connect (G_OBJECT (item),
+                        "notify",
+                        G_CALLBACK (item_changed),
+                        NULL);
+
+      g_ptr_array_add (keys_array, item);
+    }
+
+  g_hash_table_destroy (reverse_items);
+
+  /* Add the keys to the hash table */
+  if (is_new)
+    {
+      g_hash_table_insert (hash, g_strdup (id), keys_array);
+
+      /* Append the section to the left tree view */
+      gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+      gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+                          SECTION_DESCRIPTION_COLUMN, title,
+                          SECTION_ID_COLUMN, id,
+                          SECTION_GROUP_COLUMN, group,
+                          -1);
+    }
 }
 
 static void
-cc_keyboard_panel_set_property (GObject      *object,
-                               guint         property_id,
-                               const GValue *value,
-                               GParamSpec   *pspec)
+append_sections_from_file (CcKeyboardPanel  *self,
+                           const gchar      *path,
+                           const char       *datadir,
+                           gchar           **wm_keybindings)
 {
-  CcKeyboardPanel *panel = CC_KEYBOARD_PANEL (object);
+  KeyList *keylist;
+  KeyListEntry *keys;
+  KeyListEntry key = { 0, 0, 0, 0, 0, 0, 0 };
+  const char *title;
+  int group;
+  guint i;
 
-  switch (property_id)
+  keylist = parse_keylist_from_file (path);
+
+  if (keylist == NULL)
+    return;
+
+#define const_strv(s) ((const gchar* const*) s)
+
+  /* If there's no keys to add, or the settings apply to a window manager
+   * that's not the one we're running */
+  if (keylist->entries->len == 0 ||
+      (keylist->wm_name != NULL && !g_strv_contains (const_strv (wm_keybindings), keylist->wm_name)) ||
+      keylist->name == NULL)
+    {
+      g_free (keylist->name);
+      g_free (keylist->package);
+      g_free (keylist->wm_name);
+      g_array_free (keylist->entries, TRUE);
+      g_free (keylist);
+      return;
+    }
+
+#undef const_strv
+
+  /* Empty KeyListEntry to end the array */
+  key.name = NULL;
+  g_array_append_val (keylist->entries, key);
+
+  keys = (KeyListEntry *) g_array_free (keylist->entries, FALSE);
+  if (keylist->package)
+    {
+      char *localedir;
+
+      localedir = g_build_filename (datadir, "locale", NULL);
+      bindtextdomain (keylist->package, localedir);
+      g_free (localedir);
+
+      title = dgettext (keylist->package, keylist->name);
+    } else {
+      title = _(keylist->name);
+    }
+  if (keylist->group && strcmp (keylist->group, "system") == 0)
+    group = BINDING_GROUP_SYSTEM;
+  else
+    group = BINDING_GROUP_APPS;
+
+  append_section (self, title, keylist->name, group, keys);
+
+  g_free (keylist->name);
+  g_free (keylist->package);
+  g_free (keylist->wm_name);
+  g_free (keylist->schema);
+  g_free (keylist->group);
+
+  for (i = 0; keys[i].name != NULL; i++)
+    {
+      KeyListEntry *entry = &keys[i];
+      g_free (entry->schema);
+      g_free (entry->description);
+      g_free (entry->name);
+      g_free (entry->reverse_entry);
+    }
+
+  g_free (keylist);
+  g_free (keys);
+}
+
+static void
+append_sections_from_gsettings (CcKeyboardPanel *self)
+{
+  char **custom_paths;
+  GArray *entries;
+  KeyListEntry key = { 0, 0, 0, 0, 0, 0, 0 };
+  int i;
+
+  /* load custom shortcuts from GSettings */
+  entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
+
+  custom_paths = g_settings_get_strv (self->binding_settings, "custom-keybindings");
+  for (i = 0; custom_paths[i]; i++)
     {
-    case PROP_PARAMETERS: {
-      GVariant *parameters, *v;
-      const gchar *page, *section;
-
-      parameters = g_value_get_variant (value);
-      if (!parameters)
-        break;
-      page = section = NULL;
-      switch (g_variant_n_children (parameters))
-        {
-          case 2:
-            g_variant_get_child (parameters, 1, "v", &v);
-            section = g_variant_get_string (v, NULL);
-            g_variant_unref (v);
-            /* fall-through */
-          case 1:
-            g_variant_get_child (parameters, 0, "v", &v);
-            page = g_variant_get_string (v, NULL);
-            g_variant_unref (v);
-            cc_keyboard_panel_set_page (panel, page, section);
-            /* fall-through */
-          case 0:
-            break;
-          default:
-            g_warning ("Unexpected parameters found, ignore request");
+      key.name = g_strdup (custom_paths[i]);
+      if (!have_key_for_group (self, BINDING_GROUP_USER, key.name))
+        {
+          key.type = CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH;
+          g_array_append_val (entries, key);
+        }
+      else
+        g_free (key.name);
+    }
+  g_strfreev (custom_paths);
+
+  if (entries->len > 0)
+    {
+      KeyListEntry *keys;
+      int i;
+
+      /* Empty KeyListEntry to end the array */
+      key.name = NULL;
+      g_array_append_val (entries, key);
+
+      keys = (KeyListEntry *) entries->data;
+      append_section (self, _("Custom Shortcuts"), CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, keys);
+      for (i = 0; i < entries->len; ++i)
+        {
+          g_free (keys[i].name);
+        }
+    }
+  else
+    {
+      append_section (self, _("Custom Shortcuts"), CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, NULL);
+    }
+
+  g_array_free (entries, TRUE);
+}
+
+static void
+reload_sections (CcKeyboardPanel *self)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel *shortcut_model;
+  GtkTreeModel *section_model;
+  GtkTreeModel *sort_model;
+  GtkTreeView *section_treeview;
+  GtkTreeIter iter;
+  GHashTable *loaded_files;
+  GDir *dir;
+  gchar *default_wm_keybindings[] = { "Mutter", "GNOME Shell", NULL };
+  gchar **wm_keybindings;
+  const gchar * const * data_dirs;
+  guint i;
+
+  section_treeview = GTK_TREE_VIEW (self->section_treeview);
+  sort_model = gtk_tree_view_get_model (section_treeview);
+  section_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model));
+
+  shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->shortcut_treeview));
+  /* FIXME: get current selection and keep it after refreshing */
+
+  /* Clear previous models and hash tables */
+  gtk_list_store_clear (GTK_LIST_STORE (section_model));
+  gtk_list_store_clear (GTK_LIST_STORE (shortcut_model));
+
+  g_clear_pointer (&self->kb_system_sections, g_hash_table_destroy);
+  self->kb_system_sections = g_hash_table_new_full (g_str_hash,
+                                                    g_str_equal,
+                                                    g_free,
+                                                    (GDestroyNotify) free_key_array);
+
+  g_clear_pointer (&self->kb_apps_sections, g_hash_table_destroy);
+  self->kb_apps_sections = g_hash_table_new_full (g_str_hash,
+                                                  g_str_equal,
+                                                  g_free,
+                                                  (GDestroyNotify) free_key_array);
+
+  g_clear_pointer (&self->kb_user_sections, g_hash_table_destroy);
+  self->kb_user_sections = g_hash_table_new_full (g_str_hash,
+                                                  g_str_equal,
+                                                  g_free,
+                                                  (GDestroyNotify) free_key_array);
+
+  /* Load WM keybindings */
+#ifdef GDK_WINDOWING_X11
+  if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
+    wm_keybindings = wm_common_get_current_keybindings ();
+  else
+#endif
+    wm_keybindings = g_strdupv (default_wm_keybindings);
+
+  loaded_files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+  data_dirs = g_get_system_data_dirs ();
+  for (i = 0; data_dirs[i] != NULL; i++)
+    {
+      char *dir_path;
+      const gchar *name;
+
+      dir_path = g_build_filename (data_dirs[i], "gnome-control-center", "keybindings", NULL);
+
+      dir = g_dir_open (dir_path, 0, NULL);
+      if (!dir)
+        {
+          g_free (dir_path);
+          continue;
+        }
+
+      for (name = g_dir_read_name (dir) ; name ; name = g_dir_read_name (dir))
+        {
+          gchar *path;
+
+          if (g_str_has_suffix (name, ".xml") == FALSE)
+            continue;
+
+          if (g_hash_table_lookup (loaded_files, name) != NULL)
+            {
+              g_debug ("Not loading %s, it was already loaded from another directory", name);
+              continue;
+            }
+
+          g_hash_table_insert (loaded_files, g_strdup (name), GINT_TO_POINTER (1));
+          path = g_build_filename (dir_path, name, NULL);
+          append_sections_from_file (self, path, data_dirs[i], wm_keybindings);
+          g_free (path);
+        }
+      g_free (dir_path);
+      g_dir_close (dir);
+    }
+
+  g_hash_table_destroy (loaded_files);
+  g_strfreev (wm_keybindings);
+
+  /* Add a separator */
+  gtk_list_store_append (GTK_LIST_STORE (section_model), &iter);
+  gtk_list_store_set (GTK_LIST_STORE (section_model), &iter,
+                      SECTION_DESCRIPTION_COLUMN, NULL,
+                      SECTION_GROUP_COLUMN, BINDING_GROUP_SEPARATOR,
+                      -1);
+
+  /* Load custom keybindings */
+  append_sections_from_gsettings (self);
+
+  /* Select the first item, or the requested section, if any */
+  if (self->section_to_set != NULL)
+    {
+      if (cc_keyboard_panel_set_section (self, self->section_to_set))
+        {
+          g_clear_pointer (&self->section_to_set, g_free);
+          return;
         }
-      break;
     }
+  g_assert (gtk_tree_model_get_iter_first (sort_model, &iter));
+  selection = gtk_tree_view_get_selection (section_treeview);
+  gtk_tree_selection_select_iter (selection, &iter);
+}
+
+static gboolean
+sections_separator_func (GtkTreeModel *model,
+                         GtkTreeIter  *iter,
+                         gpointer      data)
+{
+  BindingGroupType type;
+
+  gtk_tree_model_get (model, iter, SECTION_GROUP_COLUMN, &type, -1);
+
+  return type == BINDING_GROUP_SEPARATOR;
+}
+
+static int
+section_sort_item  (GtkTreeModel *model,
+                    GtkTreeIter  *a,
+                    GtkTreeIter  *b,
+                    gpointer      data)
+{
+  char *a_desc;
+  int   a_group;
+  char *b_desc;
+  int   b_group;
+  int   ret;
+
+  gtk_tree_model_get (model, a,
+                      SECTION_DESCRIPTION_COLUMN, &a_desc,
+                      SECTION_GROUP_COLUMN, &a_group,
+                      -1);
+  gtk_tree_model_get (model, b,
+                      SECTION_DESCRIPTION_COLUMN, &b_desc,
+                      SECTION_GROUP_COLUMN, &b_group,
+                      -1);
+
+  if (a_group == b_group && a_desc && b_desc)
+    ret = g_utf8_collate (a_desc, b_desc);
+  else
+    ret = a_group - b_group;
+
+  g_free (a_desc);
+  g_free (b_desc);
+
+  return ret;
+}
+
+static void
+section_selection_changed (GtkTreeSelection *selection,
+                           CcKeyboardPanel  *self)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  if (gtk_tree_selection_get_selected (selection, &model, &iter))
+    {
+      BindingGroupType group;
+      GtkTreeModel *shortcut_model;
+      GPtrArray *keys;
+      gchar *id;
+      gint i;
+
+      gtk_tree_model_get (model, &iter,
+                          SECTION_ID_COLUMN, &id,
+                          SECTION_GROUP_COLUMN, &group, -1);
+
+      keys = g_hash_table_lookup (get_hash_for_group (self, group), id);
+      if (keys == NULL)
+        {
+          g_warning ("Can't find section %s in sections hash table.", id);
+          g_free (id);
+          return;
+        }
+
+      gtk_widget_set_sensitive (self->remove_toolbutton, FALSE);
+
+      /* Fill the shortcut treeview with the keys for the selected section */
+      shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->shortcut_treeview));
+      gtk_list_store_clear (GTK_LIST_STORE (shortcut_model));
+
+      for (i = 0; i < keys->len; i++)
+        {
+          GtkTreeIter new_row;
+          CcKeyboardItem *item = g_ptr_array_index (keys, i);
+
+          if (!cc_keyboard_item_is_hidden (item))
+            {
+              gtk_list_store_append (GTK_LIST_STORE (shortcut_model), &new_row);
+              gtk_list_store_set (GTK_LIST_STORE (shortcut_model), &new_row,
+                                  DETAIL_DESCRIPTION_COLUMN, item->description,
+                                  DETAIL_KEYENTRY_COLUMN, item,
+                                  DETAIL_TYPE_COLUMN, SHORTCUT_TYPE_KEY_ENTRY,
+                                  -1);
+            }
+        }
+
+      if (g_str_equal (id, "Typing"))
+        fill_xkb_options_shortcuts (shortcut_model);
+
+      g_free (id);
+    }
+}
+
+static void
+description_set_func (GtkTreeViewColumn *tree_column,
+                      GtkCellRenderer   *cell,
+                      GtkTreeModel      *model,
+                      GtkTreeIter       *iter,
+                      gpointer           data)
+{
+  gchar *description;
+  CcKeyboardItem *item;
+  ShortcutType type;
+
+  gtk_tree_model_get (model, iter,
+                      DETAIL_DESCRIPTION_COLUMN, &description,
+                      DETAIL_KEYENTRY_COLUMN, &item,
+                      DETAIL_TYPE_COLUMN, &type,
+                      -1);
+
+  if (type == SHORTCUT_TYPE_XKB_OPTION)
+    {
+      g_object_set (cell, "text", description, NULL);
+    }
+  else
+    {
+      if (item != NULL)
+        g_object_set (cell,
+                      "editable", FALSE,
+                      "text", item->description != NULL ?
+                      item->description : _("<Unknown Action>"),
+                      NULL);
+      else
+        g_object_set (cell,
+                      "editable", FALSE, NULL);
+    }
+
+  g_free (description);
+}
+
+
+static void
+accel_set_func (GtkTreeViewColumn *tree_column,
+                GtkCellRenderer   *cell,
+                GtkTreeModel      *model,
+                GtkTreeIter       *iter,
+                gpointer           data)
+{
+  gpointer entry;
+  ShortcutType type;
 
+  gtk_tree_model_get (model, iter,
+                      DETAIL_KEYENTRY_COLUMN, &entry,
+                      DETAIL_TYPE_COLUMN, &type,
+                      -1);
+
+  gtk_cell_renderer_set_visible (cell, FALSE);
+
+  if (type == SHORTCUT_TYPE_XKB_OPTION &&
+      GTK_IS_CELL_RENDERER_COMBO (cell))
+    {
+      CcKeyboardOption *option = entry;
+
+      gtk_cell_renderer_set_visible (cell, TRUE);
+      g_object_set (cell,
+                    "model", cc_keyboard_option_get_store (option),
+                    "text", cc_keyboard_option_get_current_value_description (option),
+                    NULL);
+    }
+  else if (type == SHORTCUT_TYPE_KEY_ENTRY &&
+           GTK_IS_CELL_RENDERER_TEXT (cell) &&
+           !GTK_IS_CELL_RENDERER_COMBO (cell) &&
+           entry != NULL)
+    {
+      CcKeyboardItem *item = entry;
+
+      gtk_cell_renderer_set_visible (cell, TRUE);
+
+      if (item->editable)
+        g_object_set (cell,
+                      "editable", TRUE,
+                      "accel-key", item->keyval,
+                      "accel-mods", item->mask,
+                      "keycode", item->keycode,
+                      "style", PANGO_STYLE_NORMAL,
+                      NULL);
+      else
+        g_object_set (cell,
+                      "editable", FALSE,
+                      "accel-key", item->keyval,
+                      "accel-mods", item->mask,
+                      "keycode", item->keycode,
+                      "style", PANGO_STYLE_ITALIC,
+                      NULL);
+    }
+}
+
+static void
+shortcut_selection_changed (GtkTreeSelection *selection,
+                            GtkWidget        *button)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  gboolean can_remove;
+
+  can_remove = FALSE;
+
+  if (gtk_tree_selection_get_selected (selection, &model, &iter))
+    {
+      CcKeyboardItem *item;
+      ShortcutType type;
+
+      gtk_tree_model_get (model, &iter,
+                          DETAIL_KEYENTRY_COLUMN, &item,
+                          DETAIL_TYPE_COLUMN, &type,
+                          -1);
+
+      if (type == SHORTCUT_TYPE_KEY_ENTRY &&
+          item &&
+          item->command != NULL &&
+          item->editable)
+        {
+          can_remove = TRUE;
+        }
+    }
+
+  gtk_widget_set_sensitive (button, can_remove);
+}
+
+
+static gboolean
+edit_custom_shortcut (CcKeyboardPanel *self,
+                      CcKeyboardItem  *item)
+{
+  gint result;
+  gboolean ret;
+  GSettings *settings;
+
+  settings = g_settings_new_with_path (item->schema, item->gsettings_path);
+
+  g_settings_bind (settings, "name",
+                   G_OBJECT (self->custom_shortcut_name_entry), "text",
+                   G_SETTINGS_BIND_DEFAULT);
+  gtk_widget_grab_focus (self->custom_shortcut_name_entry);
+
+  g_settings_bind (settings, "command",
+                   G_OBJECT (self->custom_shortcut_command_entry), "text",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  g_settings_delay (settings);
+
+  gtk_widget_set_sensitive (self->custom_shortcut_name_entry, item->desc_editable);
+  gtk_widget_set_sensitive (self->custom_shortcut_command_entry, item->cmd_editable);
+  gtk_window_present (GTK_WINDOW (self->custom_shortcut_dialog));
+
+  result = gtk_dialog_run (GTK_DIALOG (self->custom_shortcut_dialog));
+  switch (result)
+    {
+    case GTK_RESPONSE_OK:
+      g_settings_apply (settings);
+      ret = TRUE;
+      break;
     default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      g_settings_revert (settings);
+      ret = FALSE;
+      break;
     }
+
+  g_settings_unbind (G_OBJECT (self->custom_shortcut_name_entry), "text");
+  g_settings_unbind (G_OBJECT (self->custom_shortcut_command_entry), "text");
+
+  gtk_widget_hide (self->custom_shortcut_dialog);
+
+  g_object_unref (settings);
+
+  return ret;
 }
 
-static GObject *
-cc_keyboard_panel_constructor (GType                  gtype,
-                               guint                  n_properties,
-                               GObjectConstructParam *properties)
+static gboolean
+remove_custom_shortcut (CcKeyboardPanel *self,
+                        GtkTreeModel    *model,
+                        GtkTreeIter     *iter)
+{
+  CcKeyboardItem *item;
+  GPtrArray *keys_array;
+  GVariantBuilder builder;
+  char **settings_paths;
+  int i;
+
+  gtk_tree_model_get (model, iter,
+                      DETAIL_KEYENTRY_COLUMN, &item,
+                      -1);
+
+  /* not a custom shortcut */
+  g_assert (item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH);
+
+  g_settings_delay (item->settings);
+  g_settings_reset (item->settings, "name");
+  g_settings_reset (item->settings, "command");
+  g_settings_reset (item->settings, "binding");
+  g_settings_apply (item->settings);
+  g_settings_sync ();
+
+  settings_paths = g_settings_get_strv (self->binding_settings, "custom-keybindings");
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+
+  for (i = 0; settings_paths[i]; i++)
+    if (strcmp (settings_paths[i], item->gsettings_path) != 0)
+      g_variant_builder_add (&builder, "s", settings_paths[i]);
+
+  g_settings_set_value (self->binding_settings,
+                        "custom-keybindings",
+                        g_variant_builder_end (&builder));
+
+  g_strfreev (settings_paths);
+  g_object_unref (item);
+
+  keys_array = g_hash_table_lookup (get_hash_for_group (self, BINDING_GROUP_USER), CUSTOM_SHORTCUTS_ID);
+  g_ptr_array_remove (keys_array, item);
+
+  gtk_list_store_remove (GTK_LIST_STORE (model), iter);
+
+  return TRUE;
+}
+
+static void
+add_custom_shortcut (CcKeyboardPanel *self,
+                     GtkTreeView     *tree_view,
+                     GtkTreeModel    *model)
+{
+  CcKeyboardItem *item;
+  GtkTreePath *path;
+  gchar *settings_path;
+
+  item = cc_keyboard_item_new (CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH);
+
+  settings_path = find_free_settings_path (self->binding_settings);
+  cc_keyboard_item_load_from_gsettings_path (item, settings_path, TRUE);
+  g_free (settings_path);
+
+  item->model = model;
+
+  if (edit_custom_shortcut (self, item) && item->command && item->command[0])
+    {
+      GPtrArray *keys_array;
+      GtkTreeIter iter;
+      GHashTable *hash;
+      GVariantBuilder builder;
+      char **settings_paths;
+      int i;
+
+      hash = get_hash_for_group (self, BINDING_GROUP_USER);
+      keys_array = g_hash_table_lookup (hash, CUSTOM_SHORTCUTS_ID);
+      if (keys_array == NULL)
+        {
+          keys_array = g_ptr_array_new ();
+          g_hash_table_insert (hash, g_strdup (CUSTOM_SHORTCUTS_ID), keys_array);
+        }
+
+      g_ptr_array_add (keys_array, item);
+
+      gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+      gtk_list_store_set (GTK_LIST_STORE (model), &iter, DETAIL_KEYENTRY_COLUMN, item, -1);
+
+      settings_paths = g_settings_get_strv (self->binding_settings, "custom-keybindings");
+      g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+      for (i = 0; settings_paths[i]; i++)
+        g_variant_builder_add (&builder, "s", settings_paths[i]);
+      g_variant_builder_add (&builder, "s", item->gsettings_path);
+      g_settings_set_value (self->binding_settings, "custom-keybindings",
+                            g_variant_builder_end (&builder));
+
+      /* make the new shortcut visible */
+      path = gtk_tree_model_get_path (model, &iter);
+      gtk_tree_view_expand_to_path (tree_view, path);
+      gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0, 0);
+      gtk_tree_path_free (path);
+    }
+  else
+    {
+      g_object_unref (item);
+    }
+}
+
+static void
+update_custom_shortcut (CcKeyboardPanel *self,
+                        GtkTreeModel    *model,
+                        GtkTreeIter     *iter)
+{
+  CcKeyboardItem *item;
+
+  gtk_tree_model_get (model, iter,
+                      DETAIL_KEYENTRY_COLUMN, &item,
+                      -1);
+
+  g_assert (item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH);
+
+  edit_custom_shortcut (self, item);
+
+  if (item->command == NULL || item->command[0] == '\0')
+    {
+      remove_custom_shortcut (self, model, iter);
+    }
+  else
+    {
+      gtk_list_store_set (GTK_LIST_STORE (model), iter,
+                          DETAIL_KEYENTRY_COLUMN, item, -1);
+    }
+}
+
+static gboolean
+start_editing_cb (GtkTreeView    *treeview,
+                  GdkEventButton *event,
+                  gpointer        user_data)
+{
+  CcKeyboardPanel *self;
+  GtkTreePath *path;
+  GtkTreeViewColumn *column;
+  GtkCellRenderer *cell = user_data;
+
+  if (event->window != gtk_tree_view_get_bin_window (treeview))
+    return FALSE;
+
+  self = CC_KEYBOARD_PANEL (gtk_widget_get_ancestor (GTK_WIDGET (treeview), CC_TYPE_KEYBOARD_PANEL));
+
+  if (gtk_tree_view_get_path_at_pos (treeview,
+                                     (gint) event->x,
+                                     (gint) event->y,
+                                     &path,
+                                     &column,
+                                     NULL,
+                                     NULL))
+    {
+      GtkTreeModel *model;
+      GtkTreeIter iter;
+      CcKeyboardItem *item;
+      ShortcutType type;
+
+      model = gtk_tree_view_get_model (treeview);
+      gtk_tree_model_get_iter (model, &iter, path);
+      gtk_tree_model_get (model, &iter,
+                          DETAIL_KEYENTRY_COLUMN, &item,
+                          DETAIL_TYPE_COLUMN, &type,
+                         -1);
+
+      if (type == SHORTCUT_TYPE_XKB_OPTION)
+        {
+          gtk_tree_path_free (path);
+          return FALSE;
+        }
+
+      /* if only the accel can be edited on the selected row
+       * always select the accel column */
+      if (item->desc_editable &&
+          column == gtk_tree_view_get_column (treeview, 0))
+        {
+          gtk_widget_grab_focus (GTK_WIDGET (treeview));
+          gtk_tree_view_set_cursor (treeview,
+                                    path,
+                                    column,
+                                    FALSE);
+
+          update_custom_shortcut (self, model, &iter);
+        }
+      else
+        {
+          gtk_widget_grab_focus (GTK_WIDGET (treeview));
+          gtk_tree_view_set_cursor_on_cell (treeview,
+                                            path,
+                                            gtk_tree_view_get_column (treeview, 1),
+                                            cell,
+                                            TRUE);
+        }
+
+      g_signal_stop_emission_by_name (treeview, "button_press_event");
+      gtk_tree_path_free (path);
+    }
+
+  return TRUE;
+}
+
+static void
+start_editing_kb_cb (GtkTreeView       *treeview,
+                     GtkTreePath       *path,
+                     GtkTreeViewColumn *column,
+                     gpointer           user_data)
 {
-  GObject *obj;
   CcKeyboardPanel *self;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  CcKeyboardItem *item;
+  ShortcutType type;
+  GtkCellRenderer *cell = user_data;
+
+  model = gtk_tree_view_get_model (treeview);
+  gtk_tree_model_get_iter (model, &iter, path);
+  gtk_tree_model_get (model, &iter,
+                      DETAIL_KEYENTRY_COLUMN, &item,
+                      DETAIL_TYPE_COLUMN, &type,
+                      -1);
+
+  if (type == SHORTCUT_TYPE_XKB_OPTION)
+    return;
+
+  self = CC_KEYBOARD_PANEL (gtk_widget_get_ancestor (GTK_WIDGET (treeview), CC_TYPE_KEYBOARD_PANEL));
+
+
+  /* if only the accel can be edited on the selected row
+   * always select the accel column */
+  if (item->desc_editable &&
+      column == gtk_tree_view_get_column (treeview, 0))
+    {
+      gtk_widget_grab_focus (GTK_WIDGET (treeview));
+      gtk_tree_view_set_cursor (treeview,
+                                path,
+                                column,
+                                FALSE);
+      update_custom_shortcut (self, model, &iter);
+    }
+  else
+    {
+       gtk_widget_grab_focus (GTK_WIDGET (treeview));
+       gtk_tree_view_set_cursor_on_cell (treeview,
+                                         path,
+                                         gtk_tree_view_get_column (treeview, 1),
+                                         cell,
+                                         TRUE);
+    }
+}
+
+static gboolean
+compare_keys_for_uniqueness (CcKeyboardItem   *element,
+                             CcUniquenessData *data)
+{
+  CcKeyboardItem *orig_item;
+
+  orig_item = data->orig_item;
+
+  /* no conflict for : blanks, different modifiers, or ourselves */
+  if (element == NULL ||
+      data->new_mask != element->mask ||
+      cc_keyboard_item_equal (orig_item, element))
+    {
+      return FALSE;
+    }
+
+  if (data->new_keyval != 0)
+    {
+      if (data->new_keyval != element->keyval)
+          return FALSE;
+    }
+  else if (element->keyval != 0 || data->new_keycode != element->keycode)
+    {
+      return FALSE;
+    }
+
+  data->conflict_item = element;
+
+  return TRUE;
+
+}
+
+static gboolean
+cb_check_for_uniqueness (gpointer          key,
+                         GPtrArray        *keys_array,
+                         CcUniquenessData *data)
+{
+  guint i;
+
+  for (i = 0; i < keys_array->len; i++)
+    {
+      CcKeyboardItem *item;
+
+      item = keys_array->pdata[i];
+      if (compare_keys_for_uniqueness (item, data))
+        return TRUE;
+    }
+  return FALSE;
+}
+
+
+static CcKeyboardItem *
+search_for_conflict_item (CcKeyboardPanel *self,
+                          CcKeyboardItem  *item,
+                          guint            keyval,
+                          GdkModifierType  mask,
+                          guint            keycode)
+{
+  CcUniquenessData data;
+
+  data.orig_item = item;
+  data.new_keyval = keyval;
+  data.new_mask = mask;
+  data.new_keycode = keycode;
+  data.conflict_item = NULL;
+
+  if (keyval != 0 || keycode != 0) /* any number of shortcuts can be disabled */
+    {
+      BindingGroupType i;
+
+      for (i = BINDING_GROUP_SYSTEM; i <= BINDING_GROUP_USER && data.conflict_item == NULL; i++)
+        {
+          GHashTable *table;
+
+          table = get_hash_for_group (self, i);
+          if (!table)
+            continue;
+          g_hash_table_find (table, (GHRFunc) cb_check_for_uniqueness, &data);
+        }
+    }
+
+  return data.conflict_item;
+}
+
+static GtkResponseType
+show_invalid_binding_dialog (CcKeyboardPanel *self,
+                             guint            keyval,
+                             GdkModifierType  mask,
+                             guint            keycode)
+{
+  GtkWidget *dialog;
+  char *name;
+
+  name = binding_name (keyval, keycode, mask, TRUE);
+  dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
+                                   GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+                                   GTK_MESSAGE_WARNING,
+                                   GTK_BUTTONS_CANCEL,
+                                   _("The shortcut “%s” cannot be used because it will become impossible to 
type using this key.\n"
+                                   "Please try with a key such as Control, Alt or Shift at the same time."),
+                                   name);
+
+  gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+
+  g_free (name);
+
+  return GTK_RESPONSE_NONE;
+}
+
+static GtkResponseType
+show_conflict_item_dialog (CcKeyboardPanel *self,
+                           CcKeyboardItem  *item,
+                           CcKeyboardItem  *conflict_item,
+                           guint            keyval,
+                           GdkModifierType  mask,
+                           guint            keycode)
+{
+  GtkWidget *dialog;
+  char *name;
+  int response;
+
+  name = binding_name (keyval, keycode, mask, TRUE);
+  dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
+                                   GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+                                   GTK_MESSAGE_WARNING,
+                                   GTK_BUTTONS_CANCEL,
+                                   _("The shortcut “%s” is already used for\n“%s”"),
+                                   name,
+                                   conflict_item->description);
+
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                            _("If you reassign the shortcut to “%s”, the “%s” shortcut "
+                                              "will be disabled."),
+                                            item->description,
+                                            conflict_item->description);
+
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+  gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Reassign"), GTK_RESPONSE_ACCEPT);
+
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+  gtk_widget_destroy (dialog);
+
+  g_free (name);
+
+  return response;
+}
+
+
+static GtkResponseType
+show_reverse_item_dialog (CcKeyboardPanel *self,
+                          CcKeyboardItem  *item,
+                          CcKeyboardItem  *reverse_item,
+                          CcKeyboardItem  *reverse_conflict_item,
+                          guint            keyval,
+                          GdkModifierType  mask,
+                          guint            keycode)
+{
+  GtkWidget *dialog;
+  char *name;
+  int response;
+
+  name = binding_name (keyval, keycode, mask, TRUE);
+
+  /* translators:
+   * This is the text you get in a dialogue when an action has an associated
+   * "reverse" action, for example Alt+Tab going in the opposite direction to
+   * Alt+Shift+Tab.
+   *
+   * An example text would be:
+   * The "Switch to next input source" shortcut has an associated "Switch to
+   * previous input source" shortcut. Do you want to automatically set it to
+   * "Shift+Ctrl+Alt+Space"? */
+  dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
+                                   GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+                                   GTK_MESSAGE_WARNING,
+                                   GTK_BUTTONS_CANCEL,
+                                   _("The “%s” shortcut has an associated “%s” shortcut. "
+                                     "Do you want to automatically set it to “%s”?"),
+                                   item->description,
+                                   reverse_item->description,
+                                   name);
+
+  if (reverse_conflict_item != NULL)
+    {
+      /* translators:
+       * This is the text you get in a dialogue when you try to use a shortcut
+       * that was already associated with another action, for example:
+       * "Alt+F4" is currently associated with "Close Window", ... */
+      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                                _("“%s” is currently associated with “%s”, this shortcut 
will be"
+                                                  " disabled if you move forward."),
+                                                name,
+                                                reverse_conflict_item->description);
+    }
+
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+  gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Assign"), GTK_RESPONSE_ACCEPT);
+
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+
+  g_free (name);
+
+  return response;
+}
+
+static void
+handle_reverse_item (CcKeyboardItem  *item,
+                     CcKeyboardItem  *reverse_item,
+                     guint            keyval,
+                     GdkModifierType  mask,
+                     guint            keycode,
+                     CcKeyboardPanel *self)
+{
+  GtkResponseType response;
+  GdkModifierType reverse_mask;
+
+  reverse_mask = mask ^ GDK_SHIFT_MASK;
+
+  if (!is_valid_binding (keyval, reverse_mask, keycode))
+    return;
+
+  if (reverse_item->keyval != keyval ||
+      reverse_item->keycode != keycode ||
+      reverse_item->mask != reverse_mask)
+    {
+      CcKeyboardItem *reverse_conflict_item;
+      char *binding_str;
+
+      reverse_conflict_item = search_for_conflict_item (self,
+                                                        reverse_item,
+                                                        keyval,
+                                                        reverse_mask,
+                                                        keycode);
+
+      response = show_reverse_item_dialog (self,
+                                           item,
+                                           reverse_item,
+                                           reverse_conflict_item,
+                                           keyval, reverse_mask,
+                                           keycode);
+      if (response == GTK_RESPONSE_ACCEPT)
+        {
+          binding_str = binding_name (keyval, keycode, reverse_mask, FALSE);
+
+          g_object_set (G_OBJECT (reverse_item), "binding", binding_str, NULL);
+          g_free (binding_str);
+
+          if (reverse_conflict_item != NULL)
+            g_object_set (G_OBJECT (reverse_conflict_item), "binding", NULL, NULL);
+        }
+      else
+        {
+          /* The existing reverse binding may be conflicting with the binding
+           * we are setting. Other conflicts have already been handled in
+           * accel_edited_callback()
+           */
+          CcKeyboardItem *conflict_item;
+
+          conflict_item = search_for_conflict_item (self, item, keyval, mask, keycode);
+
+          if (conflict_item != NULL)
+            {
+              g_warn_if_fail (conflict_item == reverse_item);
+              g_object_set (G_OBJECT (conflict_item), "binding", NULL, NULL);
+            }
+        }
+    }
+}
+
+static void
+accel_edited_callback (GtkCellRendererText   *cell,
+                       const char            *path_string,
+                       guint                  keyval,
+                       GdkModifierType        mask,
+                       guint                  keycode,
+                       CcKeyboardPanel       *self)
+{
+  GtkTreeModel *model;
+  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
+  GtkTreeIter iter;
+  CcKeyboardItem *item;
+  CcKeyboardItem *conflict_item;
+  CcKeyboardItem *reverse_item;
+  char *str;
+
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->shortcut_treeview));
+  gtk_tree_model_get_iter (model, &iter, path);
+  gtk_tree_path_free (path);
+  gtk_tree_model_get (model, &iter,
+                      DETAIL_KEYENTRY_COLUMN, &item,
+                      -1);
+
+  /* sanity check */
+  if (item == NULL)
+    return;
+
+  /* CapsLock isn't supported as a keybinding modifier, so keep it from confusing us */
+  mask &= ~GDK_LOCK_MASK;
+
+  conflict_item = search_for_conflict_item (self, item, keyval, mask, keycode);
+
+  /* Check for unmodified keys */
+  if (!is_valid_binding (keyval, mask, keycode))
+    {
+      show_invalid_binding_dialog (self, keyval, mask, keycode);
+
+      /* set it back to its previous value. */
+      g_object_set (G_OBJECT (cell),
+                    "accel-key", item->keyval,
+                    "keycode", item->keycode,
+                    "accel-mods", item->mask,
+                    NULL);
+      return;
+    }
+
+  reverse_item = cc_keyboard_item_get_reverse_item (item);
+
+  /* flag to see if the new accelerator was in use by something */
+  if ((conflict_item != NULL) && (conflict_item != reverse_item))
+    {
+      GtkResponseType response;
+
+      response = show_conflict_item_dialog (self,
+                                            item,
+                                            conflict_item,
+                                            keyval,
+                                            mask,
+                                            keycode);
+
+      if (response == GTK_RESPONSE_ACCEPT)
+        {
+          g_object_set (G_OBJECT (conflict_item), "binding", NULL, NULL);
+
+          str = binding_name (keyval, keycode, mask, FALSE);
+          g_object_set (G_OBJECT (item), "binding", str, NULL);
+
+          g_free (str);
+          if (reverse_item == NULL)
+            return;
+        }
+      else
+        {
+          /* set it back to its previous value. */
+          g_object_set (G_OBJECT (cell),
+                        "accel-key", item->keyval,
+                        "keycode", item->keycode,
+                        "accel-mods", item->mask,
+                        NULL);
+          return;
+        }
+
+    }
+
+  str = binding_name (keyval, keycode, mask, FALSE);
+  g_object_set (G_OBJECT (item), "binding", str, NULL);
+
+  if (reverse_item != NULL)
+    handle_reverse_item (item, reverse_item, keyval, mask, keycode, self);
+
+  g_free (str);
+}
+
+static void
+accel_cleared_callback (GtkCellRendererText *cell,
+                        const char          *path_string,
+                        gpointer             data)
+{
+  GtkTreeView *view = (GtkTreeView *) data;
+  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
+  CcKeyboardItem *item;
+  GtkTreeIter iter;
+  GtkTreeModel *model;
+
+  model = gtk_tree_view_get_model (view);
+  gtk_tree_model_get_iter (model, &iter, path);
+  gtk_tree_path_free (path);
+  gtk_tree_model_get (model, &iter,
+                      DETAIL_KEYENTRY_COLUMN, &item,
+                      -1);
+
+  /* sanity check */
+  if (item == NULL)
+    return;
+
+  /* Unset the key */
+  g_object_set (G_OBJECT (item), "binding", NULL, NULL);
+}
+
+static void
+shortcut_entry_changed (GtkEntry        *entry,
+                        CcKeyboardPanel *self)
+{
+  guint16 name_length;
+  guint16 command_length;
+
+  name_length = gtk_entry_get_text_length (GTK_ENTRY (self->custom_shortcut_name_entry));
+  command_length = gtk_entry_get_text_length (GTK_ENTRY (self->custom_shortcut_command_entry));
+
+  gtk_widget_set_sensitive (self->custom_shortcut_ok_button, name_length > 0 && command_length > 0);
+}
+
+static void
+add_button_clicked (GtkWidget       *button,
+                    CcKeyboardPanel *self)
+{
+  GtkTreeView *treeview;
+  GtkTreeModel *model;
+  GtkTreeModel *section_model;
+  GtkTreeIter iter;
+  gboolean found, cont;
+
+  treeview = GTK_TREE_VIEW (self->shortcut_treeview);
+  model = gtk_tree_view_get_model (treeview);
+
+  /* Select the Custom Shortcuts section
+   * before adding the shortcut itself */
+  section_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->section_treeview));
+  cont = gtk_tree_model_get_iter_first (section_model, &iter);
+  found = FALSE;
+
+  while (cont)
+    {
+      BindingGroupType group;
+
+      gtk_tree_model_get (section_model, &iter,
+                          SECTION_GROUP_COLUMN, &group,
+                          -1);
+
+      if (group == BINDING_GROUP_USER)
+        {
+          found = TRUE;
+          break;
+        }
+      cont = gtk_tree_model_iter_next (section_model, &iter);
+    }
+  if (found)
+    {
+      GtkTreeSelection *selection;
+
+      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->section_treeview));
+      gtk_tree_selection_select_iter (selection, &iter);
+    }
+
+  /* And add the shortcut */
+  add_custom_shortcut (self, treeview, model);
+}
+
+static void
+remove_button_clicked (GtkWidget       *button,
+                       CcKeyboardPanel *self)
+{
+  GtkTreeView *treeview;
+  GtkTreeModel *model;
+  GtkTreeSelection *selection;
+  GtkTreeIter iter;
+
+  treeview = GTK_TREE_VIEW (self->shortcut_treeview);
+  model = gtk_tree_view_get_model (treeview);
+  selection = gtk_tree_view_get_selection (treeview);
+
+  if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+    {
+      remove_custom_shortcut (self, model, &iter);
+    }
+}
+
+static void
+xkb_options_combo_changed (GtkCellRendererCombo *combo,
+                           gchar                *model_path,
+                           GtkTreeIter          *model_iter,
+                           CcKeyboardPanel      *self)
+{
+  GtkTreeView *shortcut_treeview;
+  GtkTreeModel *shortcut_model;
+  GtkTreeIter shortcut_iter;
+  GtkTreeSelection *selection;
+  CcKeyboardOption *option;
+  ShortcutType type;
+
+  shortcut_treeview = GTK_TREE_VIEW (self->shortcut_treeview);
+  selection = gtk_tree_view_get_selection (shortcut_treeview);
+
+  if (!gtk_tree_selection_get_selected (selection, &shortcut_model, &shortcut_iter))
+    return;
+
+  gtk_tree_model_get (shortcut_model, &shortcut_iter,
+                      DETAIL_KEYENTRY_COLUMN, &option,
+                      DETAIL_TYPE_COLUMN, &type,
+                      -1);
+
+  if (type != SHORTCUT_TYPE_XKB_OPTION)
+    return;
+
+  cc_keyboard_option_set_selection (option, model_iter);
+}
+
+static void
+setup_tree_views (CcKeyboardPanel *self)
+{
+  GtkTreeViewColumn *column;
+  GtkTreeModelSort *sort_model;
+  GtkCellRenderer *renderer;
+  GtkListStore *model;
   GtkWidget *widget;
+  CcShell *shell;
+  GList *focus_chain;
+
+  /* Setup the section treeview */
+  gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (self->section_treeview),
+                                        sections_separator_func,
+                                        self,
+                                        NULL);
+
+  renderer = gtk_cell_renderer_text_new ();
+  column = gtk_tree_view_column_new_with_attributes (_("Section"),
+                                                     renderer,
+                                                     "text", SECTION_DESCRIPTION_COLUMN,
+                                                     NULL);
+  g_object_set (renderer,
+                "width-chars", 20,
+                "ellipsize", PANGO_ELLIPSIZE_END,
+                NULL);
+
+  gtk_tree_view_append_column (GTK_TREE_VIEW (self->section_treeview), column);
+
+  model = gtk_list_store_new (SECTION_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
+  sort_model = GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (model)));
+  gtk_tree_view_set_model (GTK_TREE_VIEW (self->section_treeview), GTK_TREE_MODEL (sort_model));
+  g_object_unref (model);
+
+  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model),
+                                   SECTION_DESCRIPTION_COLUMN,
+                                   section_sort_item,
+                                   self,
+                                   NULL);
+
+  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model),
+                                        SECTION_DESCRIPTION_COLUMN,
+                                        GTK_SORT_ASCENDING);
+  g_object_unref (sort_model);
+
+  section_selection_changed (gtk_tree_view_get_selection (GTK_TREE_VIEW (self->section_treeview)),
+                             self);
+
+  /* Setup the shortcut treeview */
+  renderer = gtk_cell_renderer_text_new ();
+  g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+
+  column = gtk_tree_view_column_new_with_attributes (NULL, renderer, NULL);
+  gtk_tree_view_column_set_cell_data_func (column, renderer, description_set_func, NULL, NULL);
+  gtk_tree_view_column_set_resizable (column, FALSE);
+  gtk_tree_view_column_set_expand (column, TRUE);
+
+  gtk_tree_view_append_column (GTK_TREE_VIEW (self->shortcut_treeview), column);
+
+  renderer = (GtkCellRenderer *) g_object_new (GTK_TYPE_CELL_RENDERER_ACCEL,
+                                               "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
+                                               NULL);
+
+  g_signal_connect (self->shortcut_treeview,
+                    "button_press_event",
+                    G_CALLBACK (start_editing_cb),
+                    renderer);
+  g_signal_connect (self->shortcut_treeview,
+                    "row-activated",
+                    G_CALLBACK (start_editing_kb_cb),
+                    renderer);
+
+  g_signal_connect (renderer,
+                    "accel_edited",
+                    G_CALLBACK (accel_edited_callback),
+                    self);
+  g_signal_connect (renderer,
+                    "accel_cleared",
+                    G_CALLBACK (accel_cleared_callback),
+                    self->shortcut_treeview);
+
+  column = gtk_tree_view_column_new_with_attributes (NULL, renderer, NULL);
+  gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
+  gtk_tree_view_column_set_resizable (column, FALSE);
+  gtk_tree_view_column_set_expand (column, FALSE);
+
+  renderer = (GtkCellRenderer *) g_object_new (GTK_TYPE_CELL_RENDERER_COMBO,
+                                               "has-entry", FALSE,
+                                               "text-column", XKB_OPTION_DESCRIPTION_COLUMN,
+                                               "editable", TRUE,
+                                               "ellipsize", PANGO_ELLIPSIZE_END,
+                                               "width-chars", 25,
+                                               NULL);
+  g_signal_connect (renderer,
+                    "changed",
+                    G_CALLBACK (xkb_options_combo_changed),
+                    self);
 
-  obj = G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->constructor (gtype, n_properties, properties);
+  gtk_tree_view_column_pack_end (column, renderer, FALSE);
 
-  self = CC_KEYBOARD_PANEL (obj);
+  gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
+
+  gtk_tree_view_append_column (GTK_TREE_VIEW (self->shortcut_treeview), column);
+
+  model = gtk_list_store_new (DETAIL_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
+  gtk_tree_view_set_model (GTK_TREE_VIEW (self->shortcut_treeview), GTK_TREE_MODEL (model));
+  g_object_unref (model);
+
+  setup_keyboard_options (model);
+
+  /* set up the focus chain */
+  focus_chain = g_list_append (NULL, self->section_treeview);
+  focus_chain = g_list_append (focus_chain, self->shortcut_treeview);
+  focus_chain = g_list_append (focus_chain, self->shortcut_toolbar);
+
+  gtk_container_set_focus_chain (GTK_CONTAINER (self), focus_chain);
+  g_list_free (focus_chain);
+
+  /* set up the dialog */
+  shell = cc_panel_get_shell (CC_PANEL (self));
+  widget = cc_shell_get_toplevel (shell);
+
+  gtk_dialog_set_default_response (GTK_DIALOG (self->custom_shortcut_dialog), GTK_RESPONSE_OK);
+  gtk_window_set_transient_for (GTK_WINDOW (self->custom_shortcut_dialog), GTK_WINDOW (widget));
+}
+
+static gboolean
+cc_keyboard_panel_set_section (CcKeyboardPanel *self,
+                               const char      *section)
+{
+  GtkTreeModel *section_model;
+  GtkTreeIter iter;
+  gboolean found, cont;
+
+  section_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->section_treeview));
+  cont = gtk_tree_model_get_iter_first (section_model, &iter);
+  found = FALSE;
+  while (cont)
+    {
+      char *id;
+
+      gtk_tree_model_get (section_model, &iter,
+                          SECTION_ID_COLUMN, &id,
+                          -1);
+
+      if (g_strcmp0 (id, section) == 0)
+        {
+          found = TRUE;
+          g_free (id);
+          break;
+        }
+      g_free (id);
+      cont = gtk_tree_model_iter_next (section_model, &iter);
+    }
+  if (found)
+    {
+      GtkTreeSelection *selection;
 
-  keyboard_shortcuts_init (CC_PANEL (self), self->builder);
+      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->section_treeview));
+      gtk_tree_selection_select_iter (selection, &iter);
+    }
+  else
+    {
+      g_warning ("Could not find section '%s' to switch to.", section);
+    }
 
-  widget = (GtkWidget *) gtk_builder_get_object (self->builder, "shortcuts_page");
+  return found;
+}
 
-  gtk_container_add (GTK_CONTAINER (self), widget);
+static void
+cc_keyboard_panel_set_property (GObject      *object,
+                               guint         property_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  switch (property_id)
+    {
+    case PROP_PARAMETERS:
+      break;
 
-  return obj;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
 }
 
 static const char *
@@ -141,56 +1783,88 @@ cc_keyboard_panel_get_help_uri (CcPanel *panel)
 }
 
 static void
-cc_keyboard_panel_dispose (GObject *object)
+cc_keyboard_panel_finalize (GObject *object)
 {
-  keyboard_shortcuts_dispose (CC_PANEL (object));
+  CcKeyboardPanel *self = CC_KEYBOARD_PANEL (object);
 
-  G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->dispose (object);
+  g_clear_pointer (&self->kb_system_sections, g_hash_table_destroy);
+  g_clear_pointer (&self->kb_apps_sections, g_hash_table_destroy);
+  g_clear_pointer (&self->kb_user_sections, g_hash_table_destroy);
+  g_clear_pointer (&self->pictures_regex, g_regex_unref);
+  g_clear_pointer (&self->wm_changed_id, wm_common_unregister_window_manager_change);
+
+  g_clear_object (&self->custom_shortcut_dialog);
+  g_clear_object (&self->binding_settings);
+
+  cc_keyboard_option_clear_all ();
+
+  G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->finalize (object);
 }
 
 static void
-cc_keyboard_panel_finalize (GObject *object)
+on_window_manager_change (const char      *wm_name,
+                          CcKeyboardPanel *self)
 {
-  CcKeyboardPanel *panel = CC_KEYBOARD_PANEL (object);
+  reload_sections (self);
+}
 
-  if (panel->builder)
-    g_object_unref (panel->builder);
+static void
+cc_keyboard_panel_constructed (GObject *object)
+{
+  CcKeyboardPanel *self = CC_KEYBOARD_PANEL (object);
 
-  G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->finalize (object);
+  G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->constructed (object);
+
+#ifdef GDK_WINDOWING_X11
+  if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
+    self->wm_changed_id = wm_common_register_window_manager_change ((GFunc) on_window_manager_change,
+                                                                    self);
+#endif
+
+  setup_tree_views (self);
+  reload_sections (self);
 }
 
 static void
 cc_keyboard_panel_class_init (CcKeyboardPanelClass *klass)
 {
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
 
   panel_class->get_help_uri = cc_keyboard_panel_get_help_uri;
 
-  object_class->constructor = cc_keyboard_panel_constructor;
   object_class->set_property = cc_keyboard_panel_set_property;
-  object_class->dispose = cc_keyboard_panel_dispose;
   object_class->finalize = cc_keyboard_panel_finalize;
+  object_class->constructed = cc_keyboard_panel_constructed;
 
   g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/control-center/keyboard/gnome-keyboard-panel.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, add_toolbutton);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, custom_shortcut_command_entry);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, custom_shortcut_dialog);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, custom_shortcut_name_entry);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, custom_shortcut_ok_button);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, remove_toolbutton);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, section_treeview);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, shortcut_toolbar);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, shortcut_treeview);
+
+  gtk_widget_class_bind_template_callback (widget_class, add_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, remove_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, section_selection_changed);
+  gtk_widget_class_bind_template_callback (widget_class, shortcut_entry_changed);
+  gtk_widget_class_bind_template_callback (widget_class, shortcut_selection_changed);
 }
 
 static void
 cc_keyboard_panel_init (CcKeyboardPanel *self)
 {
-  GError *error = NULL;
-
   g_resources_register (cc_keyboard_get_resource ());
 
-  self->builder = gtk_builder_new ();
+  gtk_widget_init_template (GTK_WIDGET (self));
 
-  if (gtk_builder_add_from_resource (self->builder,
-                                     "/org/gnome/control-center/keyboard/gnome-keyboard-panel.ui",
-                                     &error) == 0)
-    {
-      g_warning ("Could not load UI: %s", error->message);
-      g_clear_error (&error);
-      g_object_unref (self->builder);
-      self->builder = NULL;
-    }
+  self->binding_settings = g_settings_new (BINDINGS_SCHEMA);
 }
diff --git a/panels/keyboard/cc-keyboard-panel.h b/panels/keyboard/cc-keyboard-panel.h
index 2276c3f..859a807 100644
--- a/panels/keyboard/cc-keyboard-panel.h
+++ b/panels/keyboard/cc-keyboard-panel.h
@@ -23,6 +23,7 @@
 #define _CC_KEYBOARD_PANEL_H
 
 #include <shell/cc-panel.h>
+#include <gtk/gtk.h>
 
 G_BEGIN_DECLS
 
diff --git a/panels/keyboard/gnome-keyboard-panel.ui b/panels/keyboard/gnome-keyboard-panel.ui
index dd9e50a..cd453db 100644
--- a/panels/keyboard/gnome-keyboard-panel.ui
+++ b/panels/keyboard/gnome-keyboard-panel.ui
@@ -8,10 +8,11 @@
     <property name="step_increment">200</property>
     <property name="page_increment">200</property>
   </object>
-  <object class="GtkDialog" id="custom-shortcut-dialog">
+  <object class="GtkDialog" id="custom_shortcut_dialog">
     <property name="can_focus">False</property>
     <property name="type_hint">dialog</property>
     <property name="use_header_bar">1</property>
+    <property name="resizable">False</property>
     <child internal-child="headerbar">
       <object class="GtkHeaderBar" id="dialog-header-bar">
         <property name="visible">True</property>
@@ -19,7 +20,7 @@
         <property name="title" translatable="yes">Custom Shortcut</property>
         <property name="show_close_button">False</property>
         <child>
-          <object class="GtkButton" id="custom-shortcut-cancel-button">
+          <object class="GtkButton" id="custom_shortcut_cancel_button">
             <property name="label" translatable="yes">_Cancel</property>
             <property name="visible">True</property>
             <property name="can_focus">True</property>
@@ -36,7 +37,7 @@
           </packing>
         </child>
         <child>
-          <object class="GtkButton" id="custom-shortcut-ok-button">
+          <object class="GtkButton" id="custom_shortcut_ok_button">
             <property name="label" translatable="yes">_Add</property>
             <property name="visible">True</property>
             <property name="can_focus">True</property>
@@ -81,7 +82,7 @@
                     <property name="xalign">0</property>
                     <property name="label" translatable="yes">_Name:</property>
                     <property name="use_underline">True</property>
-                    <property name="mnemonic_widget">custom-shortcut-name-entry</property>
+                    <property name="mnemonic_widget">custom_shortcut_name_entry</property>
                   </object>
                   <packing>
                     <property name="left_attach">0</property>
@@ -95,7 +96,7 @@
                     <property name="xalign">0</property>
                     <property name="label" translatable="yes">C_ommand:</property>
                     <property name="use_underline">True</property>
-                    <property name="mnemonic_widget">custom-shortcut-command-entry</property>
+                    <property name="mnemonic_widget">custom_shortcut_command_entry</property>
                   </object>
                   <packing>
                     <property name="left_attach">0</property>
@@ -103,12 +104,13 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkEntry" id="custom-shortcut-name-entry">
+                  <object class="GtkEntry" id="custom_shortcut_name_entry">
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
                     <property name="hexpand">True</property>
                     <property name="invisible_char">•</property>
                     <property name="activates_default">True</property>
+                    <signal name="changed" handler="shortcut_entry_changed" object="CcKeyboardPanel" 
swapped="no" />
                   </object>
                   <packing>
                     <property name="left_attach">1</property>
@@ -116,12 +118,13 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkEntry" id="custom-shortcut-command-entry">
+                  <object class="GtkEntry" id="custom_shortcut_command_entry">
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
                     <property name="hexpand">True</property>
                     <property name="invisible_char">•</property>
                     <property name="activates_default">True</property>
+                    <signal name="changed" handler="shortcut_entry_changed" object="CcKeyboardPanel" 
swapped="no" />
                   </object>
                   <packing>
                     <property name="left_attach">1</property>
@@ -145,161 +148,174 @@
       </object>
     </child>
     <action-widgets>
-      <action-widget response="-6">custom-shortcut-cancel-button</action-widget>
-      <action-widget response="-5">custom-shortcut-ok-button</action-widget>
+      <action-widget response="-6">custom_shortcut_cancel_button</action-widget>
+      <action-widget response="-5">custom_shortcut_ok_button</action-widget>
     </action-widgets>
   </object>
-              <object class="GtkBox" id="shortcuts_page">
+  <template class="CcKeyboardPanel" parent="CcPanel">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="border_width">12</property>
+    <child>
+      <object class="GtkBox" id="shortcuts_page">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="border_width">5</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkBox" id="shortcuts_vbox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkGrid" id="shortcuts_grid">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="orientation">vertical</property>
-                <property name="border_width">5</property>
-                <property name="spacing">12</property>
+                <property name="column_spacing">5</property>
                 <child>
-                  <object class="GtkBox" id="shortcuts_vbox">
+                  <object class="GtkScrolledWindow" id="sections_swindow">
                     <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="orientation">vertical</property>
-                    <property name="spacing">6</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">never</property>
+                    <property name="shadow_type">in</property>
                     <child>
-                      <object class="GtkGrid" id="shortcuts_grid">
+                      <object class="GtkTreeView" id="section_treeview">
                         <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="column_spacing">5</property>
-                        <child>
-                          <object class="GtkScrolledWindow" id="sections_swindow">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="hscrollbar_policy">never</property>
-                            <property name="shadow_type">in</property>
-                            <child>
-                              <object class="GtkTreeView" id="section_treeview">
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property name="headers_visible">False</property>
-                                <child internal-child="selection">
-                                  <object class="GtkTreeSelection" id="treeview-selection1"/>
-                                </child>
-                              </object>
-                            </child>
-                          </object>
-                          <packing>
-                            <property name="left_attach">0</property>
-                            <property name="top_attach">0</property>
-                            <property name="height">2</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <object class="GtkScrolledWindow" id="actions_swindow">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="hexpand">True</property>
-                            <property name="vexpand">True</property>
-                            <property name="hscrollbar_policy">never</property>
-                            <property name="shadow_type">in</property>
-                            <child>
-                              <object class="GtkTreeView" id="shortcut_treeview">
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property name="headers_visible">False</property>
-                                <property name="rules_hint">True</property>
-                                <child internal-child="selection">
-                                  <object class="GtkTreeSelection" id="treeview-selection2"/>
-                                </child>
-                              </object>
-                            </child>
+                        <property name="can_focus">True</property>
+                        <property name="headers_visible">False</property>
+                        <child internal-child="selection">
+                          <object class="GtkTreeSelection" id="treeview-selection1">
+                            <property name="mode">browse</property>
+                            <signal name="changed" handler="section_selection_changed" 
object="CcKeyboardPanel" swapped="no" />
                           </object>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="top_attach">0</property>
-                          </packing>
                         </child>
-                        <child>
-                          <object class="GtkToolbar" id="shortcut-toolbar">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="icon_size">1</property>
-                            <property name="toolbar-style">icons</property>
-                            <style>
-                              <class name="inline-toolbar"/>
-                            </style>
-                            <child>
-                              <object class="GtkToolButton" id="add-toolbutton">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="use_action_appearance">False</property>
-                                <property name="use_underline">True</property>
-                                <property name="icon_name">list-add-symbolic</property>
-                                <property name="label" translatable="yes">Add Shortcut</property>
-                              </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="homogeneous">True</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkToolButton" id="remove-toolbutton">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="use_action_appearance">False</property>
-                                <property name="use_underline">True</property>
-                                <property name="icon_name">list-remove-symbolic</property>
-                                <property name="label" translatable="yes">Remove Shortcut</property>
-                              </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="homogeneous">True</property>
-                              </packing>
-                            </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                    <property name="height">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hexpand">True</property>
+                    <property name="vexpand">True</property>
+                    <property name="hscrollbar_policy">never</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="GtkTreeView" id="shortcut_treeview">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="headers_visible">False</property>
+                        <property name="rules_hint">True</property>
+                        <child internal-child="selection">
+                          <object class="GtkTreeSelection" id="treeview-selection2">
+                            <signal name="changed" handler="shortcut_selection_changed" 
object="remove_toolbutton" swapped="no" />
                           </object>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="top_attach">1</property>
-                          </packing>
                         </child>
                       </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolbar" id="shortcut_toolbar">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_size">1</property>
+                    <property name="toolbar-style">icons</property>
+                    <style>
+                      <class name="inline-toolbar"/>
+                    </style>
+                    <child>
+                      <object class="GtkToolButton" id="add_toolbutton">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="use_action_appearance">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="icon_name">list-add-symbolic</property>
+                        <property name="label" translatable="yes">Add Shortcut</property>
+                        <signal name="clicked" handler="add_button_clicked" object="CcKeyboardPanel" 
swapped="no" />
+                      </object>
                       <packing>
-                        <property name="expand">True</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
+                        <property name="expand">False</property>
+                        <property name="homogeneous">True</property>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkBox">
+                      <object class="GtkToolButton" id="remove_toolbutton">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="border_width">5</property>
-                        <property name="spacing">12</property>
-                        <child>
-                          <object class="GtkLabel" id="label12">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="xalign">0</property>
-                            <property name="label" translatable="yes">To edit a shortcut, click the row and 
hold down the new keys or press Backspace to clear.</property>
-                            <property name="justify">fill</property>
-                            <property name="wrap">True</property>
-                            <property name="max_width_chars">70</property>
-                          </object>
-                          <packing>
-                            <property name="expand">True</property>
-                            <property name="fill">True</property>
-                            <property name="position">0</property>
-                          </packing>
-                        </child>
+                        <property name="use_action_appearance">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="icon_name">list-remove-symbolic</property>
+                        <property name="label" translatable="yes">Remove Shortcut</property>
+                        <signal name="clicked" handler="remove_button_clicked" object="CcKeyboardPanel" 
swapped="no" />
                       </object>
                       <packing>
                         <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
+                        <property name="homogeneous">True</property>
                       </packing>
                     </child>
                   </object>
                   <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="hbox1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="border_width">5</property>
+                <property name="spacing">12</property>
+                <child>
+                  <object class="GtkLabel" id="label12">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">To edit a shortcut, click the row and hold 
down the new keys or press Backspace to clear.</property>
+                    <property name="justify">fill</property>
+                    <property name="wrap">True</property>
+                    <property name="max_width_chars">70</property>
+                  </object>
+                  <packing>
                     <property name="expand">True</property>
                     <property name="fill">True</property>
                     <property name="position">0</property>
                   </packing>
                 </child>
               </object>
-
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
 </interface>
diff --git a/panels/keyboard/keyboard-shortcuts.c b/panels/keyboard/keyboard-shortcuts.c
index 3a6ffd0..5b59eba 100644
--- a/panels/keyboard/keyboard-shortcuts.c
+++ b/panels/keyboard/keyboard-shortcuts.c
@@ -28,238 +28,12 @@
 #include "cc-keyboard-option.h"
 #include "wm-common.h"
 
-#ifdef GDK_WINDOWING_X11
-#include <gdk/gdkx.h>
-#endif
-
-#define BINDINGS_SCHEMA "org.gnome.settings-daemon.plugins.media-keys"
-#define CUSTOM_KEYS_BASENAME "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings"
-#define CUSTOM_SHORTCUTS_ID "custom"
-#define WID(builder, name) (GTK_WIDGET (gtk_builder_get_object (builder, name)))
-
-static GRegex *pictures_regex = NULL;
-static GSettings *binding_settings = NULL;
-static GtkWidget *custom_shortcut_dialog = NULL;
-static GtkWidget *custom_shortcut_name_entry = NULL;
-static GtkWidget *custom_shortcut_command_entry = NULL;
-static GtkWidget *custom_shortcut_ok_button = NULL;
-static GHashTable *kb_system_sections = NULL;
-static GHashTable *kb_apps_sections = NULL;
-static GHashTable *kb_user_sections = NULL;
-static gpointer wm_changed_id = NULL;
-
-static void
-free_key_array (GPtrArray *keys)
-{
-  if (keys != NULL)
-    {
-      gint i;
-
-      for (i = 0; i < keys->len; i++)
-        {
-          CcKeyboardItem *item;
-
-          item = g_ptr_array_index (keys, i);
-
-          g_object_unref (item);
-        }
-
-      g_ptr_array_free (keys, TRUE);
-    }
-}
-
-static GHashTable *
-get_hash_for_group (BindingGroupType group)
-{
-  GHashTable *hash;
-
-  switch (group)
-    {
-    case BINDING_GROUP_SYSTEM:
-      hash = kb_system_sections;
-      break;
-    case BINDING_GROUP_APPS:
-      hash = kb_apps_sections;
-      break;
-    case BINDING_GROUP_USER:
-      hash = kb_user_sections;
-      break;
-    default:
-      hash = NULL;
-    }
-  return hash;
-}
-
-static gboolean
-have_key_for_group (int group, const gchar *name)
-{
-  GHashTableIter iter;
-  GPtrArray *keys;
-  gint i;
-
-  g_hash_table_iter_init (&iter, get_hash_for_group (group));
-  while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&keys))
-    {
-      for (i = 0; i < keys->len; i++)
-        {
-          CcKeyboardItem *item = g_ptr_array_index (keys, i);
-
-         if (item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS &&
-             g_strcmp0 (name, item->key) == 0)
-           {
-                 return TRUE;
-           }
-
-         return FALSE;
-        }
-    }
-
-  return FALSE;
-}
-
-static gboolean
-keybinding_key_changed_foreach (GtkTreeModel   *model,
-                                GtkTreePath    *path,
-                                GtkTreeIter    *iter,
-                                CcKeyboardItem *item)
-{
-  CcKeyboardItem *tmp_item;
-
-  gtk_tree_model_get (item->model, iter,
-                      DETAIL_KEYENTRY_COLUMN, &tmp_item,
-                      -1);
-
-  if (item == tmp_item)
-    {
-      gtk_tree_model_row_changed (item->model, path, iter);
-      return TRUE;
-    }
-  return FALSE;
-}
-
-static void
-item_changed (CcKeyboardItem *item,
-             GParamSpec     *pspec,
-             gpointer        user_data)
-{
-  /* update the model */
-  gtk_tree_model_foreach (item->model, (GtkTreeModelForeachFunc) keybinding_key_changed_foreach, item);
-}
-
-
-static void
-append_section (GtkBuilder         *builder,
-                const gchar        *title,
-                const gchar        *id,
-                BindingGroupType    group,
-                const KeyListEntry *keys_list)
-{
-  GPtrArray *keys_array;
-  GtkTreeModel *sort_model;
-  GtkTreeModel *model, *shortcut_model;
-  GtkTreeIter iter;
-  gint i;
-  GHashTable *hash;
-  gboolean is_new;
-
-  hash = get_hash_for_group (group);
-  if (!hash)
-    return;
-
-  sort_model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder, 
"section_treeview")));
-  model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model));
-
-  shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder, 
"shortcut_treeview")));
-
-  /* Add all CcKeyboardItems for this section */
-  is_new = FALSE;
-  keys_array = g_hash_table_lookup (hash, id);
-  if (keys_array == NULL)
-    {
-      keys_array = g_ptr_array_new ();
-      is_new = TRUE;
-    }
-
-  GHashTable *reverse_items = g_hash_table_new (g_str_hash, g_str_equal);
-
-  for (i = 0; keys_list != NULL && keys_list[i].name != NULL; i++)
-    {
-      CcKeyboardItem *item;
-      gboolean ret;
-
-      if (have_key_for_group (group, keys_list[i].name))
-        continue;
-
-      item = cc_keyboard_item_new (keys_list[i].type);
-      switch (keys_list[i].type)
-        {
-       case CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH:
-          ret = cc_keyboard_item_load_from_gsettings_path (item, keys_list[i].name, FALSE);
-          break;
-       case CC_KEYBOARD_ITEM_TYPE_GSETTINGS:
-         ret = cc_keyboard_item_load_from_gsettings (item,
-                                                     keys_list[i].description,
-                                                     keys_list[i].schema,
-                                                     keys_list[i].name);
-          if (ret && keys_list[i].reverse_entry != NULL)
-            {
-              CcKeyboardItem *reverse_item;
-              reverse_item = g_hash_table_lookup (reverse_items,
-                                                  keys_list[i].reverse_entry);
-              if (reverse_item != NULL)
-                {
-                  cc_keyboard_item_add_reverse_item (item,
-                                                     reverse_item,
-                                                     keys_list[i].is_reversed);
-                }
-              else
-                {
-                  g_hash_table_insert (reverse_items,
-                                       keys_list[i].name,
-                                       item);
-                }
-            }
-         break;
-       default:
-         g_assert_not_reached ();
-       }
-
-      if (ret == FALSE)
-        {
-          /* We don't actually want to popup a dialog - just skip this one */
-          g_object_unref (item);
-          continue;
-        }
-
-      cc_keyboard_item_set_hidden (item, keys_list[i].hidden);
-      item->model = shortcut_model;
-
-      g_signal_connect (G_OBJECT (item), "notify",
-                       G_CALLBACK (item_changed), NULL);
-
-      g_ptr_array_add (keys_array, item);
-    }
-
-  g_hash_table_destroy (reverse_items);
-
-  /* Add the keys to the hash table */
-  if (is_new)
-    {
-      g_hash_table_insert (hash, g_strdup (id), keys_array);
-
-      /* Append the section to the left tree view */
-      gtk_list_store_append (GTK_LIST_STORE (model), &iter);
-      gtk_list_store_set (GTK_LIST_STORE (model), &iter,
-                          SECTION_DESCRIPTION_COLUMN, title,
-                          SECTION_ID_COLUMN, id,
-                          SECTION_GROUP_COLUMN, group,
-                          -1);
-    }
-}
+#define CUSTOM_KEYS_BASENAME  "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings"
 
 static char *
 replace_pictures_folder (const char *description)
 {
+  GRegex *pictures_regex;
   const char *path;
   char *dirname;
   char *ret;
@@ -270,11 +44,15 @@ replace_pictures_folder (const char *description)
   if (strstr (description, "$PICTURES") == NULL)
     return g_strdup (description);
 
+  pictures_regex = g_regex_new ("\\$PICTURES", 0, 0, NULL);
   path = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
   dirname = g_filename_display_basename (path);
   ret = g_regex_replace (pictures_regex, description, -1,
                          0, dirname, 0, NULL);
+
+  g_regex_unref (pictures_regex);
   g_free (dirname);
+
   if (ret == NULL)
     return g_strdup (description);
 
@@ -436,412 +214,7 @@ parse_start_tag (GMarkupParseContext *ctx,
   g_array_append_val (keylist->entries, key);
 }
 
-static gboolean
-strv_contains (char **strv,
-               char  *str)
-{
-  char **p = strv;
-  for (p = strv; *p; p++)
-    if (strcmp (*p, str) == 0)
-      return TRUE;
-
-  return FALSE;
-}
-
-static void
-append_sections_from_file (GtkBuilder *builder, const gchar *path, const char *datadir, gchar 
**wm_keybindings)
-{
-  GError *err = NULL;
-  char *buf;
-  gsize buf_len;
-  KeyList *keylist;
-  KeyListEntry *keys;
-  KeyListEntry key = { 0, };
-  const char *title;
-  int group;
-  guint i;
-  GMarkupParseContext *ctx;
-  GMarkupParser parser = { parse_start_tag, NULL, NULL, NULL, NULL };
-
-  /* Parse file */
-  if (!g_file_get_contents (path, &buf, &buf_len, &err))
-    return;
-
-  keylist = g_new0 (KeyList, 1);
-  keylist->entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
-  ctx = g_markup_parse_context_new (&parser, 0, keylist, NULL);
-
-  if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err))
-    {
-      g_warning ("Failed to parse '%s': '%s'", path, err->message);
-      g_error_free (err);
-      g_free (keylist->name);
-      g_free (keylist->package);
-      g_free (keylist->wm_name);
-      for (i = 0; i < keylist->entries->len; i++)
-        g_free (((KeyListEntry *) &(keylist->entries->data[i]))->name);
-      g_array_free (keylist->entries, TRUE);
-      g_free (keylist);
-      keylist = NULL;
-    }
-  g_markup_parse_context_free (ctx);
-  g_free (buf);
-
-  if (keylist == NULL)
-    return;
-
-  /* If there's no keys to add, or the settings apply to a window manager
-   * that's not the one we're running */
-  if (keylist->entries->len == 0
-      || (keylist->wm_name != NULL && !strv_contains (wm_keybindings, keylist->wm_name))
-      || keylist->name == NULL)
-    {
-      g_free (keylist->name);
-      g_free (keylist->package);
-      g_free (keylist->wm_name);
-      g_array_free (keylist->entries, TRUE);
-      g_free (keylist);
-      return;
-    }
-
-  /* Empty KeyListEntry to end the array */
-  key.name = NULL;
-  g_array_append_val (keylist->entries, key);
-
-  keys = (KeyListEntry *) g_array_free (keylist->entries, FALSE);
-  if (keylist->package)
-    {
-      char *localedir;
-
-      localedir = g_build_filename (datadir, "locale", NULL);
-      bindtextdomain (keylist->package, localedir);
-      g_free (localedir);
-
-      title = dgettext (keylist->package, keylist->name);
-    } else {
-      title = _(keylist->name);
-    }
-  if (keylist->group && strcmp (keylist->group, "system") == 0)
-    group = BINDING_GROUP_SYSTEM;
-  else
-    group = BINDING_GROUP_APPS;
-
-  append_section (builder, title, keylist->name, group, keys);
-
-  g_free (keylist->name);
-  g_free (keylist->package);
-  g_free (keylist->wm_name);
-  g_free (keylist->schema);
-  g_free (keylist->group);
-
-  for (i = 0; keys[i].name != NULL; i++) {
-    KeyListEntry *entry = &keys[i];
-    g_free (entry->schema);
-    g_free (entry->description);
-    g_free (entry->name);
-    g_free (entry->reverse_entry);
-  }
-  g_free (keys);
-
-  g_free (keylist);
-}
-
-static void
-append_sections_from_gsettings (GtkBuilder *builder)
-{
-  char **custom_paths;
-  GArray *entries;
-  KeyListEntry key = { 0, };
-  int i;
-
-  /* load custom shortcuts from GSettings */
-  entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
-
-  custom_paths = g_settings_get_strv (binding_settings, "custom-keybindings");
-  for (i = 0; custom_paths[i]; i++)
-    {
-      key.name = g_strdup (custom_paths[i]);
-      if (!have_key_for_group (BINDING_GROUP_USER, key.name))
-        {
-          key.type = CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH;
-          g_array_append_val (entries, key);
-        }
-      else
-        g_free (key.name);
-    }
-  g_strfreev (custom_paths);
-
-  if (entries->len > 0)
-    {
-      KeyListEntry *keys;
-      int i;
-
-      /* Empty KeyListEntry to end the array */
-      key.name = NULL;
-      g_array_append_val (entries, key);
-
-      keys = (KeyListEntry *) entries->data;
-      append_section (builder, _("Custom Shortcuts"), CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, keys);
-      for (i = 0; i < entries->len; ++i)
-        {
-          g_free (keys[i].name);
-        }
-    }
-  else
-    {
-      append_section (builder, _("Custom Shortcuts"), CUSTOM_SHORTCUTS_ID, BINDING_GROUP_USER, NULL);
-    }
-
-  g_array_free (entries, TRUE);
-}
-
-static void
-reload_sections (CcPanel *panel)
-{
-  GtkBuilder *builder;
-  gchar **wm_keybindings;
-  gchar *default_wm_keybindings[] = { "Mutter", "GNOME Shell", NULL };
-  GDir *dir;
-  GtkTreeModel *sort_model;
-  GtkTreeModel *section_model;
-  GtkTreeModel *shortcut_model;
-  const gchar * const * data_dirs;
-  guint i;
-  GtkTreeView *section_treeview;
-  GtkTreeSelection *selection;
-  GtkTreeIter iter;
-  GHashTable *loaded_files;
-  const char *section_to_set;
-
-  builder = g_object_get_data (G_OBJECT (panel), "builder");
-
-  section_treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "section_treeview"));
-  sort_model = gtk_tree_view_get_model (section_treeview);
-  section_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model));
-
-  shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder, 
"shortcut_treeview")));
-  /* FIXME: get current selection and keep it after refreshing */
-
-  /* Clear previous models and hash tables */
-  gtk_list_store_clear (GTK_LIST_STORE (section_model));
-  gtk_list_store_clear (GTK_LIST_STORE (shortcut_model));
-  if (kb_system_sections != NULL)
-    g_hash_table_destroy (kb_system_sections);
-  kb_system_sections = g_hash_table_new_full (g_str_hash,
-                                              g_str_equal,
-                                              g_free,
-                                              (GDestroyNotify) free_key_array);
-
-  if (kb_apps_sections != NULL)
-    g_hash_table_destroy (kb_apps_sections);
-  kb_apps_sections = g_hash_table_new_full (g_str_hash,
-                                            g_str_equal,
-                                            g_free,
-                                            (GDestroyNotify) free_key_array);
-
-  if (kb_user_sections != NULL)
-    g_hash_table_destroy (kb_user_sections);
-  kb_user_sections = g_hash_table_new_full (g_str_hash,
-                                            g_str_equal,
-                                            g_free,
-                                            (GDestroyNotify) free_key_array);
-
-  /* Load WM keybindings */
-#ifdef GDK_WINDOWING_X11
-  if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
-    wm_keybindings = wm_common_get_current_keybindings ();
-#endif
-  else
-    wm_keybindings = g_strdupv (default_wm_keybindings);
-
-  loaded_files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-
-  data_dirs = g_get_system_data_dirs ();
-  for (i = 0; data_dirs[i] != NULL; i++)
-    {
-      char *dir_path;
-      const gchar *name;
-
-      dir_path = g_build_filename (data_dirs[i], "gnome-control-center", "keybindings", NULL);
-
-      dir = g_dir_open (dir_path, 0, NULL);
-      if (!dir)
-        {
-          g_free (dir_path);
-          continue;
-        }
-
-      for (name = g_dir_read_name (dir) ; name ; name = g_dir_read_name (dir))
-        {
-          gchar *path;
-
-         if (g_str_has_suffix (name, ".xml") == FALSE)
-           continue;
-
-          if (g_hash_table_lookup (loaded_files, name) != NULL)
-           {
-             g_debug ("Not loading %s, it was already loaded from another directory", name);
-              continue;
-           }
-
-         g_hash_table_insert (loaded_files, g_strdup (name), GINT_TO_POINTER (1));
-         path = g_build_filename (dir_path, name, NULL);
-         append_sections_from_file (builder, path, data_dirs[i], wm_keybindings);
-         g_free (path);
-       }
-      g_free (dir_path);
-      g_dir_close (dir);
-    }
-
-  g_hash_table_destroy (loaded_files);
-  g_strfreev (wm_keybindings);
-
-  /* Add a separator */
-  gtk_list_store_append (GTK_LIST_STORE (section_model), &iter);
-  gtk_list_store_set (GTK_LIST_STORE (section_model), &iter,
-                      SECTION_DESCRIPTION_COLUMN, NULL,
-                      SECTION_GROUP_COLUMN, BINDING_GROUP_SEPARATOR,
-                      -1);
-
-  /* Load custom keybindings */
-  append_sections_from_gsettings (builder);
-
-  /* Select the first item, or the requested section, if any */
-  section_to_set = g_object_get_data (G_OBJECT (panel), "section-to-set");
-  if (section_to_set != NULL)
-    {
-      if (keyboard_shortcuts_set_section (panel, section_to_set))
-        {
-          g_object_set_data (G_OBJECT (panel), "section-to-set", NULL);
-          return;
-       }
-    }
-  g_assert (gtk_tree_model_get_iter_first (sort_model, &iter));
-  selection = gtk_tree_view_get_selection (section_treeview);
-  gtk_tree_selection_select_iter (selection, &iter);
-
-  g_object_set_data (G_OBJECT (panel), "section-to-set", NULL);
-}
-
-static void
-accel_set_func (GtkTreeViewColumn *tree_column,
-                GtkCellRenderer   *cell,
-                GtkTreeModel      *model,
-                GtkTreeIter       *iter,
-                gpointer           data)
-{
-  gpointer entry;
-  ShortcutType type;
-
-  gtk_tree_model_get (model, iter,
-                      DETAIL_KEYENTRY_COLUMN, &entry,
-                      DETAIL_TYPE_COLUMN, &type,
-                      -1);
-
-  gtk_cell_renderer_set_visible (cell, FALSE);
-
-  if (type == SHORTCUT_TYPE_XKB_OPTION &&
-      GTK_IS_CELL_RENDERER_COMBO (cell))
-    {
-      CcKeyboardOption *option = entry;
-
-      gtk_cell_renderer_set_visible (cell, TRUE);
-      g_object_set (cell,
-                    "model", cc_keyboard_option_get_store (option),
-                    "text", cc_keyboard_option_get_current_value_description (option),
-                    NULL);
-    }
-  else if (type == SHORTCUT_TYPE_KEY_ENTRY &&
-           GTK_IS_CELL_RENDERER_TEXT (cell) &&
-           !GTK_IS_CELL_RENDERER_COMBO (cell) &&
-           entry != NULL)
-    {
-      CcKeyboardItem *item = entry;
-
-      gtk_cell_renderer_set_visible (cell, TRUE);
-
-      if (item->editable)
-        g_object_set (cell,
-                      "editable", TRUE,
-                      "accel-key", item->keyval,
-                      "accel-mods", item->mask,
-                      "keycode", item->keycode,
-                      "style", PANGO_STYLE_NORMAL,
-                      NULL);
-      else
-        g_object_set (cell,
-                      "editable", FALSE,
-                      "accel-key", item->keyval,
-                      "accel-mods", item->mask,
-                      "keycode", item->keycode,
-                      "style", PANGO_STYLE_ITALIC,
-                      NULL);
-    }
-}
-
-static void
-description_set_func (GtkTreeViewColumn *tree_column,
-                      GtkCellRenderer   *cell,
-                      GtkTreeModel      *model,
-                      GtkTreeIter       *iter,
-                      gpointer           data)
-{
-  gchar *description;
-  CcKeyboardItem *item;
-  ShortcutType type;
-
-  gtk_tree_model_get (model, iter,
-                      DETAIL_DESCRIPTION_COLUMN, &description,
-                      DETAIL_KEYENTRY_COLUMN, &item,
-                      DETAIL_TYPE_COLUMN, &type,
-                      -1);
-
-  if (type == SHORTCUT_TYPE_XKB_OPTION)
-    {
-      g_object_set (cell, "text", description, NULL);
-    }
-  else
-    {
-      if (item != NULL)
-        g_object_set (cell,
-                      "editable", FALSE,
-                      "text", item->description != NULL ?
-                      item->description : _("<Unknown Action>"),
-                      NULL);
-      else
-        g_object_set (cell,
-                      "editable", FALSE, NULL);
-    }
-
-  g_free (description);
-}
-
-static void
-shortcut_selection_changed (GtkTreeSelection *selection, gpointer data)
-{
-  GtkWidget *button = data;
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  CcKeyboardItem *item;
-  gboolean can_remove;
-  ShortcutType type;
-
-  can_remove = FALSE;
-  if (gtk_tree_selection_get_selected (selection, &model, &iter))
-    {
-      gtk_tree_model_get (model, &iter,
-                          DETAIL_KEYENTRY_COLUMN, &item,
-                          DETAIL_TYPE_COLUMN, &type,
-                          -1);
-      if (type == SHORTCUT_TYPE_KEY_ENTRY &&
-          item && item->command != NULL && item->editable)
-        can_remove = TRUE;
-    }
-
-  gtk_widget_set_sensitive (button, can_remove);
-}
-
-static void
+void
 fill_xkb_options_shortcuts (GtkTreeModel *model)
 {
   GList *l;
@@ -860,284 +233,6 @@ fill_xkb_options_shortcuts (GtkTreeModel *model)
     }
 }
 
-static void
-section_selection_changed (GtkTreeSelection *selection, gpointer data)
-{
-  GtkTreeIter iter;
-  GtkTreeModel *model;
-  GtkBuilder *builder = GTK_BUILDER (data);
-
-  if (gtk_tree_selection_get_selected (selection, &model, &iter))
-    {
-      GPtrArray *keys;
-      GtkWidget *shortcut_treeview;
-      GtkTreeModel *shortcut_model;
-      gchar *id;
-      BindingGroupType group;
-      gint i;
-
-      gtk_tree_model_get (model, &iter,
-                          SECTION_ID_COLUMN, &id,
-                          SECTION_GROUP_COLUMN, &group, -1);
-
-      keys = g_hash_table_lookup (get_hash_for_group (group), id);
-      if (keys == NULL)
-        {
-          g_warning ("Can't find section %s in sections hash table.", id);
-          g_free (id);
-          return;
-        }
-
-      gtk_widget_set_sensitive (WID (builder, "remove-toolbutton"), FALSE);
-
-      /* Fill the shortcut treeview with the keys for the selected section */
-      shortcut_treeview = GTK_WIDGET (gtk_builder_get_object (builder, "shortcut_treeview"));
-      shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (shortcut_treeview));
-      gtk_list_store_clear (GTK_LIST_STORE (shortcut_model));
-
-      for (i = 0; i < keys->len; i++)
-        {
-          GtkTreeIter new_row;
-          CcKeyboardItem *item = g_ptr_array_index (keys, i);
-
-          if (!cc_keyboard_item_is_hidden (item))
-            {
-              gtk_list_store_append (GTK_LIST_STORE (shortcut_model), &new_row);
-              gtk_list_store_set (GTK_LIST_STORE (shortcut_model), &new_row,
-                                  DETAIL_DESCRIPTION_COLUMN, item->description,
-                                  DETAIL_KEYENTRY_COLUMN, item,
-                                  DETAIL_TYPE_COLUMN, SHORTCUT_TYPE_KEY_ENTRY,
-                                  -1);
-            }
-        }
-
-      if (g_str_equal (id, "Typing"))
-        fill_xkb_options_shortcuts (shortcut_model);
-
-      g_free (id);
-    }
-}
-
-static gboolean
-edit_custom_shortcut (CcKeyboardItem *item)
-{
-  gint result;
-  gboolean ret;
-  GSettings *settings;
-
-  settings = g_settings_new_with_path (item->schema, item->gsettings_path);
-
-  g_settings_bind (settings, "name",
-                   G_OBJECT (custom_shortcut_name_entry), "text",
-                   G_SETTINGS_BIND_DEFAULT);
-  gtk_widget_grab_focus (custom_shortcut_name_entry);
-
-  g_settings_bind (settings, "command",
-                   G_OBJECT (custom_shortcut_command_entry), "text",
-                   G_SETTINGS_BIND_DEFAULT);
-
-  g_settings_delay (settings);
-
-  gtk_widget_set_sensitive (custom_shortcut_name_entry,
-                            item->desc_editable);
-  gtk_widget_set_sensitive (custom_shortcut_command_entry,
-                            item->cmd_editable);
-  gtk_window_present (GTK_WINDOW (custom_shortcut_dialog));
-  result = gtk_dialog_run (GTK_DIALOG (custom_shortcut_dialog));
-  switch (result)
-    {
-    case GTK_RESPONSE_OK:
-      g_settings_apply (settings);
-      ret = TRUE;
-      break;
-    default:
-      g_settings_revert (settings);
-      ret = FALSE;
-      break;
-    }
-
-  g_settings_unbind (G_OBJECT (custom_shortcut_name_entry), "text");
-  g_settings_unbind (G_OBJECT (custom_shortcut_command_entry), "text");
-
-  g_object_unref (settings);
-
-  gtk_widget_hide (custom_shortcut_dialog);
-
-  return ret;
-}
-
-static gboolean
-remove_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
-{
-  CcKeyboardItem *item;
-  GPtrArray *keys_array;
-  GVariantBuilder builder;
-  char **settings_paths;
-  int i;
-
-  gtk_tree_model_get (model, iter,
-                      DETAIL_KEYENTRY_COLUMN, &item,
-                      -1);
-
-  /* not a custom shortcut */
-  g_assert (item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH);
-
-  g_settings_delay (item->settings);
-  g_settings_reset (item->settings, "name");
-  g_settings_reset (item->settings, "command");
-  g_settings_reset (item->settings, "binding");
-  g_settings_apply (item->settings);
-  g_settings_sync ();
-
-  settings_paths = g_settings_get_strv (binding_settings, "custom-keybindings");
-  g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
-  for (i = 0; settings_paths[i]; i++)
-    if (strcmp (settings_paths[i], item->gsettings_path) != 0)
-      g_variant_builder_add (&builder, "s", settings_paths[i]);
-  g_settings_set_value (binding_settings,
-                        "custom-keybindings", g_variant_builder_end (&builder));
-  g_strfreev (settings_paths);
-  g_object_unref (item);
-
-  keys_array = g_hash_table_lookup (get_hash_for_group (BINDING_GROUP_USER), CUSTOM_SHORTCUTS_ID);
-  g_ptr_array_remove (keys_array, item);
-
-  gtk_list_store_remove (GTK_LIST_STORE (model), iter);
-
-  return TRUE;
-}
-
-static void
-update_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
-{
-  CcKeyboardItem *item;
-
-  gtk_tree_model_get (model, iter,
-                      DETAIL_KEYENTRY_COLUMN, &item,
-                      -1);
-
-  g_assert (item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH);
-
-  edit_custom_shortcut (item);
-  if (item->command == NULL || item->command[0] == '\0')
-    {
-      remove_custom_shortcut (model, iter);
-    }
-  else
-    {
-      gtk_list_store_set (GTK_LIST_STORE (model), iter,
-                          DETAIL_KEYENTRY_COLUMN, item, -1);
-    }
-}
-
-static gboolean
-start_editing_cb (GtkTreeView    *tree_view,
-                  GdkEventButton *event,
-                  gpointer        user_data)
-{
-  GtkTreePath *path;
-  GtkTreeViewColumn *column;
-  GtkCellRenderer *cell = user_data;
-
-  if (event->window != gtk_tree_view_get_bin_window (tree_view))
-    return FALSE;
-
-  if (gtk_tree_view_get_path_at_pos (tree_view,
-                                     (gint) event->x,
-                                     (gint) event->y,
-                                     &path, &column,
-                                     NULL, NULL))
-    {
-      GtkTreeModel *model;
-      GtkTreeIter iter;
-      CcKeyboardItem *item;
-      ShortcutType type;
-
-      model = gtk_tree_view_get_model (tree_view);
-      gtk_tree_model_get_iter (model, &iter, path);
-      gtk_tree_model_get (model, &iter,
-                          DETAIL_KEYENTRY_COLUMN, &item,
-                          DETAIL_TYPE_COLUMN, &type,
-                         -1);
-
-      if (type == SHORTCUT_TYPE_XKB_OPTION)
-        {
-          gtk_tree_path_free (path);
-          return FALSE;
-        }
-
-      /* if only the accel can be edited on the selected row
-       * always select the accel column */
-      if (item->desc_editable &&
-          column == gtk_tree_view_get_column (tree_view, 0))
-        {
-          gtk_widget_grab_focus (GTK_WIDGET (tree_view));
-          gtk_tree_view_set_cursor (tree_view,
-                                    path,
-                                    column,
-                                    FALSE);
-          update_custom_shortcut (model, &iter);
-        }
-      else
-        {
-          gtk_widget_grab_focus (GTK_WIDGET (tree_view));
-          gtk_tree_view_set_cursor_on_cell (tree_view,
-                                            path,
-                                            gtk_tree_view_get_column (tree_view, 1),
-                                            cell,
-                                            TRUE);
-        }
-      g_signal_stop_emission_by_name (tree_view, "button_press_event");
-      gtk_tree_path_free (path);
-    }
-  return TRUE;
-}
-
-static void
-start_editing_kb_cb (GtkTreeView       *treeview,
-                     GtkTreePath       *path,
-                     GtkTreeViewColumn *column,
-                     gpointer           user_data)
-{
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  CcKeyboardItem *item;
-  ShortcutType type;
-  GtkCellRenderer *cell = user_data;
-
-  model = gtk_tree_view_get_model (treeview);
-  gtk_tree_model_get_iter (model, &iter, path);
-  gtk_tree_model_get (model, &iter,
-                      DETAIL_KEYENTRY_COLUMN, &item,
-                      DETAIL_TYPE_COLUMN, &type,
-                      -1);
-
-  if (type == SHORTCUT_TYPE_XKB_OPTION)
-    return;
-
-  /* if only the accel can be edited on the selected row
-   * always select the accel column */
-  if (item->desc_editable &&
-      column == gtk_tree_view_get_column (treeview, 0))
-    {
-      gtk_widget_grab_focus (GTK_WIDGET (treeview));
-      gtk_tree_view_set_cursor (treeview,
-                                path,
-                                column,
-                                FALSE);
-      update_custom_shortcut (model, &iter);
-    }
-  else
-    {
-       gtk_widget_grab_focus (GTK_WIDGET (treeview));
-       gtk_tree_view_set_cursor_on_cell (treeview,
-                                         path,
-                                         gtk_tree_view_get_column (treeview, 1),
-                                         cell,
-                                         TRUE);
-    }
-}
-
 static const guint forbidden_keyvals[] = {
   /* Navigation keys */
   GDK_KEY_Home,
@@ -1157,20 +252,6 @@ static const guint forbidden_keyvals[] = {
   GDK_KEY_Mode_switch
 };
 
-static char*
-binding_name (guint                   keyval,
-              guint                   keycode,
-              GdkModifierType         mask,
-              gboolean                translate)
-{
-  if (keyval != 0 || keycode != 0)
-    return translate ?
-        gtk_accelerator_get_label_with_keycode (NULL, keyval, keycode, mask) :
-        gtk_accelerator_name_with_keycode (NULL, keyval, keycode, mask);
-  else
-    return g_strdup (translate ? _("Disabled") : NULL);
-}
-
 static gboolean
 keyval_is_forbidden (guint keyval)
 {
@@ -1184,81 +265,7 @@ keyval_is_forbidden (guint keyval)
   return FALSE;
 }
 
-static gboolean
-compare_keys_for_uniqueness (CcKeyboardItem   *element,
-                             CcUniquenessData *data)
-{
-  CcKeyboardItem *orig_item;
-
-  orig_item = data->orig_item;
-
-  /* no conflict for : blanks, different modifiers, or ourselves */
-  if (element == NULL || data->new_mask != element->mask ||
-      cc_keyboard_item_equal (orig_item, element))
-    return FALSE;
-
-  if (data->new_keyval != 0) {
-      if (data->new_keyval != element->keyval)
-          return FALSE;
-  } else if (element->keyval != 0 || data->new_keycode != element->keycode)
-    return FALSE;
-
-  data->conflict_item = element;
-
-  return TRUE;
-}
-
-static gboolean
-cb_check_for_uniqueness (gpointer          key,
-                         GPtrArray        *keys_array,
-                         CcUniquenessData *data)
-{
-  guint i;
-
-  for (i = 0; i < keys_array->len; i++)
-    {
-      CcKeyboardItem *item;
-
-      item = keys_array->pdata[i];
-      if (compare_keys_for_uniqueness (item, data))
-        return TRUE;
-    }
-  return FALSE;
-}
-
-static CcKeyboardItem *
-search_for_conflict_item (CcKeyboardItem *item,
-                          guint           keyval,
-                          GdkModifierType mask,
-                          guint           keycode)
-{
-  CcUniquenessData data;
-
-  data.orig_item = item;
-  data.new_keyval = keyval;
-  data.new_mask = mask;
-  data.new_keycode = keycode;
-  data.conflict_item = NULL;
-
-  if (keyval != 0 || keycode != 0) /* any number of shortcuts can be disabled */
-    {
-      BindingGroupType i;
-
-      for (i = BINDING_GROUP_SYSTEM; i <= BINDING_GROUP_USER && data.conflict_item == NULL; i++)
-        {
-          GHashTable *table;
-
-          table = get_hash_for_group (i);
-          if (!table)
-            continue;
-          g_hash_table_find (table, (GHRFunc) cb_check_for_uniqueness, &data);
-        }
-    }
-
-  return data.conflict_item;
-}
-
-static gboolean
+gboolean
 is_valid_binding (guint                  keyval,
                   GdkModifierType        mask,
                   guint                  keycode)
@@ -1283,316 +290,14 @@ is_valid_binding (guint                  keyval,
   return TRUE;
 }
 
-static GtkResponseType
-show_invalid_binding_dialog (GtkTreeView           *view,
-                             guint                  keyval,
-                             GdkModifierType        mask,
-                             guint                  keycode)
-{
-  GtkWidget *dialog;
-  char *name;
-
-  name = binding_name (keyval, keycode, mask, TRUE);
-
-  dialog =
-    gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
-                            GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
-                            GTK_MESSAGE_WARNING,
-                            GTK_BUTTONS_CANCEL,
-                            _("The shortcut \"%s\" cannot be used because it will become impossible to type 
using this key.\n"
-                            "Please try with a key such as Control, Alt or Shift at the same time."),
-                            name);
-
-  g_free (name);
-  gtk_dialog_run (GTK_DIALOG (dialog));
-  gtk_widget_destroy (dialog);
-
-  return GTK_RESPONSE_NONE;
-}
-
-static GtkResponseType
-show_conflict_item_dialog (GtkTreeView           *view,
-                           CcKeyboardItem        *item,
-                           CcKeyboardItem        *conflict_item,
-                           guint                  keyval,
-                           GdkModifierType        mask,
-                           guint                  keycode)
-{
-  GtkWidget *dialog;
-  char *name;
-  int response;
-
-  name = binding_name (keyval, keycode, mask, TRUE);
-
-  dialog =
-    gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
-                            GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
-                            GTK_MESSAGE_WARNING,
-                            GTK_BUTTONS_CANCEL,
-                            _("The shortcut \"%s\" is already used for\n\"%s\""),
-                            name, conflict_item->description);
-  g_free (name);
-
-  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-      _("If you reassign the shortcut to \"%s\", the \"%s\" shortcut "
-        "will be disabled."),
-      item->description,
-      conflict_item->description);
-
-  gtk_dialog_add_button (GTK_DIALOG (dialog),
-                         _("_Reassign"),
-                         GTK_RESPONSE_ACCEPT);
-
-  gtk_dialog_set_default_response (GTK_DIALOG (dialog),
-                                   GTK_RESPONSE_ACCEPT);
-
-  response = gtk_dialog_run (GTK_DIALOG (dialog));
-  gtk_widget_destroy (dialog);
-
-  return response;
-}
-
-static GtkResponseType
-show_reverse_item_dialog (GtkTreeView           *view,
-                          CcKeyboardItem        *item,
-                          CcKeyboardItem        *reverse_item,
-                          CcKeyboardItem        *reverse_conflict_item,
-                          guint                  keyval,
-                          GdkModifierType        mask,
-                          guint                  keycode)
-{
-  GtkWidget *dialog;
-  char *name;
-  int response;
-
-  name = binding_name (keyval, keycode, mask, TRUE);
-
-  /* translators:
-   * This is the text you get in a dialogue when an action has an associated
-   * "reverse" action, for example Alt+Tab going in the opposite direction to
-   * Alt+Shift+Tab.
-   *
-   * An example text would be:
-   * The "Switch to next input source" shortcut has an associated "Switch to
-   * previous input source" shortcut. Do you want to automatically set it to
-   * "Shift+Ctrl+Alt+Space"? */
-  dialog =
-    gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
-                            GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
-                            GTK_MESSAGE_WARNING,
-                            GTK_BUTTONS_CANCEL,
-                            _("The \"%s\" shortcut has an associated \"%s\" shortcut. "
-                              "Do you want to automatically set it to \"%s\"?"),
-                            item->description, reverse_item->description, name);
-
-  if (reverse_conflict_item != NULL) {
-    /* translators:
-     * This is the text you get in a dialogue when you try to use a shortcut
-     * that was already associated with another action, for example:
-     * "Alt+F4" is currently associated with "Close Window", ... */
-    gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-        _("\"%s\" is currently associated with \"%s\", this shortcut will be"
-          " disabled if you move forward."),
-        name, reverse_conflict_item->description);
-  }
-  g_free (name);
-
-  gtk_dialog_add_button (GTK_DIALOG (dialog),
-                         _("_Assign"),
-                         GTK_RESPONSE_ACCEPT);
-
-  gtk_dialog_set_default_response (GTK_DIALOG (dialog),
-                                   GTK_RESPONSE_ACCEPT);
-
-  response = gtk_dialog_run (GTK_DIALOG (dialog));
-  gtk_widget_destroy (dialog);
-
-  return response;
-}
-
-static void
-handle_reverse_item (CcKeyboardItem        *item,
-                     CcKeyboardItem        *reverse_item,
-                     guint                  keyval,
-                     GdkModifierType        mask,
-                     guint                  keycode,
-                     GtkTreeView           *view)
-{
-  GtkResponseType response;
-  GdkModifierType reverse_mask;
-
-  reverse_mask = mask ^ GDK_SHIFT_MASK;
-
-  if (!is_valid_binding(keyval, reverse_mask, keycode))
-    return;
-
-  if ((reverse_item->keyval != keyval)
-       || (reverse_item->keycode != keycode)
-       || (reverse_item->mask != reverse_mask))
-    {
-      CcKeyboardItem *reverse_conflict_item;
-      char *binding_str;
-
-      reverse_conflict_item = search_for_conflict_item (reverse_item, keyval,
-                                                        reverse_mask,
-                                                        keycode);
-
-      response = show_reverse_item_dialog (view, item, reverse_item,
-                                           reverse_conflict_item,
-                                           keyval, reverse_mask,
-                                           keycode);
-      if (response == GTK_RESPONSE_ACCEPT)
-        {
-          binding_str = binding_name (keyval, keycode, reverse_mask, FALSE);
-          g_object_set (G_OBJECT (reverse_item), "binding", binding_str, NULL);
-          g_free (binding_str);
-
-          if (reverse_conflict_item != NULL)
-            g_object_set (G_OBJECT (reverse_conflict_item),
-                          "binding", NULL, NULL);
-        }
-      else
-        {
-          /* The existing reverse binding may be conflicting with the binding
-           * we are setting. Other conflicts have already been handled in
-           * accel_edited_callback()
-           */
-          CcKeyboardItem *conflict_item;
-          conflict_item = search_for_conflict_item (item, keyval,
-                                                    mask, keycode);
-          if (conflict_item != NULL)
-            {
-              g_warn_if_fail (conflict_item == reverse_item);
-              g_object_set (G_OBJECT (conflict_item),
-                            "binding", NULL, NULL);
-            }
-        }
-    }
-}
-
-static void
-accel_edited_callback (GtkCellRendererText   *cell,
-                       const char            *path_string,
-                       guint                  keyval,
-                       GdkModifierType        mask,
-                       guint                  keycode,
-                       GtkTreeView           *view)
-{
-  GtkTreeModel *model;
-  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
-  GtkTreeIter iter;
-  CcKeyboardItem *item;
-  CcKeyboardItem *conflict_item;
-  CcKeyboardItem *reverse_item;
-  char *str;
-
-  model = gtk_tree_view_get_model (view);
-  gtk_tree_model_get_iter (model, &iter, path);
-  gtk_tree_path_free (path);
-  gtk_tree_model_get (model, &iter,
-                      DETAIL_KEYENTRY_COLUMN, &item,
-                      -1);
-
-  /* sanity check */
-  if (item == NULL)
-    return;
-
-  /* CapsLock isn't supported as a keybinding modifier, so keep it from confusing us */
-  mask &= ~GDK_LOCK_MASK;
-
-  conflict_item = search_for_conflict_item (item, keyval, mask, keycode);
-
-  /* Check for unmodified keys */
-  if (!is_valid_binding (keyval, mask, keycode))
-    {
-      show_invalid_binding_dialog (view, keyval, mask, keycode);
-
-      /* set it back to its previous value. */
-      g_object_set (G_OBJECT (cell),
-                    "accel-key", item->keyval,
-                    "keycode", item->keycode,
-                    "accel-mods", item->mask,
-                    NULL);
-      return;
-    }
-
-  reverse_item = cc_keyboard_item_get_reverse_item (item);
-
-  /* flag to see if the new accelerator was in use by something */
-  if ((conflict_item != NULL) && (conflict_item != reverse_item))
-    {
-      GtkResponseType response;
-
-      response = show_conflict_item_dialog (view, item, conflict_item,
-                                            keyval, mask, keycode);
-
-      if (response == GTK_RESPONSE_ACCEPT)
-        {
-         g_object_set (G_OBJECT (conflict_item), "binding", NULL, NULL);
-
-          str = binding_name (keyval, keycode, mask, FALSE);
-          g_object_set (G_OBJECT (item), "binding", str, NULL);
-
-          g_free (str);
-          if (reverse_item == NULL)
-            return;
-        }
-      else
-        {
-          /* set it back to its previous value. */
-          g_object_set (G_OBJECT (cell),
-                        "accel-key", item->keyval,
-                        "keycode", item->keycode,
-                        "accel-mods", item->mask,
-                        NULL);
-          return;
-        }
-
-    }
-
-  str = binding_name (keyval, keycode, mask, FALSE);
-  g_object_set (G_OBJECT (item), "binding", str, NULL);
-
-  g_free (str);
-
-  if (reverse_item != NULL)
-    handle_reverse_item (item, reverse_item, keyval, mask, keycode, view);
-}
-
-static void
-accel_cleared_callback (GtkCellRendererText *cell,
-                        const char          *path_string,
-                        gpointer             data)
-{
-  GtkTreeView *view = (GtkTreeView *) data;
-  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
-  CcKeyboardItem *item;
-  GtkTreeIter iter;
-  GtkTreeModel *model;
-
-  model = gtk_tree_view_get_model (view);
-  gtk_tree_model_get_iter (model, &iter, path);
-  gtk_tree_path_free (path);
-  gtk_tree_model_get (model, &iter,
-                      DETAIL_KEYENTRY_COLUMN, &item,
-                      -1);
-
-  /* sanity check */
-  if (item == NULL)
-    return;
-
-  /* Unset the key */
-  g_object_set (G_OBJECT (item), "binding", NULL, NULL);
-}
-
-static gchar *
-find_free_settings_path ()
+gchar*
+find_free_settings_path (GSettings *settings)
 {
   char **used_names;
   char *dir = NULL;
   int i, num, n_names;
 
-  used_names = g_settings_get_strv (binding_settings, "custom-keybindings");
+  used_names = g_settings_get_strv (settings, "custom-keybindings");
   n_names = g_strv_length (used_names);
 
   for (num = 0; dir == NULL; num++)
@@ -1614,219 +319,6 @@ find_free_settings_path ()
   return dir;
 }
 
-static void
-add_custom_shortcut (GtkTreeView  *tree_view,
-                     GtkTreeModel *model)
-{
-  CcKeyboardItem *item;
-  GtkTreePath *path;
-  gchar *settings_path;
-
-  item = cc_keyboard_item_new (CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH);
-
-  settings_path = find_free_settings_path ();
-  cc_keyboard_item_load_from_gsettings_path (item, settings_path, TRUE);
-  g_free (settings_path);
-
-  item->model = model;
-
-  if (edit_custom_shortcut (item) &&
-      item->command && item->command[0])
-    {
-      GPtrArray *keys_array;
-      GtkTreeIter iter;
-      GHashTable *hash;
-      GVariantBuilder builder;
-      char **settings_paths;
-      int i;
-
-      hash = get_hash_for_group (BINDING_GROUP_USER);
-      keys_array = g_hash_table_lookup (hash, CUSTOM_SHORTCUTS_ID);
-      if (keys_array == NULL)
-        {
-          keys_array = g_ptr_array_new ();
-          g_hash_table_insert (hash, g_strdup (CUSTOM_SHORTCUTS_ID), keys_array);
-        }
-
-      g_ptr_array_add (keys_array, item);
-
-      gtk_list_store_append (GTK_LIST_STORE (model), &iter);
-      gtk_list_store_set (GTK_LIST_STORE (model), &iter, DETAIL_KEYENTRY_COLUMN, item, -1);
-
-      settings_paths = g_settings_get_strv (binding_settings, "custom-keybindings");
-      g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
-      for (i = 0; settings_paths[i]; i++)
-        g_variant_builder_add (&builder, "s", settings_paths[i]);
-      g_variant_builder_add (&builder, "s", item->gsettings_path);
-      g_settings_set_value (binding_settings, "custom-keybindings",
-                            g_variant_builder_end (&builder));
-
-      /* make the new shortcut visible */
-      path = gtk_tree_model_get_path (model, &iter);
-      gtk_tree_view_expand_to_path (tree_view, path);
-      gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0, 0);
-      gtk_tree_path_free (path);
-    }
-  else
-    {
-      g_object_unref (item);
-    }
-}
-
-static void
-shortcut_entry_changed (GtkEntry *entry,
-                        gpointer  user_data)
-{
-  guint16 name_length;
-  guint16 command_length;
-
-  name_length = gtk_entry_get_text_length (GTK_ENTRY (custom_shortcut_name_entry));
-  command_length = gtk_entry_get_text_length (GTK_ENTRY (custom_shortcut_command_entry));
-
-  gtk_widget_set_sensitive (custom_shortcut_ok_button,
-                            name_length > 0 && command_length > 0);
-}
-
-static void
-add_button_clicked (GtkWidget  *button,
-                    GtkBuilder *builder)
-{
-  GtkTreeView *treeview;
-  GtkTreeModel *model;
-  GtkTreeModel *section_model;
-  GtkTreeIter iter;
-  gboolean found, cont;
-
-  treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
-                                                    "shortcut_treeview"));
-  model = gtk_tree_view_get_model (treeview);
-
-  /* Select the Custom Shortcuts section
-   * before adding the shortcut itself */
-  section_model = gtk_tree_view_get_model (GTK_TREE_VIEW (WID (builder, "section_treeview")));
-  cont = gtk_tree_model_get_iter_first (section_model, &iter);
-  found = FALSE;
-  while (cont)
-    {
-      BindingGroupType group;
-
-      gtk_tree_model_get (section_model, &iter,
-                          SECTION_GROUP_COLUMN, &group,
-                          -1);
-
-      if (group == BINDING_GROUP_USER)
-        {
-          found = TRUE;
-          break;
-        }
-      cont = gtk_tree_model_iter_next (section_model, &iter);
-    }
-  if (found)
-    {
-      GtkTreeSelection *selection;
-
-      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (WID (builder, "section_treeview")));
-      gtk_tree_selection_select_iter (selection, &iter);
-    }
-
-  /* And add the shortcut */
-  add_custom_shortcut (treeview, model);
-}
-
-static void
-remove_button_clicked (GtkWidget  *button,
-                       GtkBuilder *builder)
-{
-  GtkTreeView *treeview;
-  GtkTreeModel *model;
-  GtkTreeSelection *selection;
-  GtkTreeIter iter;
-
-  treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
-                                                    "shortcut_treeview"));
-  model = gtk_tree_view_get_model (treeview);
-
-  selection = gtk_tree_view_get_selection (treeview);
-  if (gtk_tree_selection_get_selected (selection, NULL, &iter))
-    {
-      remove_custom_shortcut (model, &iter);
-    }
-}
-
-static int
-section_sort_item  (GtkTreeModel *model,
-                    GtkTreeIter  *a,
-                    GtkTreeIter  *b,
-                    gpointer      data)
-{
-  char *a_desc;
-  int   a_group;
-  char *b_desc;
-  int   b_group;
-  int   ret;
-
-  gtk_tree_model_get (model, a,
-                      SECTION_DESCRIPTION_COLUMN, &a_desc,
-                      SECTION_GROUP_COLUMN, &a_group,
-                      -1);
-  gtk_tree_model_get (model, b,
-                      SECTION_DESCRIPTION_COLUMN, &b_desc,
-                      SECTION_GROUP_COLUMN, &b_group,
-                      -1);
-
-  if (a_group == b_group && a_desc && b_desc)
-    ret = g_utf8_collate (a_desc, b_desc);
-  else
-    ret = a_group - b_group;
-
-  g_free (a_desc);
-  g_free (b_desc);
-
-  return ret;
-}
-
-static gboolean
-sections_separator_func (GtkTreeModel *model,
-                         GtkTreeIter  *iter,
-                         gpointer      data)
-{
-  BindingGroupType type;
-
-  gtk_tree_model_get (model, iter, SECTION_GROUP_COLUMN, &type, -1);
-
-  return type == BINDING_GROUP_SEPARATOR;
-}
-
-static void
-xkb_options_combo_changed (GtkCellRendererCombo *combo,
-                           gchar                *model_path,
-                           GtkTreeIter          *model_iter,
-                           gpointer              data)
-{
-  GtkTreeView *shortcut_treeview;
-  GtkTreeModel *shortcut_model;
-  GtkTreeIter shortcut_iter;
-  GtkTreeSelection *selection;
-  CcKeyboardOption *option;
-  ShortcutType type;
-  GtkBuilder *builder = data;
-
-  shortcut_treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "shortcut_treeview"));
-  selection = gtk_tree_view_get_selection (shortcut_treeview);
-  if (!gtk_tree_selection_get_selected (selection, &shortcut_model, &shortcut_iter))
-    return;
-
-  gtk_tree_model_get (shortcut_model, &shortcut_iter,
-                      DETAIL_KEYENTRY_COLUMN, &option,
-                      DETAIL_TYPE_COLUMN, &type,
-                      -1);
-
-  if (type != SHORTCUT_TYPE_XKB_OPTION)
-    return;
-
-  cc_keyboard_option_set_selection (option, model_iter);
-}
-
 static gboolean
 poke_xkb_option_row (GtkTreeModel *model,
                      GtkTreePath  *path,
@@ -1855,7 +347,7 @@ xkb_option_changed (CcKeyboardOption *option,
   gtk_tree_model_foreach (model, poke_xkb_option_row, option);
 }
 
-static void
+void
 setup_keyboard_options (GtkListStore *store)
 {
   GList *l;
@@ -1865,278 +357,43 @@ setup_keyboard_options (GtkListStore *store)
                       G_CALLBACK (xkb_option_changed), store);
 }
 
-static void
-setup_dialog (CcPanel *panel, GtkBuilder *builder)
+KeyList*
+parse_keylist_from_file (const gchar *path)
 {
-  GtkCellRenderer *renderer;
-  GtkTreeViewColumn *column;
-  GtkWidget *widget;
-  GtkTreeView *treeview;
-  GtkTreeSelection *selection;
-  GList *focus_chain;
-  CcShell *shell;
-  GtkListStore *model;
-  GtkTreeModelSort *sort_model;
-  GtkStyleContext *context;
-
-  gtk_widget_set_size_request (GTK_WIDGET (panel), -1, 400);
-
-  /* Setup the section treeview */
-  treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "section_treeview"));
-  gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (treeview),
-                                       sections_separator_func,
-                                       panel,
-                                       NULL);
-
-  renderer = gtk_cell_renderer_text_new ();
-  column = gtk_tree_view_column_new_with_attributes (_("Section"),
-                                                     renderer,
-                                                     "text", SECTION_DESCRIPTION_COLUMN,
-                                                     NULL);
-  g_object_set (renderer,
-                "width-chars", 20,
-                "ellipsize", PANGO_ELLIPSIZE_END,
-                NULL);
-
-  gtk_tree_view_append_column (treeview, column);
-
-  model = gtk_list_store_new (SECTION_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
-  sort_model = GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (model)));
-  gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (sort_model));
-  g_object_unref (model);
-
-  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model),
-                                   SECTION_DESCRIPTION_COLUMN,
-                                   section_sort_item,
-                                   panel,
-                                   NULL);
-
-  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model),
-                                        SECTION_DESCRIPTION_COLUMN,
-                                        GTK_SORT_ASCENDING);
-  g_object_unref (sort_model);
-
-  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
-
-  gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
-
-  g_signal_connect (selection, "changed",
-                    G_CALLBACK (section_selection_changed), builder);
-  section_selection_changed (selection, builder);
-
-  /* Setup the shortcut treeview */
-  treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
-                                                    "shortcut_treeview"));
-
-  binding_settings = g_settings_new (BINDINGS_SCHEMA);
-
-  renderer = gtk_cell_renderer_text_new ();
-  g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-
-  column = gtk_tree_view_column_new_with_attributes (NULL, renderer, NULL);
-  gtk_tree_view_column_set_cell_data_func (column, renderer, description_set_func, NULL, NULL);
-  gtk_tree_view_column_set_resizable (column, FALSE);
-  gtk_tree_view_column_set_expand (column, TRUE);
-
-  gtk_tree_view_append_column (treeview, column);
-
-  renderer = (GtkCellRenderer *) g_object_new (GTK_TYPE_CELL_RENDERER_ACCEL,
-                                               "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
-                                               NULL);
-
-  g_signal_connect (treeview, "button_press_event",
-                    G_CALLBACK (start_editing_cb), renderer);
-  g_signal_connect (treeview, "row-activated",
-                    G_CALLBACK (start_editing_kb_cb), renderer);
-
-  g_signal_connect (renderer, "accel_edited",
-                    G_CALLBACK (accel_edited_callback),
-                    treeview);
-  g_signal_connect (renderer, "accel_cleared",
-                    G_CALLBACK (accel_cleared_callback),
-                    treeview);
-
-  column = gtk_tree_view_column_new_with_attributes (NULL, renderer, NULL);
-  gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
-  gtk_tree_view_column_set_resizable (column, FALSE);
-  gtk_tree_view_column_set_expand (column, FALSE);
-
-  renderer = (GtkCellRenderer *) g_object_new (GTK_TYPE_CELL_RENDERER_COMBO,
-                                               "has-entry", FALSE,
-                                               "text-column", XKB_OPTION_DESCRIPTION_COLUMN,
-                                               "editable", TRUE,
-                                               "ellipsize", PANGO_ELLIPSIZE_END,
-                                               "width-chars", 25,
-                                               NULL);
-  g_signal_connect (renderer, "changed",
-                    G_CALLBACK (xkb_options_combo_changed), builder);
-
-  gtk_tree_view_column_pack_end (column, renderer, FALSE);
-
-  gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
-
-  gtk_tree_view_append_column (treeview, column);
-
-  model = gtk_list_store_new (DETAIL_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
-  gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (model));
-  g_object_unref (model);
-
-  setup_keyboard_options (model);
-
-  widget = GTK_WIDGET (gtk_builder_get_object (builder, "actions_swindow"));
-  context = gtk_widget_get_style_context (widget);
-  gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
-  widget = GTK_WIDGET (gtk_builder_get_object (builder, "shortcut-toolbar"));
-  context = gtk_widget_get_style_context (widget);
-  gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
-
-  /* set up the focus chain */
-  focus_chain = g_list_append (NULL, WID (builder, "sections_swindow"));
-  focus_chain = g_list_append (focus_chain, WID (builder, "actions_swindow"));
-  focus_chain = g_list_append (focus_chain, WID (builder, "shortcut-toolbar"));
-
-  widget = GTK_WIDGET (gtk_builder_get_object (builder, "shortcuts_grid"));
-  gtk_container_set_focus_chain (GTK_CONTAINER (widget), focus_chain);
-  g_list_free (focus_chain);
-
-  /* set up the dialog */
-  shell = cc_panel_get_shell (CC_PANEL (panel));
-  widget = cc_shell_get_toplevel (shell);
-
-  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
-  g_signal_connect (selection, "changed",
-                    G_CALLBACK (shortcut_selection_changed),
-                    WID (builder, "remove-toolbutton"));
-
-  /* setup the custom shortcut dialog */
-  custom_shortcut_dialog = WID (builder,
-                                "custom-shortcut-dialog");
-  custom_shortcut_ok_button = WID (builder, "custom-shortcut-ok-button");
-  custom_shortcut_name_entry = WID (builder,
-                                    "custom-shortcut-name-entry");
-  g_signal_connect (custom_shortcut_name_entry, "changed",
-                    G_CALLBACK (shortcut_entry_changed), NULL);
-  custom_shortcut_command_entry = WID (builder,
-                                       "custom-shortcut-command-entry");
-  g_signal_connect (custom_shortcut_command_entry, "changed",
-                    G_CALLBACK (shortcut_entry_changed), NULL);
-  g_signal_connect (WID (builder, "add-toolbutton"),
-                    "clicked", G_CALLBACK (add_button_clicked), builder);
-  g_signal_connect (WID (builder, "remove-toolbutton"),
-                    "clicked", G_CALLBACK (remove_button_clicked), builder);
-
-  gtk_dialog_set_default_response (GTK_DIALOG (custom_shortcut_dialog),
-                                   GTK_RESPONSE_OK);
-
-  gtk_window_set_transient_for (GTK_WINDOW (custom_shortcut_dialog),
-                                GTK_WINDOW (widget));
-
-  gtk_window_set_resizable (GTK_WINDOW (custom_shortcut_dialog), FALSE);
-}
-
-static void
-on_window_manager_change (const char *wm_name, CcPanel *panel)
-{
-  reload_sections (panel);
-}
-
-void
-keyboard_shortcuts_init (CcPanel *panel, GtkBuilder *builder)
-{
-  g_object_set_data (G_OBJECT (panel), "builder", builder);
-#ifdef GDK_WINDOWING_X11
-  if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
-    wm_changed_id = wm_common_register_window_manager_change ((GFunc) on_window_manager_change,
-                                                              panel);
-#endif
-  pictures_regex = g_regex_new ("\\$PICTURES", 0, 0, NULL);
-  setup_dialog (panel, builder);
-  reload_sections (panel);
-}
-
-gboolean
-keyboard_shortcuts_set_section (CcPanel *panel, const char *section)
-{
-  GtkBuilder *builder;
-  GtkTreeModel *section_model;
-  GtkTreeIter iter;
-  gboolean found, cont;
-
-  builder = g_object_get_data (G_OBJECT (panel), "builder");
-  if (builder == NULL)
-    {
-      /* Remember the section name to be set later */
-      g_object_set_data_full (G_OBJECT (panel), "section-to-set", g_strdup (section), g_free);
-      return TRUE;
-    }
-  section_model = gtk_tree_view_get_model (GTK_TREE_VIEW (WID (builder, "section_treeview")));
-  cont = gtk_tree_model_get_iter_first (section_model, &iter);
-  found = FALSE;
-  while (cont)
-    {
-      char *id;
-
-      gtk_tree_model_get (section_model, &iter,
-                          SECTION_ID_COLUMN, &id,
-                          -1);
+  KeyList *keylist;
+  GError *err = NULL;
+  char *buf;
+  gsize buf_len;
+  guint i;
 
-      if (g_strcmp0 (id, section) == 0)
-        {
-          found = TRUE;
-          g_free (id);
-          break;
-        }
-      g_free (id);
-      cont = gtk_tree_model_iter_next (section_model, &iter);
-    }
-  if (found)
-    {
-      GtkTreeSelection *selection;
+  GMarkupParseContext *ctx;
+  GMarkupParser parser = { parse_start_tag, NULL, NULL, NULL, NULL };
 
-      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (WID (builder, "section_treeview")));
-      gtk_tree_selection_select_iter (selection, &iter);
-    }
-  else
-    {
-      g_warning ("Could not find section '%s' to switch to.", section);
-    }
+  /* Parse file */
+  if (!g_file_get_contents (path, &buf, &buf_len, &err))
+    return NULL;
 
-  return found;
-}
+  keylist = g_new0 (KeyList, 1);
+  keylist->entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
+  ctx = g_markup_parse_context_new (&parser, 0, keylist, NULL);
 
-void
-keyboard_shortcuts_dispose (CcPanel *panel)
-{
-  if (kb_system_sections != NULL)
-    {
-      g_hash_table_destroy (kb_system_sections);
-      kb_system_sections = NULL;
-    }
-  if (kb_apps_sections != NULL)
-    {
-      g_hash_table_destroy (kb_apps_sections);
-      kb_apps_sections = NULL;
-    }
-  if (kb_user_sections != NULL)
-    {
-      g_hash_table_destroy (kb_user_sections);
-      kb_user_sections = NULL;
-    }
-  if (pictures_regex != NULL)
+  if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err))
     {
-      g_regex_unref (pictures_regex);
-      pictures_regex = NULL;
-    }
-
-  g_clear_object (&binding_settings);
+      g_warning ("Failed to parse '%s': '%s'", path, err->message);
+      g_error_free (err);
+      g_free (keylist->name);
+      g_free (keylist->package);
+      g_free (keylist->wm_name);
 
-  g_clear_pointer (&custom_shortcut_dialog, gtk_widget_destroy);
+      for (i = 0; i < keylist->entries->len; i++)
+        g_free (((KeyListEntry *) &(keylist->entries->data[i]))->name);
 
-  if (wm_changed_id)
-    {
-      wm_common_unregister_window_manager_change (wm_changed_id);
-      wm_changed_id = NULL;
+      g_array_free (keylist->entries, TRUE);
+      g_free (keylist);
+      keylist = NULL;
     }
+  g_markup_parse_context_free (ctx);
+  g_free (buf);
 
-  cc_keyboard_option_clear_all ();
+  return keylist;
 }
diff --git a/panels/keyboard/keyboard-shortcuts.h b/panels/keyboard/keyboard-shortcuts.h
index 02564b1..4631b34 100644
--- a/panels/keyboard/keyboard-shortcuts.h
+++ b/panels/keyboard/keyboard-shortcuts.h
@@ -79,6 +79,14 @@ enum
   SECTION_N_COLUMNS
 };
 
-void keyboard_shortcuts_init (CcPanel *panel, GtkBuilder *builder);
-gboolean keyboard_shortcuts_set_section (CcPanel *panel, const char *section);
-void keyboard_shortcuts_dispose (CcPanel *panel);
+gchar*   find_free_settings_path        (GSettings *settings);
+
+void     fill_xkb_options_shortcuts     (GtkTreeModel *model);
+
+void     setup_keyboard_options         (GtkListStore *store);
+
+gboolean is_valid_binding               (guint           keyval,
+                                         GdkModifierType mask,
+                                         guint           keycode);
+
+KeyList* parse_keylist_from_file        (const gchar *path);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]