[gnome-control-center/wip/gbsneto/new-keyboard-panel: 3/9] keyboard: introduce CcKeyboardShortcutEditor



commit d0d83f4fa7b2aef6b314ee7edc1367852e71a6bf
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Fri Jul 8 00:35:38 2016 -0300

    keyboard: introduce CcKeyboardShortcutEditor
    
    The current CcKeyboardPanel used to manage keyboard
    editing through GtkCellRendererAccel, which was replaced
    when we moved to use a GtkListBox. Because of that,
    the ability to edit shortcuts is now missing.
    
    Readd shortcut editing capabilities through a new dialog,
    which is done according to the latest mockups available.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=769063

 panels/keyboard/Makefile.am                   |    4 +
 panels/keyboard/cc-keyboard-manager.c         |  818 +++++++++++++
 panels/keyboard/cc-keyboard-manager.h         |   51 +
 panels/keyboard/cc-keyboard-panel.c           | 1533 +------------------------
 panels/keyboard/cc-keyboard-panel.h           |    2 +
 panels/keyboard/cc-keyboard-shortcut-editor.c |  698 +++++++++++
 panels/keyboard/cc-keyboard-shortcut-editor.h |   56 +
 panels/keyboard/gnome-keyboard-panel.ui       |  144 ---
 panels/keyboard/keyboard.gresource.xml        |    1 +
 panels/keyboard/shortcut-editor.ui            |  262 +++++
 po/POTFILES.in                                |    2 +
 11 files changed, 1936 insertions(+), 1635 deletions(-)
---
diff --git a/panels/keyboard/Makefile.am b/panels/keyboard/Makefile.am
index 3f9a86b..6125ad7 100644
--- a/panels/keyboard/Makefile.am
+++ b/panels/keyboard/Makefile.am
@@ -9,12 +9,16 @@ BUILT_SOURCES =                       \
 
 libkeyboard_la_SOURCES =   \
        $(BUILT_SOURCES)                \
+       cc-keyboard-manager.c           \
+       cc-keyboard-manager.h           \
        cc-keyboard-panel.c             \
        cc-keyboard-panel.h             \
        cc-keyboard-item.c              \
        cc-keyboard-item.h              \
        cc-keyboard-option.c            \
        cc-keyboard-option.h            \
+       cc-keyboard-shortcut-editor.c   \
+       cc-keyboard-shortcut-editor.h   \
        cc-shortcut-label.c             \
        cc-shortcut-label.h             \
        wm-common.c                     \
diff --git a/panels/keyboard/cc-keyboard-manager.c b/panels/keyboard/cc-keyboard-manager.c
new file mode 100644
index 0000000..bd0f480
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-manager.c
@@ -0,0 +1,818 @@
+/*
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Thomas Wood <thomas wood intel com>
+ *         Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ */
+
+#include <glib/gi18n.h>
+
+#include "cc-keyboard-manager.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 _CcKeyboardManager
+{
+  GObject             parent;
+
+  GtkListStore       *shortcuts_model;
+  GtkListStore       *sections_store;
+
+  GHashTable         *kb_system_sections;
+  GHashTable         *kb_apps_sections;
+  GHashTable         *kb_user_sections;
+
+  GSettings          *binding_settings;
+
+  gpointer            wm_changed_id;
+};
+
+G_DEFINE_TYPE (CcKeyboardManager, cc_keyboard_manager, G_TYPE_OBJECT)
+
+enum
+{
+  SHORTCUT_ADDED,
+  SHORTCUT_CHANGED,
+  SHORTCUT_REMOVED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+/*
+ * Auxiliary methos
+ */
+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 (CcKeyboardManager *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 (CcKeyboardManager *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
+add_shortcuts (CcKeyboardManager *self)
+{
+  GtkTreeModel *sections_model;
+  GtkTreeIter sections_iter;
+  gboolean can_continue;
+
+  sections_model = GTK_TREE_MODEL (self->sections_store);
+  can_continue = gtk_tree_model_get_iter_first (sections_model, &sections_iter);
+
+  while (can_continue)
+    {
+      BindingGroupType group;
+      GPtrArray *keys;
+      gchar *id, *title;
+      gint i;
+
+      gtk_tree_model_get (sections_model,
+                          &sections_iter,
+                          SECTION_DESCRIPTION_COLUMN, &title,
+                          SECTION_GROUP_COLUMN, &group,
+                          SECTION_ID_COLUMN, &id,
+                          -1);
+
+      /* Ignore separators */
+      if (group == BINDING_GROUP_SEPARATOR)
+        {
+          can_continue = gtk_tree_model_iter_next (sections_model, &sections_iter);
+          continue;
+        }
+
+      keys = g_hash_table_lookup (get_hash_for_group (self, group), id);
+
+      for (i = 0; i < keys->len; i++)
+        {
+          CcKeyboardItem *item = g_ptr_array_index (keys, i);
+
+          if (!cc_keyboard_item_is_hidden (item))
+            {
+              GtkTreeIter new_row;
+
+              gtk_list_store_append (self->shortcuts_model, &new_row);
+              gtk_list_store_set (self->shortcuts_model,
+                                  &new_row,
+                                  DETAIL_DESCRIPTION_COLUMN, item->description,
+                                  DETAIL_KEYENTRY_COLUMN, item,
+                                  DETAIL_TYPE_COLUMN, SHORTCUT_TYPE_KEY_ENTRY,
+                                  -1);
+
+              g_signal_emit (self, signals[SHORTCUT_ADDED],
+                             0,
+                             item,
+                             id,
+                             title);
+            }
+        }
+
+      can_continue = gtk_tree_model_iter_next (sections_model, &sections_iter);
+
+      g_free (title);
+      g_free (id);
+    }
+}
+
+static void
+append_section (CcKeyboardManager  *self,
+                const gchar        *title,
+                const gchar        *id,
+                BindingGroupType    group,
+                const KeyListEntry *keys_list)
+{
+  GtkTreeModel *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;
+
+  shortcut_model = GTK_TREE_MODEL (self->shortcuts_model);
+
+  /* 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);
+
+  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;
+      item->group = group;
+
+      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 (self->sections_store), &iter);
+      gtk_list_store_set (GTK_LIST_STORE (self->sections_store),
+                          &iter,
+                          SECTION_DESCRIPTION_COLUMN, title,
+                          SECTION_ID_COLUMN, id,
+                          SECTION_GROUP_COLUMN, group,
+                          -1);
+    }
+}
+
+static void
+append_sections_from_file (CcKeyboardManager  *self,
+                           const gchar        *path,
+                           const char         *datadir,
+                           gchar             **wm_keybindings)
+{
+  KeyList *keylist;
+  KeyListEntry *keys;
+  KeyListEntry key = { 0, 0, 0, 0, 0, 0, 0 };
+  const char *title;
+  int group;
+  guint i;
+
+  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 (CcKeyboardManager *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++)
+    {
+      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 (CcKeyboardManager *self)
+{
+  GtkTreeModel *shortcut_model;
+  GHashTable *loaded_files;
+  GDir *dir;
+  gchar *default_wm_keybindings[] = { "Mutter", "GNOME Shell", NULL };
+  gchar **wm_keybindings;
+  const gchar * const * data_dirs;
+  guint i;
+
+  shortcut_model = GTK_TREE_MODEL (self->shortcuts_model);
+
+  /* Clear previous models and hash tables */
+  gtk_list_store_clear (GTK_LIST_STORE (self->sections_store));
+  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);
+
+  /* Load custom keybindings */
+  append_sections_from_gsettings (self);
+}
+
+/*
+ * Callbacks
+ */
+static void
+on_window_manager_change (const char        *wm_name,
+                          CcKeyboardManager *self)
+{
+  reload_sections (self);
+}
+
+static void
+cc_keyboard_manager_finalize (GObject *object)
+{
+  CcKeyboardManager *self = (CcKeyboardManager *)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_object (&self->binding_settings);
+  g_clear_object (&self->shortcuts_model);
+
+  g_clear_pointer (&self->wm_changed_id, wm_common_unregister_window_manager_change);
+
+  G_OBJECT_CLASS (cc_keyboard_manager_parent_class)->finalize (object);
+}
+
+static void
+cc_keyboard_manager_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+cc_keyboard_manager_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+cc_keyboard_manager_class_init (CcKeyboardManagerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = cc_keyboard_manager_finalize;
+  object_class->get_property = cc_keyboard_manager_get_property;
+  object_class->set_property = cc_keyboard_manager_set_property;
+
+  /**
+   * CcKeyboardManager:shortcut-added:
+   *
+   * Emited when a shortcut is added.
+   */
+  signals[SHORTCUT_ADDED] = g_signal_new ("shortcut-added",
+                                          CC_TYPE_KEYBOARD_MANAGER,
+                                          G_SIGNAL_RUN_FIRST,
+                                          0, NULL, NULL, NULL,
+                                          G_TYPE_NONE,
+                                          3,
+                                          CC_TYPE_KEYBOARD_ITEM,
+                                          G_TYPE_STRING,
+                                          G_TYPE_STRING);
+
+  /**
+   * CcKeyboardManager:shortcut-changed:
+   *
+   * Emited when a shortcut is added.
+   */
+  signals[SHORTCUT_CHANGED] = g_signal_new ("shortcut-changed",
+                                            CC_TYPE_KEYBOARD_MANAGER,
+                                            G_SIGNAL_RUN_FIRST,
+                                            0, NULL, NULL, NULL,
+                                            G_TYPE_NONE,
+                                            1,
+                                            CC_TYPE_KEYBOARD_ITEM);
+
+
+  /**
+   * CcKeyboardManager:shortcut-removed:
+   *
+   * Emited when a shortcut is removed.
+   */
+  signals[SHORTCUT_REMOVED] = g_signal_new ("shortcut-removed",
+                                            CC_TYPE_KEYBOARD_MANAGER,
+                                            G_SIGNAL_RUN_FIRST,
+                                            0, NULL, NULL, NULL,
+                                            G_TYPE_NONE,
+                                            1,
+                                            CC_TYPE_KEYBOARD_ITEM);
+}
+
+static void
+cc_keyboard_manager_init (CcKeyboardManager *self)
+{
+  /* Bindings */
+  self->binding_settings = g_settings_new (BINDINGS_SCHEMA);
+
+  /* Setup the section models */
+  self->sections_store = gtk_list_store_new (SECTION_N_COLUMNS,
+                                             G_TYPE_STRING,
+                                             G_TYPE_STRING,
+                                             G_TYPE_INT);
+
+  self->shortcuts_model = gtk_list_store_new (DETAIL_N_COLUMNS,
+                                              G_TYPE_STRING,
+                                              G_TYPE_POINTER,
+                                              G_TYPE_INT);
+
+#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
+}
+
+
+CcKeyboardManager *
+cc_keyboard_manager_new (void)
+{
+  return g_object_new (CC_TYPE_KEYBOARD_MANAGER, NULL);
+}
+
+void
+cc_keyboard_manager_load_shortcuts (CcKeyboardManager *self)
+{
+  g_return_if_fail (CC_IS_KEYBOARD_MANAGER (self));
+
+  reload_sections (self);
+  add_shortcuts (self);
+}
+
+/**
+ * cc_keyboard_manager_create_custom_shortcut:
+ * @self: a #CcKeyboardPanel
+ *
+ * Creates a new temporary keyboard shortcut.
+ *
+ * Returns: (transfer full): a #CcKeyboardItem
+ */
+CcKeyboardItem*
+cc_keyboard_manager_create_custom_shortcut (CcKeyboardManager *self)
+{
+  CcKeyboardItem *item;
+  gchar *settings_path;
+
+  g_return_val_if_fail (CC_IS_KEYBOARD_MANAGER (self), NULL);
+
+  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 = GTK_TREE_MODEL (self->shortcuts_model);
+  item->group = BINDING_GROUP_USER;
+
+  return item;
+}
+
+/**
+ * cc_keyboard_manager_add_custom_shortcut:
+ * @self: a #CcKeyboardPanel
+ * @item: the #CcKeyboardItem to be added
+ *
+ * Effectively adds the custom shortcut.
+ */
+void
+cc_keyboard_manager_add_custom_shortcut (CcKeyboardManager *self,
+                                         CcKeyboardItem    *item)
+{
+  GPtrArray *keys_array;
+  GtkTreeIter iter;
+  GHashTable *hash;
+  GVariantBuilder builder;
+  char **settings_paths;
+  int i;
+
+  g_return_if_fail (CC_IS_KEYBOARD_MANAGER (self));
+
+  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 (self->shortcuts_model, &iter);
+  gtk_list_store_set (self->shortcuts_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));
+
+  g_signal_emit (self, signals[SHORTCUT_ADDED],
+                 0,
+                 item,
+                 CUSTOM_SHORTCUTS_ID,
+                 _("Custom Shortcuts"));
+}
+
+/**
+ * cc_keyboard_manager_remove_custom_shortcut:
+ * @self: a #CcKeyboardPanel
+ * @item: the #CcKeyboardItem to be added
+ *
+ * Removed the custom shortcut.
+ */
+void
+cc_keyboard_manager_remove_custom_shortcut  (CcKeyboardManager *self,
+                                             CcKeyboardItem    *item)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  GPtrArray *keys_array;
+  GVariantBuilder builder;
+  gboolean valid;
+  char **settings_paths;
+  int i;
+
+  g_return_if_fail (CC_IS_KEYBOARD_MANAGER (self));
+
+  model = GTK_TREE_MODEL (self->shortcuts_model);
+  valid = gtk_tree_model_get_iter_first (model, &iter);
+
+  /* Search for the iter */
+  while (valid)
+    {
+      CcKeyboardItem  *current_item;
+
+      gtk_tree_model_get (model, &iter,
+                          DETAIL_KEYENTRY_COLUMN, &current_item,
+                          -1);
+
+      if (current_item == item)
+        break;
+
+      valid = gtk_tree_model_iter_next (model, &iter);
+    }
+
+  /* Shortcut not found or not a custom shortcut */
+  g_assert (valid);
+  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);
+
+  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);
+
+  g_signal_emit (self, signals[SHORTCUT_REMOVED], 0, item);
+}
diff --git a/panels/keyboard/cc-keyboard-manager.h b/panels/keyboard/cc-keyboard-manager.h
new file mode 100644
index 0000000..ab14d4a
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-manager.h
@@ -0,0 +1,51 @@
+/*
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Thomas Wood <thomas wood intel com>
+ *         Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ */
+
+#ifndef CC_KEYBOARD_MANAGER_H
+#define CC_KEYBOARD_MANAGER_H
+
+#include <glib-object.h>
+
+#include "cc-keyboard-item.h"
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_KEYBOARD_MANAGER (cc_keyboard_manager_get_type())
+
+G_DECLARE_FINAL_TYPE (CcKeyboardManager, cc_keyboard_manager, CC, KEYBOARD_MANAGER, GObject)
+
+CcKeyboardManager*   cc_keyboard_manager_new                     (void);
+
+void                 cc_keyboard_manager_load_shortcuts          (CcKeyboardManager  *self);
+
+CcKeyboardItem*      cc_keyboard_manager_create_custom_shortcut  (CcKeyboardManager  *self);
+
+void                 cc_keyboard_manager_add_custom_shortcut     (CcKeyboardManager  *self,
+                                                                  CcKeyboardItem     *item);
+
+void                 cc_keyboard_manager_remove_custom_shortcut  (CcKeyboardManager  *self,
+                                                                  CcKeyboardItem     *item);
+
+G_END_DECLS
+
+#endif /* CC_KEYBOARD_MANAGER_H */
+
diff --git a/panels/keyboard/cc-keyboard-panel.c b/panels/keyboard/cc-keyboard-panel.c
index 64602fc..e423937 100644
--- a/panels/keyboard/cc-keyboard-panel.c
+++ b/panels/keyboard/cc-keyboard-panel.c
@@ -23,19 +23,13 @@
 #include <glib/gi18n.h>
 
 #include "cc-keyboard-item.h"
+#include "cc-keyboard-manager.h"
 #include "cc-keyboard-option.h"
 #include "cc-keyboard-panel.h"
 #include "cc-keyboard-resources.h"
+#include "cc-keyboard-shortcut-editor.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"
 
 typedef struct {
   CcKeyboardItem *item;
@@ -47,29 +41,17 @@ struct _CcKeyboardPanel
 {
   CcPanel             parent;
 
-  /* Shortcut models */
-  GtkListStore       *shortcuts_model;
-  GtkListStore       *sections_store;
-  GtkTreeModel       *sections_model;
+  /* Shortcuts */
   GtkWidget          *listbox;
   GtkListBoxRow      *add_shortcut_row;
   GtkSizeGroup       *accelerator_sizegroup;
 
   /* 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;
+  GtkWidget          *shortcut_editor;
 
   GRegex             *pictures_regex;
 
-  gpointer            wm_changed_id;
+  CcKeyboardManager  *manager;
 };
 
 CC_PANEL_REGISTER (CcKeyboardPanel, cc_keyboard_panel)
@@ -309,1451 +291,29 @@ header_function (GtkListBoxRow *row,
     }
 }
 
-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
-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 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 *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;
-
-  shortcut_model = GTK_TREE_MODEL (self->shortcuts_model);
-
-  /* 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);
-
-  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;
-      item->group = group;
-
-      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 (self->sections_store), &iter);
-      gtk_list_store_set (GTK_LIST_STORE (self->sections_store),
-                          &iter,
-                          SECTION_DESCRIPTION_COLUMN, title,
-                          SECTION_ID_COLUMN, id,
-                          SECTION_GROUP_COLUMN, group,
-                          -1);
-    }
-}
-
-static void
-append_sections_from_file (CcKeyboardPanel  *self,
-                           const gchar      *path,
-                           const char       *datadir,
-                           gchar           **wm_keybindings)
-{
-  KeyList *keylist;
-  KeyListEntry *keys;
-  KeyListEntry key = { 0, 0, 0, 0, 0, 0, 0 };
-  const char *title;
-  int group;
-  guint i;
-
-  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++)
-    {
-      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)
-{
-  GtkTreeModel *shortcut_model;
-  GHashTable *loaded_files;
-  GDir *dir;
-  gchar *default_wm_keybindings[] = { "Mutter", "GNOME Shell", NULL };
-  gchar **wm_keybindings;
-  const gchar * const * data_dirs;
-  guint i;
-
-  shortcut_model = GTK_TREE_MODEL (self->shortcuts_model);
-  /* FIXME: get current selection and keep it after refreshing */
-
-  /* Clear previous models and hash tables */
-  gtk_list_store_clear (GTK_LIST_STORE (self->sections_store));
-  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);
-
-  /* Load custom keybindings */
-  append_sections_from_gsettings (self);
-}
-
-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
-add_shortcuts (CcKeyboardPanel *self)
-{
-  GtkTreeIter sections_iter;
-  gboolean can_continue;
-
-  can_continue = gtk_tree_model_get_iter_first (self->sections_model, &sections_iter);
-
-  while (can_continue)
-    {
-      BindingGroupType group;
-      GPtrArray *keys;
-      gchar *id, *title;
-      gint i;
-
-      gtk_tree_model_get (self->sections_model,
-                          &sections_iter,
-                          SECTION_DESCRIPTION_COLUMN, &title,
-                          SECTION_GROUP_COLUMN, &group,
-                          SECTION_ID_COLUMN, &id,
-                          -1);
-
-      /* Ignore separators */
-      if (group == BINDING_GROUP_SEPARATOR)
-        {
-          can_continue = gtk_tree_model_iter_next (self->sections_model, &sections_iter);
-          continue;
-        }
-
-      keys = g_hash_table_lookup (get_hash_for_group (self, group), id);
-
-      for (i = 0; i < keys->len; i++)
-        {
-          CcKeyboardItem *item = g_ptr_array_index (keys, i);
-
-          if (!cc_keyboard_item_is_hidden (item))
-            {
-              GtkTreeIter new_row;
-
-              gtk_list_store_append (self->shortcuts_model, &new_row);
-              gtk_list_store_set (self->shortcuts_model,
-                                  &new_row,
-                                  DETAIL_DESCRIPTION_COLUMN, item->description,
-                                  DETAIL_KEYENTRY_COLUMN, item,
-                                  DETAIL_TYPE_COLUMN, SHORTCUT_TYPE_KEY_ENTRY,
-                                  -1);
-
-              add_item (self, item, id, title);
-            }
-        }
-
-      can_continue = gtk_tree_model_iter_next (self->sections_model, &sections_iter);
-
-      g_free (title);
-      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_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 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);
-
-  remove_item (self, item);
-
-  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)
+shortcut_row_activated (GtkWidget       *button,
+                        GtkListBoxRow   *row,
+                        CcKeyboardPanel *self)
 {
-  CcKeyboardItem *item;
-  GtkTreePath *path;
-  gchar *settings_path;
-
-  item = cc_keyboard_item_new (CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH);
+  CcKeyboardShortcutEditor *editor;
 
-  settings_path = find_free_settings_path (self->binding_settings);
-  cc_keyboard_item_load_from_gsettings_path (item, settings_path, TRUE);
-  g_free (settings_path);
+  editor = CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor);
 
-  item->model = GTK_TREE_MODEL (self->shortcuts_model);
-  item->group = BINDING_GROUP_USER;
-
-  if (edit_custom_shortcut (self, item) && item->command && item->command[0])
+  if (row != self->add_shortcut_row)
     {
-      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);
-        }
+      RowData *data = g_object_get_data (G_OBJECT (row), "data");
 
-      g_ptr_array_add (keys_array, item);
-
-      gtk_list_store_append (self->shortcuts_model, &iter);
-      gtk_list_store_set (self->shortcuts_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 (GTK_TREE_MODEL (self->shortcuts_model), &iter);
-      gtk_tree_path_free (path);
-
-      add_item (self, item, CUSTOM_SHORTCUTS_ID, _("Custom Shortcuts"));
+      cc_keyboard_shortcut_editor_set_mode (editor, CC_SHORTCUT_EDITOR_EDIT);
+      cc_keyboard_shortcut_editor_set_item (editor, data->item);
     }
   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)
-{
-  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);
+      cc_keyboard_shortcut_editor_set_mode (editor, CC_SHORTCUT_EDITOR_CREATE);
+      cc_keyboard_shortcut_editor_set_item (editor, NULL);
     }
 
-  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_MODEL (self->shortcuts_model);
-  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
-shortcut_row_activated (GtkWidget       *button,
-                        GtkListBoxRow   *row,
-                        CcKeyboardPanel *self)
-{
-  if (row == self->add_shortcut_row)
-    add_custom_shortcut (self);
-}
-
-static void
-xkb_options_combo_changed (GtkCellRendererCombo *combo,
-                           gchar                *model_path,
-                           GtkTreeIter          *model_iter,
-                           CcKeyboardPanel      *self)
-{
-  GtkTreeModel *shortcut_model;
-  GtkTreeIter shortcut_iter;
-  CcKeyboardOption *option;
-  ShortcutType type;
-
-  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)
-{
-  GtkWidget *widget;
-  CcShell *shell;
-
-  /* Setup the section treeview */
-  self->sections_store = gtk_list_store_new (SECTION_N_COLUMNS,
-                                             G_TYPE_STRING,
-                                             G_TYPE_STRING,
-                                             G_TYPE_INT);
-  self->sections_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (self->sections_store));
-
-  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (self->sections_model),
-                                   SECTION_DESCRIPTION_COLUMN,
-                                   section_sort_item,
-                                   self,
-                                   NULL);
-
-  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self->sections_model),
-                                        SECTION_DESCRIPTION_COLUMN,
-                                        GTK_SORT_ASCENDING);
-
-  self->shortcuts_model = gtk_list_store_new (DETAIL_N_COLUMNS,
-                                              G_TYPE_STRING,
-                                              G_TYPE_POINTER,
-                                              G_TYPE_INT);
-
-  setup_keyboard_options (self->shortcuts_model);
-
-  /* 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));
+  gtk_widget_show (self->shortcut_editor);
 }
 
 static void
@@ -1783,18 +343,8 @@ cc_keyboard_panel_finalize (GObject *object)
 {
   CcKeyboardPanel *self = CC_KEYBOARD_PANEL (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->accelerator_sizegroup);
-  g_clear_object (&self->custom_shortcut_dialog);
-  g_clear_object (&self->binding_settings);
-  g_clear_object (&self->shortcuts_model);
-  g_clear_object (&self->sections_store);
-  g_clear_object (&self->sections_model);
 
   cc_keyboard_option_clear_all ();
 
@@ -1802,29 +352,16 @@ cc_keyboard_panel_finalize (GObject *object)
 }
 
 static void
-on_window_manager_change (const char      *wm_name,
-                          CcKeyboardPanel *self)
-{
-  reload_sections (self);
-}
-
-static void
 cc_keyboard_panel_constructed (GObject *object)
 {
   CcKeyboardPanel *self = CC_KEYBOARD_PANEL (object);
+  GtkWindow *toplevel;
 
   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);
-
-  add_shortcuts (self);
+  /* Setup the dialog's transient parent */
+  toplevel = GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self))));
+  gtk_window_set_transient_for (GTK_WINDOW (self->shortcut_editor), toplevel);
 }
 
 static void
@@ -1845,15 +382,9 @@ cc_keyboard_panel_class_init (CcKeyboardPanelClass *klass)
   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_shortcut_row);
-  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, listbox);
 
-  gtk_widget_class_bind_template_callback (widget_class, shortcut_entry_changed);
   gtk_widget_class_bind_template_callback (widget_class, shortcut_row_activated);
-  gtk_widget_class_bind_template_callback (widget_class, shortcut_selection_changed);
 }
 
 static void
@@ -1863,11 +394,30 @@ cc_keyboard_panel_init (CcKeyboardPanel *self)
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
-  self->binding_settings = g_settings_new (BINDINGS_SCHEMA);
+  self->manager = cc_keyboard_manager_new ();
 
   /* Use a sizegroup to make the accelerator labels the same width */
   self->accelerator_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
 
+  /* Shortcut editor dialog */
+  self->shortcut_editor = cc_keyboard_shortcut_editor_new (self->manager);
+
+  g_signal_connect_swapped (self->manager,
+                            "shortcut-added",
+                            G_CALLBACK (add_item),
+                            self);
+
+  g_signal_connect_swapped (self->manager,
+                            "shortcut-removed",
+                            G_CALLBACK (remove_item),
+                            self);
+
+  cc_keyboard_manager_load_shortcuts (self->manager);
+
+  /* Shortcut editor dialog */
+  self->shortcut_editor = cc_keyboard_shortcut_editor_new (self->manager);
+
+  /* Setup the shortcuts listbox */
   gtk_list_box_set_sort_func (GTK_LIST_BOX (self->listbox),
                               sort_function,
                               self,
@@ -1878,3 +428,4 @@ cc_keyboard_panel_init (CcKeyboardPanel *self)
                                 self,
                                 NULL);
 }
+
diff --git a/panels/keyboard/cc-keyboard-panel.h b/panels/keyboard/cc-keyboard-panel.h
index 859a807..cbf8e0d 100644
--- a/panels/keyboard/cc-keyboard-panel.h
+++ b/panels/keyboard/cc-keyboard-panel.h
@@ -31,6 +31,8 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (CcKeyboardPanel, cc_keyboard_panel, CC, KEYBOARD_PANEL, CcPanel)
 
+CcKeyboardItem*      cc_keyboard_panel_create_custom_item        (CcKeyboardPanel    *self);
+
 G_END_DECLS
 
 #endif /* _CC_KEYBOARD_PANEL_H */
diff --git a/panels/keyboard/cc-keyboard-shortcut-editor.c b/panels/keyboard/cc-keyboard-shortcut-editor.c
new file mode 100644
index 0000000..74b1911
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-shortcut-editor.c
@@ -0,0 +1,698 @@
+/* cc-keyboard-shortcut-editor.h
+ *
+ * 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
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Georges Basile Stavracas Neto <georges stavracas gmail com>
+ */
+
+#include <glib-object.h>
+#include <glib/gi18n.h>
+
+#include "cc-keyboard-shortcut-editor.h"
+#include "cc-shortcut-label.h"
+#include "keyboard-shortcuts.h"
+
+/*
+ * Workaround to stop receiving a stray Meta modifier.
+ */
+#ifdef __APPLE__
+#define ALL_ACCELS_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK | GDK_META_MASK)
+#else
+#define ALL_ACCELS_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK)
+#endif
+
+struct _CcKeyboardShortcutEditor
+{
+  GtkDialog           parent;
+
+  GtkWidget          *add_button;
+  GtkWidget          *cancel_button;
+  GtkWidget          *command_entry;
+  GtkWidget          *custom_shortcut_accel_label;
+  GtkWidget          *edit_button;
+  GtkWidget          *headerbar;
+  GtkWidget          *name_entry;
+  GtkWidget          *remove_button;
+  GtkWidget          *replace_button;
+  GtkWidget          *shortcut_accel_label;
+  GtkWidget          *stack;
+  GtkWidget          *top_info_label;
+
+  CcShortcutEditorMode mode;
+
+  GdkDevice          *grab_device;
+
+  CcKeyboardManager  *manager;
+  CcKeyboardItem     *item;
+
+  /* Custom shortcuts */
+  GdkDevice          *grab_pointer;
+
+  guint               custom_keycode;
+  guint               custom_keyval;
+  GdkModifierType     custom_mask;
+  gboolean            custom_is_modifier;
+  gboolean            edited : 1;
+};
+
+static void          command_entry_changed_cb                    (CcKeyboardShortcutEditor *self);
+static void          name_entry_changed_cb                       (CcKeyboardShortcutEditor *self);
+
+G_DEFINE_TYPE (CcKeyboardShortcutEditor, cc_keyboard_shortcut_editor, GTK_TYPE_DIALOG)
+
+enum
+{
+  PROP_0,
+  PROP_KEYBOARD_ITEM,
+  PROP_MANAGER,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS] = { NULL, };
+
+static void
+apply_custom_item_fields (CcKeyboardShortcutEditor *self,
+                          CcKeyboardItem           *item)
+{
+  /* Only setup the binding when it was actually edited */
+  if (self->edited)
+    {
+      gchar *binding;
+
+      item->keycode = self->custom_keycode;
+      item->keyval = self->custom_keyval;
+      item->mask = self->custom_mask;
+
+      binding = gtk_accelerator_name_with_keycode (NULL,
+                                                   item->keyval,
+                                                   item->keycode,
+                                                   item->mask);
+
+      g_object_set (G_OBJECT (item), "binding", binding, NULL);
+
+      g_free (binding);
+    }
+
+  /* Set the keyboard shortcut name and command for custom entries */
+  if (item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH)
+    {
+      g_settings_set_string (item->settings, "name", gtk_entry_get_text (GTK_ENTRY (self->name_entry)));
+      g_settings_set_string (item->settings, "command", gtk_entry_get_text (GTK_ENTRY 
(self->command_entry)));
+    }
+}
+
+static void
+clear_custom_entries (CcKeyboardShortcutEditor *self)
+{
+  g_signal_handlers_block_by_func (self->command_entry, command_entry_changed_cb, self);
+  g_signal_handlers_block_by_func (self->name_entry, name_entry_changed_cb, self);
+
+  gtk_entry_set_text (GTK_ENTRY (self->name_entry), "");
+  gtk_entry_set_text (GTK_ENTRY (self->command_entry), "");
+
+  cc_shortcut_label_set_accelerator (CC_SHORTCUT_LABEL (self->custom_shortcut_accel_label), "");
+
+  self->custom_keycode = 0;
+  self->custom_keyval = 0;
+  self->custom_mask = 0;
+  self->custom_is_modifier = TRUE;
+  self->edited = FALSE;
+
+  g_signal_handlers_unblock_by_func (self->command_entry, command_entry_changed_cb, self);
+  g_signal_handlers_unblock_by_func (self->name_entry, name_entry_changed_cb, self);
+}
+
+static gboolean
+is_custom_shortcut (CcKeyboardShortcutEditor *self)
+{
+  return g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (self->stack)), "custom");
+}
+
+static void
+grab_seat (CcKeyboardShortcutEditor *self,
+           GdkEvent                 *event)
+{
+  GdkGrabStatus status;
+  GdkDevice *pointer;
+  GdkDevice *device;
+  GdkWindow *window;
+
+  if (!event)
+    event = gtk_get_current_event ();
+
+  device = gdk_event_get_device (event);
+  window = gtk_widget_get_window (GTK_WIDGET (self));
+
+  if (!device || !window)
+    return;
+
+  if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+    pointer = gdk_device_get_associated_device (device);
+  else
+    pointer = device;
+
+  status = gdk_seat_grab (gdk_device_get_seat (pointer),
+                          window,
+                          GDK_SEAT_CAPABILITY_ALL,
+                          FALSE,
+                          NULL,
+                          event,
+                          NULL,
+                          NULL);
+
+  if (status != GDK_GRAB_SUCCESS)
+    return;
+
+  self->grab_pointer = pointer;
+
+  gtk_grab_add (GTK_WIDGET (self));
+}
+
+static void
+release_grab (CcKeyboardShortcutEditor *self)
+{
+  if (self->grab_pointer)
+    {
+      gdk_seat_ungrab (gdk_device_get_seat (self->grab_pointer));
+      self->grab_pointer = NULL;
+
+      gtk_grab_remove (GTK_WIDGET (self));
+    }
+}
+
+static void
+update_shortcut (CcKeyboardShortcutEditor *self)
+{
+  if (!self->item)
+    return;
+
+  /* Setup the binding */
+  apply_custom_item_fields (self, self->item);
+
+  /* Cleanup whatever was set before */
+  clear_custom_entries (self);
+
+  cc_keyboard_shortcut_editor_set_item (self, NULL);
+}
+
+static CcShortcutLabel*
+get_current_shortcut_label (CcKeyboardShortcutEditor *self)
+{
+  if (is_custom_shortcut (self))
+    return CC_SHORTCUT_LABEL (self->custom_shortcut_accel_label);
+
+  return CC_SHORTCUT_LABEL (self->shortcut_accel_label);
+}
+
+static void
+validate_custom_shortcut (CcKeyboardShortcutEditor *self)
+{
+  gboolean valid;
+
+  valid = is_valid_binding (self->custom_keyval, self->custom_mask, self->custom_keycode) &&
+          gtk_accelerator_valid (self->custom_keyval, self->custom_mask) &&
+          !self->custom_is_modifier;
+
+  /* Additional checks for custom shortcuts */
+  if (is_custom_shortcut (self))
+    {
+      valid = valid &&
+              gtk_entry_get_text_length (GTK_ENTRY (self->name_entry)) > 0 &&
+              gtk_entry_get_text_length (GTK_ENTRY (self->command_entry)) > 0;
+    }
+
+  gtk_widget_set_sensitive (self->add_button, valid);
+
+  if (valid)
+    {
+      CcShortcutLabel *shortcut_label;
+      gchar *accel;
+
+      shortcut_label = get_current_shortcut_label (self);
+      accel = gtk_accelerator_name (self->custom_keyval, self->custom_mask);
+
+      /* Setup the accelerator label */
+      cc_shortcut_label_set_accelerator (shortcut_label, accel);
+
+      /*
+       * When the user finishes typing the new shortcut, it gets immediately
+       * applied and the toggle button gets inactive.
+       */
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->edit_button), FALSE);
+
+      self->edited = TRUE;
+
+      release_grab (self);
+
+      g_free (accel);
+    }
+}
+
+static void
+add_button_clicked_cb (CcKeyboardShortcutEditor *self)
+{
+  CcKeyboardItem *item;
+
+  item = cc_keyboard_manager_create_custom_shortcut (self->manager);
+
+  /* Apply the custom shortcut setup at the new item */
+  apply_custom_item_fields (self, item);
+
+  /* Cleanup everything once we're done */
+  clear_custom_entries (self);
+
+  cc_keyboard_manager_add_custom_shortcut (self->manager, item);
+
+  gtk_widget_hide (GTK_WIDGET (self));
+}
+
+static void
+cancel_button_clicked_cb (GtkWidget                *button,
+                          CcKeyboardShortcutEditor *self)
+{
+  cc_keyboard_shortcut_editor_set_item (self, NULL);
+  clear_custom_entries (self);
+
+  gtk_widget_hide (GTK_WIDGET (self));
+}
+
+static void
+command_entry_changed_cb (CcKeyboardShortcutEditor *self)
+{
+  validate_custom_shortcut (self);
+}
+
+static void
+edit_custom_shortcut_button_toggled_cb (CcKeyboardShortcutEditor *self,
+                                        GParamSpec               *pspec,
+                                        GtkToggleButton          *button)
+{
+  if (gtk_toggle_button_get_active (button))
+    grab_seat (self, NULL);
+  else
+    release_grab (self);
+}
+
+static void
+name_entry_changed_cb (CcKeyboardShortcutEditor *self)
+{
+  validate_custom_shortcut (self);
+}
+
+static void
+remove_button_clicked_cb (CcKeyboardShortcutEditor *self)
+{
+  gtk_widget_hide (GTK_WIDGET (self));
+
+  cc_keyboard_manager_remove_custom_shortcut (self->manager, self->item);
+}
+
+static void
+setup_keyboard_item (CcKeyboardShortcutEditor *self,
+                     CcKeyboardItem           *item)
+{
+  gboolean is_custom;
+  gchar *accel;
+  gchar *text;
+
+  if (!item)
+    return;
+
+  is_custom = item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH;
+  accel = gtk_accelerator_name (item->keyval, item->mask);
+
+  /* Headerbar */
+  gtk_header_bar_set_title (GTK_HEADER_BAR (self->headerbar), item->description);
+  gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (self->headerbar), TRUE);
+
+  gtk_widget_hide (self->add_button);
+  gtk_widget_hide (self->cancel_button);
+  gtk_widget_hide (self->replace_button);
+
+  /* Setup the top label */
+  text = g_strdup_printf (_("Keyboard shortcut for <b>%s</b>. Enter new shortcut to change."), 
item->description);
+
+  gtk_label_set_markup (GTK_LABEL (self->top_info_label), text);
+
+  /* Accelerator labels */
+  cc_shortcut_label_set_accelerator (CC_SHORTCUT_LABEL (self->shortcut_accel_label), accel);
+  cc_shortcut_label_set_accelerator (CC_SHORTCUT_LABEL (self->custom_shortcut_accel_label), accel);
+
+  /* Setup the custom entries */
+  if (is_custom)
+    {
+      g_signal_handlers_block_by_func (self->command_entry, command_entry_changed_cb, self);
+      g_signal_handlers_block_by_func (self->name_entry, name_entry_changed_cb, self);
+
+      /* Name entry */
+      gtk_entry_set_text (GTK_ENTRY (self->name_entry), item->description);
+      gtk_widget_set_sensitive (self->name_entry, item->desc_editable);
+
+      /* Command entry */
+      gtk_entry_set_text (GTK_ENTRY (self->command_entry), item->command);
+      gtk_widget_set_sensitive (self->command_entry, item->cmd_editable);
+
+      gtk_widget_show (self->remove_button);
+
+      g_signal_handlers_unblock_by_func (self->command_entry, command_entry_changed_cb, self);
+      g_signal_handlers_unblock_by_func (self->name_entry, name_entry_changed_cb, self);
+    }
+
+  g_free (accel);
+  g_free (text);
+
+  /* Show the apropriate view */
+  gtk_stack_set_visible_child_name (GTK_STACK (self->stack), is_custom ? "custom" : "edit");
+}
+
+static void
+cc_keyboard_shortcut_editor_finalize (GObject *object)
+{
+  CcKeyboardShortcutEditor *self = (CcKeyboardShortcutEditor *)object;
+
+  g_clear_object (&self->item);
+  g_clear_object (&self->manager);
+
+  G_OBJECT_CLASS (cc_keyboard_shortcut_editor_parent_class)->finalize (object);
+}
+
+static void
+cc_keyboard_shortcut_editor_get_property (GObject    *object,
+                                          guint       prop_id,
+                                          GValue     *value,
+                                          GParamSpec *pspec)
+{
+  CcKeyboardShortcutEditor *self = CC_KEYBOARD_SHORTCUT_EDITOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_KEYBOARD_ITEM:
+      g_value_set_object (value, self->item);
+      break;
+
+    case PROP_MANAGER:
+      g_value_set_object (value, self->manager);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+cc_keyboard_shortcut_editor_set_property (GObject      *object,
+                                          guint         prop_id,
+                                          const GValue *value,
+                                          GParamSpec   *pspec)
+{
+  CcKeyboardShortcutEditor *self = CC_KEYBOARD_SHORTCUT_EDITOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_KEYBOARD_ITEM:
+      cc_keyboard_shortcut_editor_set_item (self, g_value_get_object (value));
+      break;
+
+    case PROP_MANAGER:
+      g_set_object (&self->manager, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static gboolean
+cc_keyboard_shortcut_editor_key_press_event (GtkWidget   *widget,
+                                             GdkEventKey *event)
+{
+  CcKeyboardShortcutEditor *self;
+  gboolean editing;
+
+  self = CC_KEYBOARD_SHORTCUT_EDITOR (widget);
+
+  editing = !g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (self->stack)), "custom") ||
+             gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->edit_button));
+
+  if (editing)
+    {
+      GdkModifierType real_mask;
+
+      real_mask = event->state & gtk_accelerator_get_default_mod_mask () & ALL_ACCELS_MASK;
+
+      if (!event->is_modifier && real_mask == 0)
+        {
+          /* A single Escape press cancels the editing */
+          if (event->keyval == GDK_KEY_Escape)
+            {
+              self->edited = FALSE;
+
+              gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->edit_button), FALSE);
+              release_grab (self);
+
+              return TRUE;
+            }
+
+          /* Backspace disables the current shortcut */
+          if (event->keyval == GDK_KEY_BackSpace)
+            {
+              self->edited = TRUE;
+              self->custom_keycode = 0;
+              self->custom_keyval = 0;
+              self->custom_mask = 0;
+
+              if (self->item)
+                apply_custom_item_fields (self, self->item);
+
+              cc_shortcut_label_set_accelerator (CC_SHORTCUT_LABEL (self->custom_shortcut_accel_label), "");
+              cc_shortcut_label_set_accelerator (CC_SHORTCUT_LABEL (self->shortcut_accel_label), "");
+
+              gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->edit_button), FALSE);
+              release_grab (self);
+
+              self->edited = FALSE;
+
+              return TRUE;
+            }
+        }
+
+      self->custom_is_modifier = event->is_modifier;
+      self->custom_keycode = event->hardware_keycode;
+      self->custom_keyval = event->keyval;
+      self->custom_mask = real_mask;
+
+      /* Ignore CapsLock */
+      self->custom_mask &= ~GDK_LOCK_MASK;
+
+      if (!self->grab_pointer)
+        grab_seat (self, (GdkEvent*) event);
+
+      validate_custom_shortcut (self);
+
+      return TRUE;
+    }
+
+  return GTK_WIDGET_CLASS (cc_keyboard_shortcut_editor_parent_class)->key_press_event (widget, event);
+}
+
+static void
+cc_keyboard_shortcut_editor_close (GtkDialog *dialog)
+{
+  CcKeyboardShortcutEditor *self = CC_KEYBOARD_SHORTCUT_EDITOR (dialog);
+
+  if (self->mode == CC_SHORTCUT_EDITOR_EDIT)
+    update_shortcut (self);
+
+  GTK_DIALOG_CLASS (cc_keyboard_shortcut_editor_parent_class)->close (dialog);
+}
+
+static void
+cc_keyboard_shortcut_editor_response (GtkDialog *dialog,
+                                      gint       response_id)
+{
+  CcKeyboardShortcutEditor *self = CC_KEYBOARD_SHORTCUT_EDITOR (dialog);
+
+  if (response_id == GTK_RESPONSE_DELETE_EVENT &&
+      self->mode == CC_SHORTCUT_EDITOR_EDIT)
+    {
+      update_shortcut (self);
+    }
+}
+
+static void
+cc_keyboard_shortcut_editor_class_init (CcKeyboardShortcutEditorClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = cc_keyboard_shortcut_editor_finalize;
+  object_class->get_property = cc_keyboard_shortcut_editor_get_property;
+  object_class->set_property = cc_keyboard_shortcut_editor_set_property;
+
+  widget_class->key_press_event = cc_keyboard_shortcut_editor_key_press_event;
+
+  dialog_class->close = cc_keyboard_shortcut_editor_close;
+  dialog_class->response = cc_keyboard_shortcut_editor_response;
+
+  /**
+   * CcKeyboardShortcutEditor:keyboard-item:
+   *
+   * The current keyboard shortcut being edited.
+   */
+  properties[PROP_KEYBOARD_ITEM] = g_param_spec_object ("keyboard-item",
+                                                        "Keyboard item",
+                                                        "The keyboard item being edited",
+                                                        CC_TYPE_KEYBOARD_ITEM,
+                                                        G_PARAM_READWRITE);
+
+  /**
+   * CcKeyboardShortcutEditor:panel:
+   *
+   * The current keyboard panel.
+   */
+  properties[PROP_MANAGER] = g_param_spec_object ("manager",
+                                                  "Keyboard manager",
+                                                  "The keyboard manager",
+                                                  CC_TYPE_KEYBOARD_MANAGER,
+                                                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/control-center/keyboard/shortcut-editor.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, add_button);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, cancel_button);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, command_entry);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, custom_shortcut_accel_label);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, edit_button);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, headerbar);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, name_entry);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, remove_button);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, replace_button);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, shortcut_accel_label);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, stack);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutEditor, top_info_label);
+
+  gtk_widget_class_bind_template_callback (widget_class, add_button_clicked_cb);
+  gtk_widget_class_bind_template_callback (widget_class, cancel_button_clicked_cb);
+  gtk_widget_class_bind_template_callback (widget_class, command_entry_changed_cb);
+  gtk_widget_class_bind_template_callback (widget_class, edit_custom_shortcut_button_toggled_cb);
+  gtk_widget_class_bind_template_callback (widget_class, name_entry_changed_cb);
+  gtk_widget_class_bind_template_callback (widget_class, remove_button_clicked_cb);
+}
+
+static void
+cc_keyboard_shortcut_editor_init (CcKeyboardShortcutEditor *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  self->mode = CC_SHORTCUT_EDITOR_EDIT;
+  self->custom_is_modifier = TRUE;
+}
+
+/**
+ * cc_keyboard_shortcut_editor_new:
+ *
+ * Creates a new #CcKeyboardShortcutEditor.
+ *
+ * Returns: (transfer full): a newly created #CcKeyboardShortcutEditor.
+ */
+GtkWidget*
+cc_keyboard_shortcut_editor_new (CcKeyboardManager *manager)
+{
+  return g_object_new (CC_TYPE_KEYBOARD_SHORTCUT_EDITOR,
+                       "manager", manager,
+                       "use-header-bar", 1,
+                       NULL);
+}
+
+/**
+ * cc_keyboard_shortcut_editor_get_item:
+ * @self: a #CcKeyboardShortcutEditor
+ *
+ * Retrieves the current keyboard shortcut being edited.
+ *
+ * Returns: (transfer none)(nullable): a #CcKeyboardItem
+ */
+CcKeyboardItem*
+cc_keyboard_shortcut_editor_get_item (CcKeyboardShortcutEditor *self)
+{
+  g_return_val_if_fail (CC_IS_KEYBOARD_SHORTCUT_EDITOR (self), NULL);
+
+  return self->item;
+}
+
+/**
+ * cc_keyboard_shortcut_editor_set_item:
+ * @self: a #CcKeyboardShortcutEditor
+ * @item: a #CcKeyboardItem
+ *
+ * Sets the current keyboard shortcut to be edited.
+ */
+void
+cc_keyboard_shortcut_editor_set_item (CcKeyboardShortcutEditor *self,
+                                      CcKeyboardItem           *item)
+{
+  g_return_if_fail (CC_IS_KEYBOARD_SHORTCUT_EDITOR (self));
+
+  if (g_set_object (&self->item, item))
+    {
+      setup_keyboard_item (self, item);
+
+      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_KEYBOARD_ITEM]);
+    }
+}
+
+CcShortcutEditorMode
+cc_keyboard_shortcut_editor_get_mode (CcKeyboardShortcutEditor *self)
+{
+  g_return_val_if_fail (CC_IS_KEYBOARD_SHORTCUT_EDITOR (self), 0);
+
+  return self->mode;
+}
+
+void
+cc_keyboard_shortcut_editor_set_mode (CcKeyboardShortcutEditor *self,
+                                      CcShortcutEditorMode      mode)
+{
+  g_return_if_fail (CC_IS_KEYBOARD_SHORTCUT_EDITOR (self));
+
+  if (self->mode != mode)
+    {
+      self->mode = mode;
+
+      if (mode == CC_SHORTCUT_EDITOR_CREATE)
+        {
+          /* Cleanup whatever was set before */
+          clear_custom_entries (self);
+
+          /* The 'Add' button is only sensitive when the shortcut is valid */
+          gtk_widget_set_sensitive (self->add_button, FALSE);
+
+          gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (self->headerbar), FALSE);
+          gtk_header_bar_set_title (GTK_HEADER_BAR (self->headerbar), _("Add Custom Shortcut"));
+
+          gtk_stack_set_visible_child_name (GTK_STACK (self->stack), "custom");
+
+          gtk_widget_show (self->add_button);
+          gtk_widget_show (self->cancel_button);
+
+          gtk_widget_hide (self->remove_button);
+          gtk_widget_hide (self->replace_button);
+        }
+    }
+}
diff --git a/panels/keyboard/cc-keyboard-shortcut-editor.h b/panels/keyboard/cc-keyboard-shortcut-editor.h
new file mode 100644
index 0000000..a9455a8
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-shortcut-editor.h
@@ -0,0 +1,56 @@
+/* cc-keyboard-shortcut-editor.h
+ *
+ * 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
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Georges Basile Stavracas Neto <georges stavracas gmail com>
+ */
+
+#ifndef CC_KEYBOARD_SHORTCUT_EDITOR_H
+#define CC_KEYBOARD_SHORTCUT_EDITOR_H
+
+#include <gtk/gtk.h>
+
+#include "cc-keyboard-item.h"
+#include "cc-keyboard-manager.h"
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_KEYBOARD_SHORTCUT_EDITOR (cc_keyboard_shortcut_editor_get_type())
+
+typedef enum
+{
+  CC_SHORTCUT_EDITOR_CREATE,
+  CC_SHORTCUT_EDITOR_EDIT
+} CcShortcutEditorMode;
+
+G_DECLARE_FINAL_TYPE (CcKeyboardShortcutEditor, cc_keyboard_shortcut_editor, CC, KEYBOARD_SHORTCUT_EDITOR, 
GtkDialog)
+
+GtkWidget*           cc_keyboard_shortcut_editor_new             (CcKeyboardManager        *manager);
+
+CcKeyboardItem*      cc_keyboard_shortcut_editor_get_item        (CcKeyboardShortcutEditor *self);
+
+void                 cc_keyboard_shortcut_editor_set_item        (CcKeyboardShortcutEditor *self,
+                                                                  CcKeyboardItem           *item);
+
+CcShortcutEditorMode cc_keyboard_shortcut_editor_get_mode        (CcKeyboardShortcutEditor *self);
+
+void                 cc_keyboard_shortcut_editor_set_mode        (CcKeyboardShortcutEditor *self,
+                                                                  CcShortcutEditorMode      mode);
+
+G_END_DECLS
+
+#endif /* CC_KEYBOARD_SHORTCUT_EDITOR_H */
+
diff --git a/panels/keyboard/gnome-keyboard-panel.ui b/panels/keyboard/gnome-keyboard-panel.ui
index 2706a76..cb0bdb1 100644
--- a/panels/keyboard/gnome-keyboard-panel.ui
+++ b/panels/keyboard/gnome-keyboard-panel.ui
@@ -8,150 +8,6 @@
     <property name="step_increment">200</property>
     <property name="page_increment">200</property>
   </object>
-  <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>
-        <property name="can_focus">False</property>
-        <property name="title" translatable="yes">Custom Shortcut</property>
-        <property name="show_close_button">False</property>
-        <child>
-          <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>
-            <property name="receives_default">False</property>
-            <property name="use_action_appearance">False</property>
-            <property name="use_underline">True</property>
-            <property name="valign">center</property>
-            <style>
-              <class name="text-button"/>
-            </style>
-          </object>
-          <packing>
-            <property name="pack_type">start</property>
-          </packing>
-        </child>
-        <child>
-          <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>
-            <property name="can_default">True</property>
-            <property name="receives_default">False</property>
-            <property name="use_action_appearance">False</property>
-            <property name="use_underline">True</property>
-            <property name="valign">center</property>
-            <property name="sensitive">False</property>
-            <style>
-              <class name="text-button"/>
-              <class name="suggested-action"/>
-            </style>
-          </object>
-          <packing>
-            <property name="pack_type">end</property>
-          </packing>
-        </child>
-      </object>
-    </child>
-    <child internal-child="vbox">
-      <object class="GtkBox" id="dialog-vbox1">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="orientation">vertical</property>
-        <child>
-          <object class="GtkBox">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="border_width">5</property>
-            <property name="spacing">6</property>
-            <child>
-              <object class="GtkGrid" id="grid1">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="row_spacing">6</property>
-                <property name="column_spacing">6</property>
-                <child>
-                  <object class="GtkLabel" id="label13">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <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>
-                  </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">0</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="label14">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <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>
-                  </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <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>
-                    <property name="top_attach">0</property>
-                  </packing>
-                </child>
-                <child>
-                  <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>
-                    <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>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">True</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-      </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-widgets>
-  </object>
   <template class="CcKeyboardPanel" parent="CcPanel">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
diff --git a/panels/keyboard/keyboard.gresource.xml b/panels/keyboard/keyboard.gresource.xml
index c52ecc9..751d58a 100644
--- a/panels/keyboard/keyboard.gresource.xml
+++ b/panels/keyboard/keyboard.gresource.xml
@@ -2,5 +2,6 @@
 <gresources>
   <gresource prefix="/org/gnome/control-center/keyboard">
     <file preprocess="xml-stripblanks">gnome-keyboard-panel.ui</file>
+    <file preprocess="xml-stripblanks">shortcut-editor.ui</file>
   </gresource>
 </gresources>
diff --git a/panels/keyboard/shortcut-editor.ui b/panels/keyboard/shortcut-editor.ui
new file mode 100644
index 0000000..b1e04c5
--- /dev/null
+++ b/panels/keyboard/shortcut-editor.ui
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <template class="CcKeyboardShortcutEditor" parent="GtkDialog">
+    <property name="can_focus">False</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="width_request">400</property>
+    <property name="height_request">300</property>
+    <property name="window_position">center</property>
+    <property name="type_hint">dialog</property>
+    <signal name="delete-event" handler="gtk_widget_hide_on_delete" object="CcKeyboardShortcutEditor" 
swapped="yes"/>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkStack" id="stack">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="hexpand">True</property>
+            <property name="vexpand">True</property>
+            <property name="border_width">12</property>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">18</property>
+                <child>
+                  <object class="GtkLabel" id="top_info_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="wrap">True</property>
+                    <property name="wrap_mode">word-char</property>
+                    <property name="width_chars">15</property>
+                    <property name="max_width_chars">20</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="CcShortcutLabel" id="shortcut_accel_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">center</property>
+                    <property name="placeholder" translatable="yes">Disabled</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton" id="reset_button">
+                    <property name="label" translatable="yes">Reset</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="halign">end</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="name">edit</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkGrid">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hexpand">True</property>
+                <property name="vexpand">True</property>
+                <property name="row_spacing">12</property>
+                <property name="column_spacing">12</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Name</property>
+                    <property name="xalign">1</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Command</property>
+                    <property name="xalign">1</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Shortcut</property>
+                    <property name="xalign">1</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="new_shortcut_conflict_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="wrap">True</property>
+                    <property name="wrap_mode">word-char</property>
+                    <property name="width_chars">15</property>
+                    <property name="max_width_chars">20</property>
+                    <property name="xalign">0</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">3</property>
+                    <property name="width">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="name_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hexpand">True</property>
+                    <property name="sensitive" bind-source="edit_button" bind-property="active" 
bind-flags="default|invert-boolean" />
+                    <signal name="notify::text" handler="name_entry_changed_cb" 
object="CcKeyboardShortcutEditor" swapped="yes" />
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">0</property>
+                    <property name="width">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="command_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hexpand">True</property>
+                    <property name="sensitive" bind-source="edit_button" bind-property="active" 
bind-flags="default|invert-boolean" />
+                    <signal name="notify::text" handler="command_entry_changed_cb" 
object="CcKeyboardShortcutEditor" swapped="yes" />
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">1</property>
+                    <property name="width">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToggleButton" id="edit_button">
+                    <property name="label" translatable="yes">Edit</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <signal name="notify::active" handler="edit_custom_shortcut_button_toggled_cb" 
object="CcKeyboardShortcutEditor" swapped="yes" />
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="CcShortcutLabel" id="custom_shortcut_accel_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">start</property>
+                    <property name="hexpand">True</property>
+                    <property name="placeholder" translatable="yes">None</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="remove_button">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Remove</property>
+                    <property name="valign">end</property>
+                    <property name="sensitive" bind-source="edit_button" bind-property="active" 
bind-flags="default|invert-boolean" />
+                    <signal name="clicked" handler="remove_button_clicked_cb" 
object="CcKeyboardShortcutEditor" swapped="yes" />
+                    <style>
+                      <class name="destructive-action" />
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">3</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="name">custom</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <child type="titlebar">
+      <object class="GtkHeaderBar" id="headerbar">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="show_close_button">True</property>
+        <child>
+          <object class="GtkButton" id="cancel_button">
+            <property name="label" translatable="yes">Cancel</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <signal name="clicked" handler="cancel_button_clicked_cb" object="CcKeyboardShortcutEditor" 
swapped="no" />
+          </object>
+        </child>
+        <child>
+          <object class="GtkButton" id="add_button">
+            <property name="label" translatable="yes">Add</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <signal name="clicked" handler="add_button_clicked_cb" object="CcKeyboardShortcutEditor" 
swapped="yes" />
+            <style>
+              <class name="suggested-action" />
+            </style>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="replace_button">
+            <property name="label" translatable="yes">Replace</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkSizeGroup">
+    <widgets>
+      <widget name="cancel_button"/>
+      <widget name="add_button"/>
+      <widget name="replace_button"/>
+      <widget name="reset_button"/>
+    </widgets>
+  </object>
+</interface>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 970dc9f..cf367f0 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -44,7 +44,9 @@ panels/keyboard/50-accessibility.xml.in
 panels/keyboard/cc-keyboard-option.c
 panels/keyboard/gnome-keyboard-panel.desktop.in.in
 [type: gettext/glade]panels/keyboard/gnome-keyboard-panel.ui
+[type: gettext/glade]panels/keyboard/shortcut-editor.ui
 panels/keyboard/keyboard-shortcuts.c
+panels/keyboard/cc-shortcut-editor.c
 panels/mouse/cc-mouse-panel.c
 panels/mouse/gnome-mouse-panel.desktop.in.in
 panels/mouse/gnome-mouse-properties.c


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