[gnome-control-center/wip/new-keybindings-ui: 19/20] keyboard: Match with previous functionality



commit ff19891f779ce6aff8656f803c2064bc39ace0c6
Author: Rodrigo Moya <rodrigo gnome-db org>
Date:   Thu Dec 9 14:27:54 2010 +0100

    keyboard: Match with previous functionality

 panels/keyboard/gnome-keyboard-panel.c | 1138 --------------------------------
 panels/keyboard/keyboard-shortcuts.c   | 1072 ++++++++++++++++++++++++++++--
 2 files changed, 1024 insertions(+), 1186 deletions(-)
---
diff --git a/panels/keyboard/gnome-keyboard-panel.c b/panels/keyboard/gnome-keyboard-panel.c
index ac5c383..bf6aa2a 100644
--- a/panels/keyboard/gnome-keyboard-panel.c
+++ b/panels/keyboard/gnome-keyboard-panel.c
@@ -15,239 +15,9 @@
 #include "gnome-keyboard-panel.h"
 
 #define MAX_ELEMENTS_BEFORE_SCROLLING 10
-#define MAX_CUSTOM_SHORTCUTS 1000
 #define RESPONSE_ADD 0
 #define RESPONSE_REMOVE 1
 
-static gboolean block_accels = FALSE;
-static GHashTable *keyb_sections = NULL;
-
-static char*
-binding_name (guint                   keyval,
-	      guint		      keycode,
-              EggVirtualModifierType  mask,
-              gboolean                translate)
-{
-  if (keyval != 0 || keycode != 0)
-    return translate ?
-	egg_virtual_accelerator_label (keyval, keycode, mask) :
-	egg_virtual_accelerator_name (keyval, keycode, mask);
-  else
-    return g_strdup (translate ? _("Disabled") : "");
-}
-
-static gboolean
-keybinding_key_changed_foreach (GtkTreeModel *model,
-				GtkTreePath  *path,
-				GtkTreeIter  *iter,
-				gpointer      user_data)
-{
-  KeyEntry *key_entry;
-  KeyEntry *tmp_key_entry;
-
-  key_entry = (KeyEntry *)user_data;
-  gtk_tree_model_get (key_entry->model, iter,
-		      KEYENTRY_COLUMN, &tmp_key_entry,
-		      -1);
-
-  if (key_entry == tmp_key_entry)
-    {
-      gtk_tree_model_row_changed (key_entry->model, path, iter);
-      return TRUE;
-    }
-  return FALSE;
-}
-
-static void
-keybinding_key_changed (GConfClient *client,
-			guint        cnxn_id,
-			GConfEntry  *entry,
-			gpointer     user_data)
-{
-  KeyEntry *key_entry;
-  const gchar *key_value;
-
-  key_entry = (KeyEntry *) user_data;
-  key_value = entry->value ? gconf_value_get_string (entry->value) : NULL;
-
-  binding_from_string (key_value, &key_entry->keyval, &key_entry->keycode, &key_entry->mask);
-  key_entry->editable = gconf_entry_get_is_writable (entry);
-
-  /* update the model */
-  gtk_tree_model_foreach (key_entry->model, keybinding_key_changed_foreach, key_entry);
-}
-
-static void
-keybinding_description_changed (GConfClient *client,
-				guint        cnxn_id,
-				GConfEntry  *entry,
-				gpointer     user_data)
-{
-  KeyEntry *key_entry;
-  const gchar *key_value;
-
-  key_entry = (KeyEntry *) user_data;
-  key_value = entry->value ? gconf_value_get_string (entry->value) : NULL;
-
-  g_free (key_entry->description);
-  key_entry->description = key_value ? g_strdup (key_value) : NULL;
-  key_entry->desc_editable = gconf_entry_get_is_writable (entry);
-
-  /* update the model */
-  gtk_tree_model_foreach (key_entry->model, keybinding_key_changed_foreach, key_entry);
-}
-
-static void
-keybinding_command_changed (GConfClient *client,
-			guint        cnxn_id,
-			GConfEntry  *entry,
-			gpointer     user_data)
-{
-  KeyEntry *key_entry;
-  const gchar *key_value;
-
-  key_entry = (KeyEntry *) user_data;
-  key_value = entry->value ? gconf_value_get_string (entry->value) : NULL;
-
-  g_free (key_entry->command);
-  key_entry->command = key_value ? g_strdup (key_value) : NULL;
-  key_entry->cmd_editable = gconf_entry_get_is_writable (entry);
-
-  /* update the model */
-  gtk_tree_model_foreach (key_entry->model, keybinding_key_changed_foreach, key_entry);
-}
-
-static int
-keyentry_sort_func (GtkTreeModel *model,
-                    GtkTreeIter  *a,
-                    GtkTreeIter  *b,
-                    gpointer      user_data)
-{
-  KeyEntry *key_entry_a;
-  KeyEntry *key_entry_b;
-  int retval;
-
-  key_entry_a = NULL;
-  gtk_tree_model_get (model, a,
-                      KEYENTRY_COLUMN, &key_entry_a,
-                      -1);
-
-  key_entry_b = NULL;
-  gtk_tree_model_get (model, b,
-                      KEYENTRY_COLUMN, &key_entry_b,
-                      -1);
-
-  if (key_entry_a && key_entry_b)
-    {
-      if ((key_entry_a->keyval || key_entry_a->keycode) &&
-          (key_entry_b->keyval || key_entry_b->keycode))
-        {
-          gchar *name_a, *name_b;
-
-          name_a = binding_name (key_entry_a->keyval,
-                                 key_entry_a->keycode,
-                                 key_entry_a->mask,
-                                 TRUE);
-
-          name_b = binding_name (key_entry_b->keyval,
-                                 key_entry_b->keycode,
-                                 key_entry_b->mask,
-                                 TRUE);
-
-          retval = g_utf8_collate (name_a, name_b);
-
-          g_free (name_a);
-          g_free (name_b);
-        }
-      else if (key_entry_a->keyval || key_entry_a->keycode)
-        retval = -1;
-      else if (key_entry_b->keyval || key_entry_b->keycode)
-        retval = 1;
-      else
-        retval = 0;
-    }
-  else if (key_entry_a)
-    retval = -1;
-  else if (key_entry_b)
-    retval = 1;
-  else
-    retval = 0;
-
-  return retval;
-}
-
-static void
-clear_old_model (GtkBuilder *builder)
-{
-  GtkWidget *tree_view;
-  GtkWidget *actions_swindow;
-  GtkTreeModel *model;
-
-  tree_view = WID (builder, "shortcut_treeview");
-  model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
-
-  if (model == NULL)
-    {
-      /* create a new model */
-      model = (GtkTreeModel *) gtk_tree_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);
-
-      gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model),
-                                       KEYENTRY_COLUMN,
-                                       keyentry_sort_func,
-                                       NULL, NULL);
-
-      gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), model);
-
-      g_object_unref (model);
-    }
-  else
-    {
-      /* clear the existing model */
-      GConfClient *client;
-      gboolean valid;
-      GtkTreeIter iter;
-      KeyEntry *key_entry;
-
-      client = gconf_client_get_default ();
-      /* we need the schema name below;
-       * cached values do not have that set, though */
-      gconf_client_clear_cache (client);
-
-      for (valid = gtk_tree_model_get_iter_first (model, &iter);
-           valid;
-           valid = gtk_tree_model_iter_next (model, &iter))
-        {
-          gtk_tree_model_get (model, &iter,
-                              KEYENTRY_COLUMN, &key_entry,
-                              -1);
-
-          if (key_entry != NULL)
-            {
-              gconf_client_remove_dir (client, key_entry->gconf_key, NULL);
-              gconf_client_notify_remove (client, key_entry->gconf_cnxn);
-              if (key_entry->gconf_cnxn_desc != 0)
-                gconf_client_notify_remove (client, key_entry->gconf_cnxn_desc);
-              if (key_entry->gconf_cnxn_cmd != 0)
-                gconf_client_notify_remove (client, key_entry->gconf_cnxn_cmd);
-              g_free (key_entry->gconf_key);
-              g_free (key_entry->description);
-              g_free (key_entry->desc_gconf_key);
-              g_free (key_entry->command);
-              g_free (key_entry->cmd_gconf_key);
-              g_free (key_entry);
-            }
-        }
-
-      gtk_tree_store_clear (GTK_TREE_STORE (model));
-      g_object_unref (client);
-    }
-
-  actions_swindow = WID (builder, "actions_swindow");
-  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (actions_swindow),
-				  GTK_POLICY_NEVER, GTK_POLICY_NEVER);
-  gtk_widget_set_size_request (actions_swindow, -1, -1);
-}
-
 typedef struct {
   const char *key;
   gboolean found;
@@ -311,911 +81,3 @@ ensure_scrollbar (GtkBuilder *builder, int i)
 				      GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
     }
 }
-
-static void
-find_section (GtkTreeModel *model,
-              GtkTreeIter  *iter,
-	      const char   *title)
-{
-  gboolean success;
-
-  success = gtk_tree_model_get_iter_first (model, iter);
-  while (success)
-    {
-      char *description = NULL;
-
-      gtk_tree_model_get (model, iter,
-			  DESCRIPTION_COLUMN, &description,
-			  -1);
-
-      if (g_strcmp0 (description, title) == 0)
-        return;
-      success = gtk_tree_model_iter_next (model, iter);
-    }
-
-    gtk_tree_store_append (GTK_TREE_STORE (model), iter, NULL);
-    gtk_tree_store_set (GTK_TREE_STORE (model), iter,
-                        DESCRIPTION_COLUMN, title,
-                        -1);
-}
-
-static void
-append_keys_to_tree (GtkBuilder         *builder,
-		     const gchar        *title,
-		     const KeyListEntry *keys_list)
-{
-  GtkTreeIter parent_iter, iter;
-  GtkTreeModel *model;
-  gint i, j;
-
-  /* Try to find a section parent iter, if it already exists */
-  find_section (model, &iter, title);
-  parent_iter = iter;
-
-  i = 0;
-  gtk_tree_model_foreach (model, count_rows_foreach, &i);
-
-  /* If the header we just added is the MAX_ELEMENTS_BEFORE_SCROLLING th,
-   * then we need to scroll now */
-  ensure_scrollbar (builder, i - 1);
-
-===============================================================
-
-  /* Don't show an empty section */
-  if (gtk_tree_model_iter_n_children (model, &parent_iter) == 0)
-    gtk_tree_store_remove (GTK_TREE_STORE (model), &parent_iter);
-
-  if (i == 0)
-      gtk_widget_hide (WID (builder, "shortcuts_vbox"));
-  else
-      gtk_widget_show (WID (builder, "shortcuts_vbox"));
-}
-
-static void
-key_entry_controlling_key_changed (GConfClient *client,
-				   guint        cnxn_id,
-				   GConfEntry  *entry,
-				   gpointer     user_data)
-{
-  reload_key_entries (user_data);
-}
-
-static gboolean
-cb_check_for_uniqueness (GtkTreeModel *model,
-			 GtkTreePath  *path,
-			 GtkTreeIter  *iter,
-			 KeyEntry *new_key)
-{
-  KeyEntry *element;
-
-  gtk_tree_model_get (new_key->model, iter,
-		      KEYENTRY_COLUMN, &element,
-		      -1);
-
-  /* no conflict for : blanks, different modifiers, or ourselves */
-  if (element == NULL || new_key->mask != element->mask ||
-      !strcmp (new_key->gconf_key, element->gconf_key))
-    return FALSE;
-
-  if (new_key->keyval != 0) {
-      if (new_key->keyval != element->keyval)
-	  return FALSE;
-  } else if (element->keyval != 0 || new_key->keycode != element->keycode)
-    return FALSE;
-
-  new_key->editable = FALSE;
-  new_key->gconf_key = element->gconf_key;
-  new_key->description = element->description;
-  new_key->desc_gconf_key = element->desc_gconf_key;
-  new_key->desc_editable = element->desc_editable;
-  return TRUE;
-}
-
-static const guint forbidden_keyvals[] = {
-  /* Navigation keys */
-  GDK_KEY_Home,
-  GDK_KEY_Left,
-  GDK_KEY_Up,
-  GDK_KEY_Right,
-  GDK_KEY_Down,
-  GDK_KEY_Page_Up,
-  GDK_KEY_Page_Down,
-  GDK_KEY_End,
-  GDK_KEY_Tab,
-
-  /* Return */
-  GDK_KEY_KP_Enter,
-  GDK_KEY_Return,
-
-  GDK_KEY_space,
-  GDK_KEY_Mode_switch
-};
-
-static gboolean
-keyval_is_forbidden (guint keyval)
-{
-  guint i;
-
-  for (i = 0; i < G_N_ELEMENTS(forbidden_keyvals); i++) {
-    if (keyval == forbidden_keyvals[i])
-      return TRUE;
-  }
-
-  return FALSE;
-}
-
-static void
-show_error (GtkWindow *parent,
-	    GError *err)
-{
-  GtkWidget *dialog;
-
-  dialog = gtk_message_dialog_new (parent,
-				   GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
-				   GTK_MESSAGE_WARNING,
-				   GTK_BUTTONS_OK,
-				   _("Error saving the new shortcut"));
-
-  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                                            "%s", err->message);
-  gtk_dialog_run (GTK_DIALOG (dialog));
-  gtk_widget_destroy (dialog);
-}
-
-static void
-accel_edited_callback (GtkCellRendererText   *cell,
-                       const char            *path_string,
-                       guint                  keyval,
-                       EggVirtualModifierType mask,
-		       guint		      keycode,
-                       gpointer               data)
-{
-  GConfClient *client;
-  GtkTreeView *view = (GtkTreeView *)data;
-  GtkTreeModel *model;
-  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
-  GtkTreeIter iter;
-  KeyEntry *key_entry, tmp_key;
-  GError *err = NULL;
-  char *str;
-
-  block_accels = FALSE;
-
-  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,
-		      KEYENTRY_COLUMN, &key_entry,
-		      -1);
-
-  /* sanity check */
-  if (key_entry == NULL)
-    return;
-
-  /* CapsLock isn't supported as a keybinding modifier, so keep it from confusing us */
-  mask &= ~EGG_VIRTUAL_LOCK_MASK;
-
-  tmp_key.model  = model;
-  tmp_key.keyval = keyval;
-  tmp_key.keycode = keycode;
-  tmp_key.mask   = mask;
-  tmp_key.gconf_key = key_entry->gconf_key;
-  tmp_key.description = NULL;
-  tmp_key.editable = TRUE; /* kludge to stuff in a return flag */
-
-  if (keyval != 0 || keycode != 0) /* any number of keys can be disabled */
-    gtk_tree_model_foreach (model,
-      (GtkTreeModelForeachFunc) cb_check_for_uniqueness,
-      &tmp_key);
-
-  /* Check for unmodified keys */
-  if (tmp_key.mask == 0 && tmp_key.keycode != 0)
-    {
-      if ((tmp_key.keyval >= GDK_KEY_a && tmp_key.keyval <= GDK_KEY_z)
-	   || (tmp_key.keyval >= GDK_KEY_A && tmp_key.keyval <= GDK_KEY_Z)
-	   || (tmp_key.keyval >= GDK_KEY_0 && tmp_key.keyval <= GDK_KEY_9)
-	   || (tmp_key.keyval >= GDK_KEY_kana_fullstop && tmp_key.keyval <= GDK_KEY_semivoicedsound)
-	   || (tmp_key.keyval >= GDK_KEY_Arabic_comma && tmp_key.keyval <= GDK_KEY_Arabic_sukun)
-	   || (tmp_key.keyval >= GDK_KEY_Serbian_dje && tmp_key.keyval <= GDK_KEY_Cyrillic_HARDSIGN)
-	   || (tmp_key.keyval >= GDK_KEY_Greek_ALPHAaccent && tmp_key.keyval <= GDK_KEY_Greek_omega)
-	   || (tmp_key.keyval >= GDK_KEY_hebrew_doublelowline && tmp_key.keyval <= GDK_KEY_hebrew_taf)
-	   || (tmp_key.keyval >= GDK_KEY_Thai_kokai && tmp_key.keyval <= GDK_KEY_Thai_lekkao)
-	   || (tmp_key.keyval >= GDK_KEY_Hangul && tmp_key.keyval <= GDK_KEY_Hangul_Special)
-	   || (tmp_key.keyval >= GDK_KEY_Hangul_Kiyeog && tmp_key.keyval <= GDK_KEY_Hangul_J_YeorinHieuh)
-	   || keyval_is_forbidden (tmp_key.keyval)) {
-        GtkWidget *dialog;
-	char *name;
-
-	name = binding_name (keyval, keycode, mask, TRUE);
-
-	dialog =
-	  gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
-			  	  GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
-				  GTK_MESSAGE_WARNING,
-				  GTK_BUTTONS_CANCEL,
-				  _("The shortcut \"%s\" cannot be used because it will become impossible to type using this key.\n"
-				  "Please try with a key such as Control, Alt or Shift at the same time."),
-				  name);
-
-	g_free (name);
-	gtk_dialog_run (GTK_DIALOG (dialog));
-	gtk_widget_destroy (dialog);
-
-	/* set it back to its previous value. */
-	egg_cell_renderer_keys_set_accelerator
-	  (EGG_CELL_RENDERER_KEYS (cell),
-	   key_entry->keyval, key_entry->keycode, key_entry->mask);
-	return;
-      }
-    }
-
-  /* flag to see if the new accelerator was in use by something */
-  if (!tmp_key.editable)
-    {
-      GtkWidget *dialog;
-      char *name;
-      int response;
-
-      name = binding_name (keyval, keycode, mask, TRUE);
-
-      dialog =
-	gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
-				GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
-				GTK_MESSAGE_WARNING,
-				GTK_BUTTONS_CANCEL,
-				_("The shortcut \"%s\" is already used for\n\"%s\""),
-				name, tmp_key.description ?
-				tmp_key.description : tmp_key.gconf_key);
-      g_free (name);
-
-      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-	  _("If you reassign the shortcut to \"%s\", the \"%s\" shortcut "
-	    "will be disabled."),
-	  key_entry->description ?
-	  key_entry->description : key_entry->gconf_key,
-	  tmp_key.description ?
-	  tmp_key.description : tmp_key.gconf_key);
-
-      gtk_dialog_add_button (GTK_DIALOG (dialog),
-                             _("_Reassign"),
-			     GTK_RESPONSE_ACCEPT);
-
-      gtk_dialog_set_default_response (GTK_DIALOG (dialog),
-				       GTK_RESPONSE_ACCEPT);
-
-      response = gtk_dialog_run (GTK_DIALOG (dialog));
-      gtk_widget_destroy (dialog);
-
-      if (response == GTK_RESPONSE_ACCEPT)
-	{
-	  GConfClient *client;
-
-	  client = gconf_client_get_default ();
-
-          gconf_client_set_string (client,
-				   tmp_key.gconf_key,
-				   "", &err);
-
-	  if (err != NULL)
-	    {
-	       show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
-			   err);
-	       g_error_free (err);
-	       g_object_unref (client);
-	       return;
-	    }
-
-	  str = binding_name (keyval, keycode, mask, FALSE);
-	  gconf_client_set_string (client,
-				   key_entry->gconf_key,
-				   str, &err);
-
-	  if (err != NULL)
-	    {
-	       show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
-			   err);
-	       g_error_free (err);
-
-	       /* reset the previous shortcut */
-	       gconf_client_set_string (client,
-					tmp_key.gconf_key,
-					str, NULL);
-	    }
-
-	  g_free (str);
-	  g_object_unref (client);
-	}
-      else
-	{
-	  /* set it back to its previous value. */
-	  egg_cell_renderer_keys_set_accelerator (EGG_CELL_RENDERER_KEYS (cell),
-						  key_entry->keyval,
-						  key_entry->keycode,
-						  key_entry->mask);
-	}
-
-      return;
-    }
-
-  str = binding_name (keyval, keycode, mask, FALSE);
-
-  client = gconf_client_get_default ();
-  gconf_client_set_string (client,
-                           key_entry->gconf_key,
-                           str,
-                           &err);
-  g_free (str);
-  g_object_unref (client);
-
-  if (err != NULL)
-    {
-      show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))), err);
-      g_error_free (err);
-      key_entry->editable = FALSE;
-    }
-}
-
-static void
-accel_cleared_callback (GtkCellRendererText *cell,
-			const char          *path_string,
-			gpointer             data)
-{
-  GConfClient *client;
-  GtkTreeView *view = (GtkTreeView *) data;
-  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
-  KeyEntry *key_entry;
-  GtkTreeIter iter;
-  GError *err = NULL;
-  GtkTreeModel *model;
-
-  block_accels = FALSE;
-
-  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,
-		      KEYENTRY_COLUMN, &key_entry,
-		      -1);
-
-  /* sanity check */
-  if (key_entry == NULL)
-    return;
-
-  /* Unset the key */
-  client = gconf_client_get_default();
-  gconf_client_set_string (client,
-			   key_entry->gconf_key,
-			   "",
-			   &err);
-  g_object_unref (client);
-
-  if (err != NULL)
-    {
-      GtkWidget *dialog;
-
-      dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
-				       GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
-				       GTK_MESSAGE_WARNING,
-				       GTK_BUTTONS_OK,
-				       _("Error unsetting accelerator in configuration database: %s"),
-				       err->message);
-      gtk_dialog_run (GTK_DIALOG (dialog));
-
-      gtk_widget_destroy (dialog);
-      g_error_free (err);
-      key_entry->editable = FALSE;
-    }
-}
-
-static void
-description_edited_callback (GtkCellRendererText *renderer,
-                             gchar               *path_string,
-                             gchar               *new_text,
-                             gpointer             user_data)
-{
-  GConfClient *client;
-  GtkTreeView *view = GTK_TREE_VIEW (user_data);
-  GtkTreeModel *model;
-  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
-  GtkTreeIter iter;
-  KeyEntry *key_entry;
-
-  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,
-		      KEYENTRY_COLUMN, &key_entry,
-		      -1);
-
-  /* sanity check */
-  if (key_entry == NULL || key_entry->desc_gconf_key == NULL)
-    return;
-
-  client = gconf_client_get_default ();
-  if (!gconf_client_set_string (client, key_entry->desc_gconf_key, new_text, NULL))
-    key_entry->desc_editable = FALSE;
-
-  g_object_unref (client);
-}
-
-
-typedef struct
-{
-  GtkTreeView *tree_view;
-  GtkTreePath *path;
-  GtkTreeViewColumn *column;
-} IdleData;
-
-static gboolean
-real_start_editing_cb (IdleData *idle_data)
-{
-  gtk_widget_grab_focus (GTK_WIDGET (idle_data->tree_view));
-  gtk_tree_view_set_cursor (idle_data->tree_view,
-			    idle_data->path,
-			    idle_data->column,
-			    TRUE);
-  gtk_tree_path_free (idle_data->path);
-  g_free (idle_data);
-  return FALSE;
-}
-
-static gboolean
-edit_custom_shortcut (KeyEntry *key)
-{
-  gint result;
-  const gchar *text;
-  gboolean ret;
-
-  gtk_entry_set_text (GTK_ENTRY (custom_shortcut_name_entry), key->description ? key->description : "");
-  gtk_widget_set_sensitive (custom_shortcut_name_entry, key->desc_editable);
-  gtk_widget_grab_focus (custom_shortcut_name_entry);
-  gtk_entry_set_text (GTK_ENTRY (custom_shortcut_command_entry), key->command ? key->command : "");
-  gtk_widget_set_sensitive (custom_shortcut_command_entry, key->cmd_editable);
-
-  gtk_window_present (GTK_WINDOW (custom_shortcut_dialog));
-  result = gtk_dialog_run (GTK_DIALOG (custom_shortcut_dialog));
-  switch (result)
-    {
-    case GTK_RESPONSE_OK:
-      text = gtk_entry_get_text (GTK_ENTRY (custom_shortcut_name_entry));
-      g_free (key->description);
-      key->description = g_strdup (text);
-      text = gtk_entry_get_text (GTK_ENTRY (custom_shortcut_command_entry));
-      g_free (key->command);
-      key->command = g_strdup (text);
-      ret = TRUE;
-      break;
-    default:
-      ret = FALSE;
-      break;
-    }
-
-  gtk_widget_hide (custom_shortcut_dialog);
-
-  return ret;
-}
-
-static gboolean
-remove_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
-{
-  GtkTreeIter parent;
-  GConfClient *client;
-  gchar *base;
-  KeyEntry *key;
-
-  gtk_tree_model_get (model, iter,
-                      KEYENTRY_COLUMN, &key,
-                      -1);
-
-  /* not a custom shortcut */
-  if (key->command == NULL)
-    return FALSE;
-
-  client = gconf_client_get_default ();
-
-  gconf_client_notify_remove (client, key->gconf_cnxn);
-  if (key->gconf_cnxn_desc != 0)
-    gconf_client_notify_remove (client, key->gconf_cnxn_desc);
-  if (key->gconf_cnxn_cmd != 0)
-    gconf_client_notify_remove (client, key->gconf_cnxn_cmd);
-
-  base = g_path_get_dirname (key->gconf_key);
-  gconf_client_recursive_unset (client, base, 0, NULL);
-  g_free (base);
-  /* suggest sync now so the unset directory actually gets dropped;
-   * if we don't do this we may end up with 'zombie' shortcuts when
-   * restarting the app */
-  gconf_client_suggest_sync (client, NULL);
-  g_object_unref (client);
-
-  g_free (key->gconf_key);
-  g_free (key->description);
-  g_free (key->desc_gconf_key);
-  g_free (key->command);
-  g_free (key->cmd_gconf_key);
-  g_free (key);
-
-  gtk_tree_model_iter_parent (model, &parent, iter);
-  gtk_tree_store_remove (GTK_TREE_STORE (model), iter);
-  if (!gtk_tree_model_iter_has_child (model, &parent))
-    gtk_tree_store_remove (GTK_TREE_STORE (model), &parent);
-
-  return TRUE;
-}
-
-static void
-update_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
-{
-  KeyEntry *key;
-
-  gtk_tree_model_get (model, iter,
-                      KEYENTRY_COLUMN, &key,
-                      -1);
-
-  edit_custom_shortcut (key);
-  if (key->command == NULL || key->command[0] == '\0')
-    {
-      remove_custom_shortcut (model, iter);
-    }
-  else
-    {
-      GConfClient *client;
-
-      gtk_tree_store_set (GTK_TREE_STORE (model), iter,
-			  KEYENTRY_COLUMN, key, -1);
-      client = gconf_client_get_default ();
-      if (key->description != NULL)
-        gconf_client_set_string (client, key->desc_gconf_key, key->description, NULL);
-      else
-        gconf_client_unset (client, key->desc_gconf_key, NULL);
-      gconf_client_set_string (client, key->cmd_gconf_key, key->command, NULL);
-      g_object_unref (client);
-    }
-}
-
-static gchar *
-find_free_gconf_key (GError **error)
-{
-  GConfClient *client;
-
-  gchar *dir;
-  int i;
-
-  client = gconf_client_get_default ();
-
-  for (i = 0; i < MAX_CUSTOM_SHORTCUTS; i++)
-    {
-      dir = g_strdup_printf ("%s/custom%d", GCONF_BINDING_DIR, i);
-      if (!gconf_client_dir_exists (client, dir, NULL))
-        break;
-      g_free (dir);
-    }
-
-  if (i == MAX_CUSTOM_SHORTCUTS)
-    {
-      dir = NULL;
-      g_set_error_literal (error,
-                           g_quark_from_string ("Keyboard Shortcuts"),
-                           0,
-                           _("Too many custom shortcuts"));
-    }
-
-  g_object_unref (client);
-
-  return dir;
-}
-
-static void
-add_custom_shortcut (GtkTreeView  *tree_view,
-                     GtkTreeModel *model)
-{
-  KeyEntry *key_entry;
-  GtkTreeIter iter;
-  GtkTreeIter parent_iter;
-  GtkTreePath *path;
-  gchar *dir;
-  GConfClient *client;
-  GError *error;
-
-  error = NULL;
-  dir = find_free_gconf_key (&error);
-  if (dir == NULL)
-    {
-      show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tree_view))), error);
-
-      g_error_free (error);
-      return;
-    }
-
-  key_entry = g_new0 (KeyEntry, 1);
-  key_entry->gconf_key = g_strconcat (dir, "/binding", NULL);
-  key_entry->editable = TRUE;
-  key_entry->model = model;
-  key_entry->desc_gconf_key = g_strconcat (dir, "/name", NULL);
-  key_entry->description = g_strdup ("");
-  key_entry->desc_editable = TRUE;
-  key_entry->cmd_gconf_key = g_strconcat (dir, "/action", NULL);
-  key_entry->command = g_strdup ("");
-  key_entry->cmd_editable = TRUE;
-  g_free (dir);
-
-  if (edit_custom_shortcut (key_entry) &&
-      key_entry->command && key_entry->command[0])
-    {
-      find_section (model, &iter, _("Custom Shortcuts"));
-      parent_iter = iter;
-      gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent_iter);
-      gtk_tree_store_set (GTK_TREE_STORE (model), &iter, KEYENTRY_COLUMN, key_entry, -1);
-
-      /* store in gconf */
-      client = gconf_client_get_default ();
-      gconf_client_set_string (client, key_entry->gconf_key, "", NULL);
-      gconf_client_set_string (client, key_entry->desc_gconf_key, key_entry->description, NULL);
-      gconf_client_set_string (client, key_entry->cmd_gconf_key, key_entry->command, NULL);
-
-      /* add gconf watches */
-      key_entry->gconf_cnxn_desc = gconf_client_notify_add (client,
-                                                            key_entry->desc_gconf_key,
-					    		    (GConfClientNotifyFunc) &keybinding_description_changed,
-							    key_entry, NULL, NULL);
-      key_entry->gconf_cnxn_cmd = gconf_client_notify_add (client,
-	                                                   key_entry->cmd_gconf_key,
-						           (GConfClientNotifyFunc) &keybinding_command_changed,
-						           key_entry, NULL, NULL);
-      key_entry->gconf_cnxn = gconf_client_notify_add (client,
-	                                               key_entry->gconf_key,
-						       (GConfClientNotifyFunc) &keybinding_key_changed,
-					               key_entry, NULL, NULL);
-
-
-      g_object_unref (client);
-
-      /* make the new shortcut visible */
-      path = gtk_tree_model_get_path (model, &iter);
-      gtk_tree_view_expand_to_path (tree_view, path);
-      gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0, 0);
-      gtk_tree_path_free (path);
-    }
-  else
-    {
-      g_free (key_entry->gconf_key);
-      g_free (key_entry->description);
-      g_free (key_entry->desc_gconf_key);
-      g_free (key_entry->command);
-      g_free (key_entry->cmd_gconf_key);
-      g_free (key_entry);
-    }
-}
-
-static void
-start_editing_kb_cb (GtkTreeView *treeview,
-			  GtkTreePath *path,
-			  GtkTreeViewColumn *column,
-			  gpointer user_data)
-{
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  KeyEntry *key;
-
-  model = gtk_tree_view_get_model (treeview);
-  gtk_tree_model_get_iter (model, &iter, path);
-  gtk_tree_model_get (model, &iter,
-		      KEYENTRY_COLUMN, &key,
-		      -1);
-
-  if (key == NULL)
-    {
-      /* This is a section heading - expand or collapse */
-      if (gtk_tree_view_row_expanded (treeview, path))
-	gtk_tree_view_collapse_row (treeview, path);
-      else
-	gtk_tree_view_expand_row (treeview, path, FALSE);
-      return;
-    }
-
-  /* if only the accel can be edited on the selected row
-   * always select the accel column */
-  if (key->desc_editable &&
-      column == gtk_tree_view_get_column (treeview, 0))
-    {
-      gtk_widget_grab_focus (GTK_WIDGET (treeview));
-      gtk_tree_view_set_cursor (treeview, path,
-				gtk_tree_view_get_column (treeview, 0),
-				FALSE);
-      update_custom_shortcut (model, &iter);
-    }
-  else
-    {
-       gtk_widget_grab_focus (GTK_WIDGET (treeview));
-       gtk_tree_view_set_cursor (treeview,
-				 path,
-				 gtk_tree_view_get_column (treeview, 1),
-				 TRUE);
-    }
-}
-
-static gboolean
-start_editing_cb (GtkTreeView    *tree_view,
-		  GdkEventButton *event,
-		  gpointer        user_data)
-{
-  GtkTreePath *path;
-  GtkTreeViewColumn *column;
-
-  if (event->window != gtk_tree_view_get_bin_window (tree_view))
-    return FALSE;
-
-  if (gtk_tree_view_get_path_at_pos (tree_view,
-				     (gint) event->x,
-				     (gint) event->y,
-				     &path, &column,
-				     NULL, NULL))
-    {
-      IdleData *idle_data;
-      GtkTreeModel *model;
-      GtkTreeIter iter;
-      KeyEntry *key;
-
-      if (gtk_tree_path_get_depth (path) == 1)
-	{
-	  gtk_tree_path_free (path);
-	  return FALSE;
-	}
-
-      model = gtk_tree_view_get_model (tree_view);
-      gtk_tree_model_get_iter (model, &iter, path);
-      gtk_tree_model_get (model, &iter,
-                          KEYENTRY_COLUMN, &key,
-                         -1);
-
-      /* if only the accel can be edited on the selected row
-       * always select the accel column */
-      if (key->desc_editable &&
-          column == gtk_tree_view_get_column (tree_view, 0))
-        {
-          gtk_widget_grab_focus (GTK_WIDGET (tree_view));
-          gtk_tree_view_set_cursor (tree_view, path,
-                                    gtk_tree_view_get_column (tree_view, 0),
-                                    FALSE);
-          update_custom_shortcut (model, &iter);
-        }
-      else
-        {
-          idle_data = g_new (IdleData, 1);
-          idle_data->tree_view = tree_view;
-          idle_data->path = path;
-          idle_data->column = key->desc_editable ? column :
-                              gtk_tree_view_get_column (tree_view, 1);
-          g_idle_add ((GSourceFunc) real_start_editing_cb, idle_data);
-          block_accels = TRUE;
-        }
-      g_signal_stop_emission_by_name (tree_view, "button_press_event");
-    }
-  return TRUE;
-}
-
-/* this handler is used to keep accels from activating while the user
- * is assigning a new shortcut so that he won't accidentally trigger one
- * of the widgets */
-static gboolean
-maybe_block_accels (GtkWidget *widget,
-                    GdkEventKey *event,
-                    gpointer user_data)
-{
-  if (block_accels)
-  {
-    return gtk_window_propagate_key_event (GTK_WINDOW (widget), event);
-  }
-  return FALSE;
-}
-
-#if 0
-static void
-cb_dialog_response (GtkWidget *widget, gint response_id, gpointer data)
-{
-  GtkBuilder *builder = data;
-  GtkTreeView *treeview;
-  GtkTreeModel *model;
-  GtkTreeSelection *selection;
-  GtkTreeIter iter;
-
-  treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
-                                                    "shortcut_treeview"));
-  model = gtk_tree_view_get_model (treeview);
-
-  if (response_id == GTK_RESPONSE_HELP)
-    {
-      capplet_help (GTK_WINDOW (widget),
-                    "goscustdesk-39");
-    }
-  else if (response_id == RESPONSE_ADD)
-    {
-      add_custom_shortcut (treeview, model);
-    }
-  else if (response_id == RESPONSE_REMOVE)
-    {
-      selection = gtk_tree_view_get_selection (treeview);
-      if (gtk_tree_selection_get_selected (selection, NULL, &iter))
-        {
-          remove_custom_shortcut (model, &iter);
-        }
-    }
-  else
-    {
-      clear_old_model (builder);
-      gtk_main_quit ();
-    }
-}
-#endif
-
-static void
-add_button_clicked (GtkWidget  *button,
-                    GtkBuilder *builder)
-{
-  GtkTreeView *treeview;
-  GtkTreeModel *model;
-
-  treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
-                                                    "shortcut_treeview"));
-  model = gtk_tree_view_get_model (treeview);
-
-  add_custom_shortcut (treeview, model);
-}
-
-static void
-remove_button_clicked (GtkWidget  *button,
-                       GtkBuilder *builder)
-{
-  GtkTreeView *treeview;
-  GtkTreeModel *model;
-  GtkTreeSelection *selection;
-  GtkTreeIter iter;
-
-  treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
-                                                    "shortcut_treeview"));
-  model = gtk_tree_view_get_model (treeview);
-
-  selection = gtk_tree_view_get_selection (treeview);
-  if (gtk_tree_selection_get_selected (selection, NULL, &iter))
-    {
-      remove_custom_shortcut (model, &iter);
-    }
-}
-
-static void
-on_window_manager_change (const char *wm_name, GtkBuilder *builder)
-{
-  reload_key_entries (builder);
-}
-
-void
-gnome_keybinding_properties_init (CcPanel *panel, GtkBuilder *builder)
-{
-  wm_common_register_window_manager_change ((GFunc) on_window_manager_change,
-                                            builder);
-
-}
-
-void
-gnome_keybinding_properties_dispose (CcPanel *panel)
-{
-  if (maybe_block_accels_id != 0)
-    {
-      CcShell *shell;
-      GtkWidget *toplevel;
-
-      shell = cc_panel_get_shell (CC_PANEL (panel));
-      toplevel = cc_shell_get_toplevel (shell);
-
-      g_signal_handler_disconnect (toplevel, maybe_block_accels_id);
-      maybe_block_accels_id = 0;
-
-      if (keyb_sections != NULL)
-        g_hash_table_destroy (keyb_sections);
-    }
-}
-
-/*
- * vim: sw=2 ts=8 cindent noai bs=2
- */
diff --git a/panels/keyboard/keyboard-shortcuts.c b/panels/keyboard/keyboard-shortcuts.c
index 8f60356..86dc433 100644
--- a/panels/keyboard/keyboard-shortcuts.c
+++ b/panels/keyboard/keyboard-shortcuts.c
@@ -25,6 +25,7 @@
 #include "keyboard-shortcuts.h"
 #include "wm-common.h"
 
+#define MAX_CUSTOM_SHORTCUTS 1000
 #define GCONF_BINDING_DIR "/desktop/gnome/keybindings"
 #define WID(builder, name) (GTK_WIDGET (gtk_builder_get_object (builder, name)))
 
@@ -82,6 +83,7 @@ enum
 };
 
 static guint maybe_block_accels_id = 0;
+static gboolean block_accels = FALSE;
 static GtkWidget *custom_shortcut_dialog = NULL;
 static GtkWidget *custom_shortcut_name_entry = NULL;
 static GtkWidget *custom_shortcut_command_entry = NULL;
@@ -96,14 +98,30 @@ free_key_array (GPtrArray *keys)
 
       for (i = 0; i < keys->len; i++)
         {
-	  KeyEntry *entry;
-
-	  entry = g_ptr_array_index (keys, i);
-	  g_free (entry->gconf_key);
-	  g_free (entry->description);
-	  g_free (entry->desc_gconf_key);
-	  g_free (entry->command);
-	  g_free (entry->cmd_gconf_key);
+	  KeyEntry *key_entry;
+	  GConfClient *client;
+
+	  key_entry = g_ptr_array_index (keys, i);
+
+	  /* Remove GConf watches */
+	  client = gconf_client_get_default ();
+	  gconf_client_remove_dir (client, key_entry->gconf_key, NULL);
+	  gconf_client_notify_remove (client, key_entry->gconf_cnxn);
+	  if (key_entry->gconf_cnxn_desc != 0)
+	    gconf_client_notify_remove (client, key_entry->gconf_cnxn_desc);
+	  if (key_entry->gconf_cnxn_cmd != 0)
+	    gconf_client_notify_remove (client, key_entry->gconf_cnxn_cmd);
+
+	  g_object_unref (client);
+
+	  /* Free memory */
+	  g_free (key_entry->gconf_key);
+	  g_free (key_entry->description);
+	  g_free (key_entry->desc_gconf_key);
+	  g_free (key_entry->command);
+	  g_free (key_entry->cmd_gconf_key);
+
+	  g_free (key_entry);
 	}
 
       g_ptr_array_free (keys, TRUE);
@@ -171,6 +189,87 @@ binding_from_string (const char             *str,
     return TRUE;
 }
 
+static gboolean
+keybinding_key_changed_foreach (GtkTreeModel *model,
+				GtkTreePath  *path,
+				GtkTreeIter  *iter,
+				gpointer      user_data)
+{
+  KeyEntry *key_entry;
+  KeyEntry *tmp_key_entry;
+
+  key_entry = (KeyEntry *)user_data;
+  gtk_tree_model_get (key_entry->model, iter,
+		      KEYENTRY_COLUMN, &tmp_key_entry,
+		      -1);
+
+  if (key_entry == tmp_key_entry)
+    {
+      gtk_tree_model_row_changed (key_entry->model, path, iter);
+      return TRUE;
+    }
+  return FALSE;
+}
+
+static void
+keybinding_description_changed (GConfClient *client,
+				guint        cnxn_id,
+				GConfEntry  *entry,
+				gpointer     user_data)
+{
+  KeyEntry *key_entry;
+  const gchar *key_value;
+
+  key_entry = (KeyEntry *) user_data;
+  key_value = entry->value ? gconf_value_get_string (entry->value) : NULL;
+
+  g_free (key_entry->description);
+  key_entry->description = key_value ? g_strdup (key_value) : NULL;
+  key_entry->desc_editable = gconf_entry_get_is_writable (entry);
+
+  /* update the model */
+  gtk_tree_model_foreach (key_entry->model, keybinding_key_changed_foreach, key_entry);
+}
+
+static void
+keybinding_key_changed (GConfClient *client,
+			guint        cnxn_id,
+			GConfEntry  *entry,
+			gpointer     user_data)
+{
+  KeyEntry *key_entry;
+  const gchar *key_value;
+
+  key_entry = (KeyEntry *) user_data;
+  key_value = entry->value ? gconf_value_get_string (entry->value) : NULL;
+
+  binding_from_string (key_value, &key_entry->keyval, &key_entry->keycode, &key_entry->mask);
+  key_entry->editable = gconf_entry_get_is_writable (entry);
+
+  /* update the model */
+  gtk_tree_model_foreach (key_entry->model, keybinding_key_changed_foreach, key_entry);
+}
+
+static void
+keybinding_command_changed (GConfClient *client,
+			guint        cnxn_id,
+			GConfEntry  *entry,
+			gpointer     user_data)
+{
+  KeyEntry *key_entry;
+  const gchar *key_value;
+
+  key_entry = (KeyEntry *) user_data;
+  key_value = entry->value ? gconf_value_get_string (entry->value) : NULL;
+
+  g_free (key_entry->command);
+  key_entry->command = key_value ? g_strdup (key_value) : NULL;
+  key_entry->cmd_editable = gconf_entry_get_is_writable (entry);
+
+  /* update the model */
+  gtk_tree_model_foreach (key_entry->model, keybinding_key_changed_foreach, key_entry);
+}
+
 static void
 append_section (GtkBuilder *builder, const gchar *title, const KeyListEntry *keys_list)
 {
@@ -258,26 +357,26 @@ append_section (GtkBuilder *builder, const gchar *title, const KeyListEntry *key
         {
           key_entry->desc_gconf_key =  g_strdup (keys_list[i].description_name);
           key_entry->desc_editable = gconf_client_key_is_writable (client, key_entry->desc_gconf_key, NULL);
-          /* key_entry->gconf_cnxn_desc = gconf_client_notify_add (client, */
-	  /* 							key_entry->desc_gconf_key, */
-	  /* 							(GConfClientNotifyFunc) &keybinding_description_changed, */
-	  /* 							key_entry, NULL, NULL); */
+          key_entry->gconf_cnxn_desc = gconf_client_notify_add (client, 
+	   							key_entry->desc_gconf_key,
+	   							(GConfClientNotifyFunc) &keybinding_description_changed,
+	   							key_entry, NULL, NULL);
         }
       if (keys_list[i].cmd_name != NULL)
         {
           key_entry->cmd_gconf_key =  g_strdup (keys_list[i].cmd_name);
           key_entry->cmd_editable = gconf_client_key_is_writable (client, key_entry->cmd_gconf_key, NULL);
-          /* key_entry->gconf_cnxn_cmd = gconf_client_notify_add (client, */
-	  /* 						       key_entry->cmd_gconf_key, */
-	  /* 							(GConfClientNotifyFunc) &keybinding_command_changed, */
-	  /* 							key_entry, NULL, NULL); */
+          key_entry->gconf_cnxn_cmd = gconf_client_notify_add (client,
+	   						       key_entry->cmd_gconf_key,
+							       (GConfClientNotifyFunc) &keybinding_command_changed,
+							       key_entry, NULL, NULL);
         }
 
       gconf_client_add_dir (client, key_string, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
-      /* key_entry->gconf_cnxn = gconf_client_notify_add (client, */
-      /* 						       key_string, */
-      /* 						       (GConfClientNotifyFunc) &keybinding_key_changed, */
-      /* 						       key_entry, NULL, NULL); */
+      key_entry->gconf_cnxn = gconf_client_notify_add (client,
+       						       key_string,
+       						       (GConfClientNotifyFunc) &keybinding_key_changed,
+       						       key_entry, NULL, NULL);
 
       key_value = gconf_client_get_string (client, key_string, NULL);
       binding_from_string (key_value, &key_entry->keyval, &key_entry->keycode, &key_entry->mask);
@@ -285,8 +384,6 @@ append_section (GtkBuilder *builder, const gchar *title, const KeyListEntry *key
 
       gconf_entry_free (entry);
 
-      g_print ("Adding %s to section %s\n", key_entry->description, title);
-
       g_ptr_array_add (keys_array, key_entry);
     }
 
@@ -707,9 +804,6 @@ section_selection_changed (GtkTreeSelection *selection, gpointer data)
 	  GtkTreeIter new_row;
 	  KeyEntry *entry = g_ptr_array_index (keys, i);
 
-	  g_print ("Adding gconf: %s, keyval = %d\n",
-		   entry->gconf_key, entry->keyval);
-
 	  gtk_list_store_append (GTK_LIST_STORE (shortcut_model), &new_row);
 	  gtk_list_store_set (GTK_LIST_STORE (shortcut_model), &new_row,
 			      DESCRIPTION_COLUMN, entry->description,
@@ -739,6 +833,859 @@ shortcut_selection_changed (GtkTreeSelection *selection, gpointer data)
   gtk_widget_set_sensitive (button, can_remove);
 }
 
+typedef struct
+{
+  GtkTreeView *tree_view;
+  GtkTreePath *path;
+  GtkTreeViewColumn *column;
+} IdleData;
+
+static gboolean
+edit_custom_shortcut (KeyEntry *key)
+{
+  gint result;
+  const gchar *text;
+  gboolean ret;
+
+  gtk_entry_set_text (GTK_ENTRY (custom_shortcut_name_entry), key->description ? key->description : "");
+  gtk_widget_set_sensitive (custom_shortcut_name_entry, key->desc_editable);
+  gtk_widget_grab_focus (custom_shortcut_name_entry);
+  gtk_entry_set_text (GTK_ENTRY (custom_shortcut_command_entry), key->command ? key->command : "");
+  gtk_widget_set_sensitive (custom_shortcut_command_entry, key->cmd_editable);
+
+  gtk_window_present (GTK_WINDOW (custom_shortcut_dialog));
+  result = gtk_dialog_run (GTK_DIALOG (custom_shortcut_dialog));
+  switch (result)
+    {
+    case GTK_RESPONSE_OK:
+      text = gtk_entry_get_text (GTK_ENTRY (custom_shortcut_name_entry));
+      g_free (key->description);
+      key->description = g_strdup (text);
+      text = gtk_entry_get_text (GTK_ENTRY (custom_shortcut_command_entry));
+      g_free (key->command);
+      key->command = g_strdup (text);
+      ret = TRUE;
+      break;
+    default:
+      ret = FALSE;
+      break;
+    }
+
+  gtk_widget_hide (custom_shortcut_dialog);
+
+  return ret;
+}
+
+static gboolean
+remove_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
+{
+  GtkTreeIter parent;
+  GConfClient *client;
+  gchar *base;
+  KeyEntry *key;
+
+  gtk_tree_model_get (model, iter,
+                      KEYENTRY_COLUMN, &key,
+                      -1);
+
+  /* not a custom shortcut */
+  if (key->command == NULL)
+    return FALSE;
+
+  client = gconf_client_get_default ();
+
+  gconf_client_notify_remove (client, key->gconf_cnxn);
+  if (key->gconf_cnxn_desc != 0)
+    gconf_client_notify_remove (client, key->gconf_cnxn_desc);
+  if (key->gconf_cnxn_cmd != 0)
+    gconf_client_notify_remove (client, key->gconf_cnxn_cmd);
+
+  base = g_path_get_dirname (key->gconf_key);
+  gconf_client_recursive_unset (client, base, 0, NULL);
+  g_free (base);
+  /* suggest sync now so the unset directory actually gets dropped;
+   * if we don't do this we may end up with 'zombie' shortcuts when
+   * restarting the app */
+  gconf_client_suggest_sync (client, NULL);
+  g_object_unref (client);
+
+  g_free (key->gconf_key);
+  g_free (key->description);
+  g_free (key->desc_gconf_key);
+  g_free (key->command);
+  g_free (key->cmd_gconf_key);
+  g_free (key);
+
+  gtk_tree_model_iter_parent (model, &parent, iter);
+  gtk_tree_store_remove (GTK_TREE_STORE (model), iter);
+  if (!gtk_tree_model_iter_has_child (model, &parent))
+    gtk_tree_store_remove (GTK_TREE_STORE (model), &parent);
+
+  return TRUE;
+}
+
+static void
+update_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
+{
+  KeyEntry *key;
+
+  gtk_tree_model_get (model, iter,
+                      KEYENTRY_COLUMN, &key,
+                      -1);
+
+  edit_custom_shortcut (key);
+  if (key->command == NULL || key->command[0] == '\0')
+    {
+      remove_custom_shortcut (model, iter);
+    }
+  else
+    {
+      GConfClient *client;
+
+      gtk_tree_store_set (GTK_TREE_STORE (model), iter,
+			  KEYENTRY_COLUMN, key, -1);
+      client = gconf_client_get_default ();
+      if (key->description != NULL)
+        gconf_client_set_string (client, key->desc_gconf_key, key->description, NULL);
+      else
+        gconf_client_unset (client, key->desc_gconf_key, NULL);
+      gconf_client_set_string (client, key->cmd_gconf_key, key->command, NULL);
+      g_object_unref (client);
+    }
+}
+
+static gboolean
+real_start_editing_cb (IdleData *idle_data)
+{
+  gtk_widget_grab_focus (GTK_WIDGET (idle_data->tree_view));
+  gtk_tree_view_set_cursor (idle_data->tree_view,
+			    idle_data->path,
+			    idle_data->column,
+			    TRUE);
+  gtk_tree_path_free (idle_data->path);
+  g_free (idle_data);
+  return FALSE;
+}
+
+static gboolean
+start_editing_cb (GtkTreeView    *tree_view,
+		  GdkEventButton *event,
+		  gpointer        user_data)
+{
+  GtkTreePath *path;
+  GtkTreeViewColumn *column;
+
+  if (event->window != gtk_tree_view_get_bin_window (tree_view))
+    return FALSE;
+
+  if (gtk_tree_view_get_path_at_pos (tree_view,
+				     (gint) event->x,
+				     (gint) event->y,
+				     &path, &column,
+				     NULL, NULL))
+    {
+      IdleData *idle_data;
+      GtkTreeModel *model;
+      GtkTreeIter iter;
+      KeyEntry *key;
+
+      if (gtk_tree_path_get_depth (path) == 1)
+	{
+	  gtk_tree_path_free (path);
+	  return FALSE;
+	}
+
+      model = gtk_tree_view_get_model (tree_view);
+      gtk_tree_model_get_iter (model, &iter, path);
+      gtk_tree_model_get (model, &iter,
+                          KEYENTRY_COLUMN, &key,
+                         -1);
+
+      /* if only the accel can be edited on the selected row
+       * always select the accel column */
+      if (key->desc_editable &&
+          column == gtk_tree_view_get_column (tree_view, 0))
+        {
+          gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+          gtk_tree_view_set_cursor (tree_view, path,
+                                    gtk_tree_view_get_column (tree_view, 0),
+                                    FALSE);
+          update_custom_shortcut (model, &iter);
+        }
+      else
+        {
+          idle_data = g_new (IdleData, 1);
+          idle_data->tree_view = tree_view;
+          idle_data->path = path;
+          idle_data->column = key->desc_editable ? column :
+                              gtk_tree_view_get_column (tree_view, 1);
+          g_idle_add ((GSourceFunc) real_start_editing_cb, idle_data);
+          block_accels = TRUE;
+        }
+      g_signal_stop_emission_by_name (tree_view, "button_press_event");
+    }
+  return TRUE;
+}
+
+static void
+start_editing_kb_cb (GtkTreeView *treeview,
+			  GtkTreePath *path,
+			  GtkTreeViewColumn *column,
+			  gpointer user_data)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  KeyEntry *key;
+
+  model = gtk_tree_view_get_model (treeview);
+  gtk_tree_model_get_iter (model, &iter, path);
+  gtk_tree_model_get (model, &iter,
+		      KEYENTRY_COLUMN, &key,
+		      -1);
+
+  if (key == NULL)
+    {
+      /* This is a section heading - expand or collapse */
+      if (gtk_tree_view_row_expanded (treeview, path))
+	gtk_tree_view_collapse_row (treeview, path);
+      else
+	gtk_tree_view_expand_row (treeview, path, FALSE);
+      return;
+    }
+
+  /* if only the accel can be edited on the selected row
+   * always select the accel column */
+  if (key->desc_editable &&
+      column == gtk_tree_view_get_column (treeview, 0))
+    {
+      gtk_widget_grab_focus (GTK_WIDGET (treeview));
+      gtk_tree_view_set_cursor (treeview, path,
+				gtk_tree_view_get_column (treeview, 0),
+				FALSE);
+      update_custom_shortcut (model, &iter);
+    }
+  else
+    {
+       gtk_widget_grab_focus (GTK_WIDGET (treeview));
+       gtk_tree_view_set_cursor (treeview,
+				 path,
+				 gtk_tree_view_get_column (treeview, 1),
+				 TRUE);
+    }
+}
+
+static void
+description_edited_callback (GtkCellRendererText *renderer,
+                             gchar               *path_string,
+                             gchar               *new_text,
+                             gpointer             user_data)
+{
+  GConfClient *client;
+  GtkTreeView *view = GTK_TREE_VIEW (user_data);
+  GtkTreeModel *model;
+  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
+  GtkTreeIter iter;
+  KeyEntry *key_entry;
+
+  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,
+		      KEYENTRY_COLUMN, &key_entry,
+		      -1);
+
+  /* sanity check */
+  if (key_entry == NULL || key_entry->desc_gconf_key == NULL)
+    return;
+
+  client = gconf_client_get_default ();
+  if (!gconf_client_set_string (client, key_entry->desc_gconf_key, new_text, NULL))
+    key_entry->desc_editable = FALSE;
+
+  g_object_unref (client);
+}
+
+static const guint forbidden_keyvals[] = {
+  /* Navigation keys */
+  GDK_KEY_Home,
+  GDK_KEY_Left,
+  GDK_KEY_Up,
+  GDK_KEY_Right,
+  GDK_KEY_Down,
+  GDK_KEY_Page_Up,
+  GDK_KEY_Page_Down,
+  GDK_KEY_End,
+  GDK_KEY_Tab,
+
+  /* Return */
+  GDK_KEY_KP_Enter,
+  GDK_KEY_Return,
+
+  GDK_KEY_space,
+  GDK_KEY_Mode_switch
+};
+
+static char*
+binding_name (guint                   keyval,
+	      guint		      keycode,
+              EggVirtualModifierType  mask,
+              gboolean                translate)
+{
+  if (keyval != 0 || keycode != 0)
+    return translate ?
+	egg_virtual_accelerator_label (keyval, keycode, mask) :
+	egg_virtual_accelerator_name (keyval, keycode, mask);
+  else
+    return g_strdup (translate ? _("Disabled") : "");
+}
+
+static gboolean
+keyval_is_forbidden (guint keyval)
+{
+  guint i;
+
+  for (i = 0; i < G_N_ELEMENTS(forbidden_keyvals); i++) {
+    if (keyval == forbidden_keyvals[i])
+      return TRUE;
+  }
+
+  return FALSE;
+}
+
+static void
+show_error (GtkWindow *parent,
+	    GError *err)
+{
+  GtkWidget *dialog;
+
+  dialog = gtk_message_dialog_new (parent,
+				   GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+				   GTK_MESSAGE_WARNING,
+				   GTK_BUTTONS_OK,
+				   _("Error saving the new shortcut"));
+
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                            "%s", err->message);
+  gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+}
+
+static gboolean
+cb_check_for_uniqueness (GtkTreeModel *model,
+			 GtkTreePath  *path,
+			 GtkTreeIter  *iter,
+			 KeyEntry *new_key)
+{
+  KeyEntry *element;
+
+  gtk_tree_model_get (new_key->model, iter,
+		      KEYENTRY_COLUMN, &element,
+		      -1);
+
+  /* no conflict for : blanks, different modifiers, or ourselves */
+  if (element == NULL || new_key->mask != element->mask ||
+      !strcmp (new_key->gconf_key, element->gconf_key))
+    return FALSE;
+
+  if (new_key->keyval != 0) {
+      if (new_key->keyval != element->keyval)
+	  return FALSE;
+  } else if (element->keyval != 0 || new_key->keycode != element->keycode)
+    return FALSE;
+
+  new_key->editable = FALSE;
+  new_key->gconf_key = element->gconf_key;
+  new_key->description = element->description;
+  new_key->desc_gconf_key = element->desc_gconf_key;
+  new_key->desc_editable = element->desc_editable;
+  return TRUE;
+}
+
+static void
+accel_edited_callback (GtkCellRendererText   *cell,
+                       const char            *path_string,
+                       guint                  keyval,
+                       EggVirtualModifierType mask,
+		       guint		      keycode,
+                       gpointer               data)
+{
+  GConfClient *client;
+  GtkTreeView *view = (GtkTreeView *)data;
+  GtkTreeModel *model;
+  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
+  GtkTreeIter iter;
+  KeyEntry *key_entry, tmp_key;
+  GError *err = NULL;
+  char *str;
+
+  block_accels = FALSE;
+
+  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,
+		      KEYENTRY_COLUMN, &key_entry,
+		      -1);
+
+  /* sanity check */
+  if (key_entry == NULL)
+    return;
+
+  /* CapsLock isn't supported as a keybinding modifier, so keep it from confusing us */
+  mask &= ~EGG_VIRTUAL_LOCK_MASK;
+
+  tmp_key.model  = model;
+  tmp_key.keyval = keyval;
+  tmp_key.keycode = keycode;
+  tmp_key.mask   = mask;
+  tmp_key.gconf_key = key_entry->gconf_key;
+  tmp_key.description = NULL;
+  tmp_key.editable = TRUE; /* kludge to stuff in a return flag */
+
+  if (keyval != 0 || keycode != 0) /* any number of keys can be disabled */
+    gtk_tree_model_foreach (model,
+      (GtkTreeModelForeachFunc) cb_check_for_uniqueness,
+      &tmp_key);
+
+  /* Check for unmodified keys */
+  if (tmp_key.mask == 0 && tmp_key.keycode != 0)
+    {
+      if ((tmp_key.keyval >= GDK_KEY_a && tmp_key.keyval <= GDK_KEY_z)
+	   || (tmp_key.keyval >= GDK_KEY_A && tmp_key.keyval <= GDK_KEY_Z)
+	   || (tmp_key.keyval >= GDK_KEY_0 && tmp_key.keyval <= GDK_KEY_9)
+	   || (tmp_key.keyval >= GDK_KEY_kana_fullstop && tmp_key.keyval <= GDK_KEY_semivoicedsound)
+	   || (tmp_key.keyval >= GDK_KEY_Arabic_comma && tmp_key.keyval <= GDK_KEY_Arabic_sukun)
+	   || (tmp_key.keyval >= GDK_KEY_Serbian_dje && tmp_key.keyval <= GDK_KEY_Cyrillic_HARDSIGN)
+	   || (tmp_key.keyval >= GDK_KEY_Greek_ALPHAaccent && tmp_key.keyval <= GDK_KEY_Greek_omega)
+	   || (tmp_key.keyval >= GDK_KEY_hebrew_doublelowline && tmp_key.keyval <= GDK_KEY_hebrew_taf)
+	   || (tmp_key.keyval >= GDK_KEY_Thai_kokai && tmp_key.keyval <= GDK_KEY_Thai_lekkao)
+	   || (tmp_key.keyval >= GDK_KEY_Hangul && tmp_key.keyval <= GDK_KEY_Hangul_Special)
+	   || (tmp_key.keyval >= GDK_KEY_Hangul_Kiyeog && tmp_key.keyval <= GDK_KEY_Hangul_J_YeorinHieuh)
+	   || keyval_is_forbidden (tmp_key.keyval)) {
+        GtkWidget *dialog;
+	char *name;
+
+	name = binding_name (keyval, keycode, mask, TRUE);
+
+	dialog =
+	  gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
+			  	  GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+				  GTK_MESSAGE_WARNING,
+				  GTK_BUTTONS_CANCEL,
+				  _("The shortcut \"%s\" cannot be used because it will become impossible to type using this key.\n"
+				  "Please try with a key such as Control, Alt or Shift at the same time."),
+				  name);
+
+	g_free (name);
+	gtk_dialog_run (GTK_DIALOG (dialog));
+	gtk_widget_destroy (dialog);
+
+	/* set it back to its previous value. */
+	egg_cell_renderer_keys_set_accelerator
+	  (EGG_CELL_RENDERER_KEYS (cell),
+	   key_entry->keyval, key_entry->keycode, key_entry->mask);
+	return;
+      }
+    }
+
+  /* flag to see if the new accelerator was in use by something */
+  if (!tmp_key.editable)
+    {
+      GtkWidget *dialog;
+      char *name;
+      int response;
+
+      name = binding_name (keyval, keycode, mask, TRUE);
+
+      dialog =
+	gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
+				GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+				GTK_MESSAGE_WARNING,
+				GTK_BUTTONS_CANCEL,
+				_("The shortcut \"%s\" is already used for\n\"%s\""),
+				name, tmp_key.description ?
+				tmp_key.description : tmp_key.gconf_key);
+      g_free (name);
+
+      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+	  _("If you reassign the shortcut to \"%s\", the \"%s\" shortcut "
+	    "will be disabled."),
+	  key_entry->description ?
+	  key_entry->description : key_entry->gconf_key,
+	  tmp_key.description ?
+	  tmp_key.description : tmp_key.gconf_key);
+
+      gtk_dialog_add_button (GTK_DIALOG (dialog),
+                             _("_Reassign"),
+			     GTK_RESPONSE_ACCEPT);
+
+      gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+				       GTK_RESPONSE_ACCEPT);
+
+      response = gtk_dialog_run (GTK_DIALOG (dialog));
+      gtk_widget_destroy (dialog);
+
+      if (response == GTK_RESPONSE_ACCEPT)
+	{
+	  GConfClient *client;
+
+	  client = gconf_client_get_default ();
+
+          gconf_client_set_string (client,
+				   tmp_key.gconf_key,
+				   "", &err);
+
+	  if (err != NULL)
+	    {
+	       show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
+			   err);
+	       g_error_free (err);
+	       g_object_unref (client);
+	       return;
+	    }
+
+	  str = binding_name (keyval, keycode, mask, FALSE);
+	  gconf_client_set_string (client,
+				   key_entry->gconf_key,
+				   str, &err);
+
+	  if (err != NULL)
+	    {
+	       show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
+			   err);
+	       g_error_free (err);
+
+	       /* reset the previous shortcut */
+	       gconf_client_set_string (client,
+					tmp_key.gconf_key,
+					str, NULL);
+	    }
+
+	  g_free (str);
+	  g_object_unref (client);
+	}
+      else
+	{
+	  /* set it back to its previous value. */
+	  egg_cell_renderer_keys_set_accelerator (EGG_CELL_RENDERER_KEYS (cell),
+						  key_entry->keyval,
+						  key_entry->keycode,
+						  key_entry->mask);
+	}
+
+      return;
+    }
+
+  str = binding_name (keyval, keycode, mask, FALSE);
+
+  client = gconf_client_get_default ();
+  gconf_client_set_string (client,
+                           key_entry->gconf_key,
+                           str,
+                           &err);
+  g_free (str);
+  g_object_unref (client);
+
+  if (err != NULL)
+    {
+      show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))), err);
+      g_error_free (err);
+      key_entry->editable = FALSE;
+    }
+}
+
+static void
+accel_cleared_callback (GtkCellRendererText *cell,
+			const char          *path_string,
+			gpointer             data)
+{
+  GConfClient *client;
+  GtkTreeView *view = (GtkTreeView *) data;
+  GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
+  KeyEntry *key_entry;
+  GtkTreeIter iter;
+  GError *err = NULL;
+  GtkTreeModel *model;
+
+  block_accels = FALSE;
+
+  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,
+		      KEYENTRY_COLUMN, &key_entry,
+		      -1);
+
+  /* sanity check */
+  if (key_entry == NULL)
+    return;
+
+  /* Unset the key */
+  client = gconf_client_get_default();
+  gconf_client_set_string (client,
+			   key_entry->gconf_key,
+			   "",
+			   &err);
+  g_object_unref (client);
+
+  if (err != NULL)
+    {
+      GtkWidget *dialog;
+
+      dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
+				       GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+				       GTK_MESSAGE_WARNING,
+				       GTK_BUTTONS_OK,
+				       _("Error unsetting accelerator in configuration database: %s"),
+				       err->message);
+      gtk_dialog_run (GTK_DIALOG (dialog));
+
+      gtk_widget_destroy (dialog);
+      g_error_free (err);
+      key_entry->editable = FALSE;
+    }
+}
+
+static void
+key_entry_controlling_key_changed (GConfClient *client,
+				   guint        cnxn_id,
+				   GConfEntry  *entry,
+				   gpointer     user_data)
+{
+  reload_sections (user_data);
+}
+
+/* this handler is used to keep accels from activating while the user
+ * is assigning a new shortcut so that he won't accidentally trigger one
+ * of the widgets */
+static gboolean
+maybe_block_accels (GtkWidget *widget,
+                    GdkEventKey *event,
+                    gpointer user_data)
+{
+  if (block_accels)
+  {
+    return gtk_window_propagate_key_event (GTK_WINDOW (widget), event);
+  }
+  return FALSE;
+}
+
+static gchar *
+find_free_gconf_key (GError **error)
+{
+  GConfClient *client;
+
+  gchar *dir;
+  int i;
+
+  client = gconf_client_get_default ();
+
+  for (i = 0; i < MAX_CUSTOM_SHORTCUTS; i++)
+    {
+      dir = g_strdup_printf ("%s/custom%d", GCONF_BINDING_DIR, i);
+      if (!gconf_client_dir_exists (client, dir, NULL))
+        break;
+      g_free (dir);
+    }
+
+  if (i == MAX_CUSTOM_SHORTCUTS)
+    {
+      dir = NULL;
+      g_set_error_literal (error,
+                           g_quark_from_string ("Keyboard Shortcuts"),
+                           0,
+                           _("Too many custom shortcuts"));
+    }
+
+  g_object_unref (client);
+
+  return dir;
+}
+
+static void
+add_custom_shortcut (GtkTreeView  *tree_view,
+                     GtkTreeModel *model)
+{
+  KeyEntry *key_entry;
+  GtkTreePath *path;
+  gchar *dir;
+  GConfClient *client;
+  GError *error;
+
+  error = NULL;
+  dir = find_free_gconf_key (&error);
+  if (dir == NULL)
+    {
+      show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tree_view))), error);
+
+      g_error_free (error);
+      return;
+    }
+
+  key_entry = g_new0 (KeyEntry, 1);
+  key_entry->gconf_key = g_strconcat (dir, "/binding", NULL);
+  key_entry->editable = TRUE;
+  key_entry->model = model;
+  key_entry->desc_gconf_key = g_strconcat (dir, "/name", NULL);
+  key_entry->description = g_strdup ("");
+  key_entry->desc_editable = TRUE;
+  key_entry->cmd_gconf_key = g_strconcat (dir, "/action", NULL);
+  key_entry->command = g_strdup ("");
+  key_entry->cmd_editable = TRUE;
+  g_free (dir);
+
+  if (edit_custom_shortcut (key_entry) &&
+      key_entry->command && key_entry->command[0])
+    {
+      GPtrArray *keys_array;
+      GtkTreeIter iter;
+
+      keys_array = g_hash_table_lookup (kb_sections, _("Custom Shortcuts"));
+      if (keys_array == NULL)
+        {
+	  keys_array = g_ptr_array_new ();
+	  g_hash_table_insert (kb_sections, g_strdup (_("Custom Shortcuts")), keys_array);
+	}
+
+      g_ptr_array_add (keys_array, key_entry);
+
+      gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+      gtk_list_store_set (GTK_LIST_STORE (model), &iter, KEYENTRY_COLUMN, key_entry, -1);
+
+      /* store in gconf */
+      client = gconf_client_get_default ();
+      gconf_client_set_string (client, key_entry->gconf_key, "", NULL);
+      gconf_client_set_string (client, key_entry->desc_gconf_key, key_entry->description, NULL);
+      gconf_client_set_string (client, key_entry->cmd_gconf_key, key_entry->command, NULL);
+
+      /* add gconf watches */
+      key_entry->gconf_cnxn_desc = gconf_client_notify_add (client,
+                                                            key_entry->desc_gconf_key,
+					    		    (GConfClientNotifyFunc) &keybinding_description_changed,
+							    key_entry, NULL, NULL);
+      key_entry->gconf_cnxn_cmd = gconf_client_notify_add (client,
+	                                                   key_entry->cmd_gconf_key,
+						           (GConfClientNotifyFunc) &keybinding_command_changed,
+						           key_entry, NULL, NULL);
+      key_entry->gconf_cnxn = gconf_client_notify_add (client,
+	                                               key_entry->gconf_key,
+						       (GConfClientNotifyFunc) &keybinding_key_changed,
+					               key_entry, NULL, NULL);
+
+
+      g_object_unref (client);
+
+      /* make the new shortcut visible */
+      path = gtk_tree_model_get_path (model, &iter);
+      gtk_tree_view_expand_to_path (tree_view, path);
+      gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0, 0);
+      gtk_tree_path_free (path);
+    }
+  else
+    {
+      g_free (key_entry->gconf_key);
+      g_free (key_entry->description);
+      g_free (key_entry->desc_gconf_key);
+      g_free (key_entry->command);
+      g_free (key_entry->cmd_gconf_key);
+      g_free (key_entry);
+    }
+}
+
+static void
+add_button_clicked (GtkWidget  *button,
+                    GtkBuilder *builder)
+{
+  GtkTreeView *treeview;
+  GtkTreeModel *model;
+
+  treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                                    "shortcut_treeview"));
+  model = gtk_tree_view_get_model (treeview);
+
+  add_custom_shortcut (treeview, model);
+}
+
+static void
+remove_button_clicked (GtkWidget  *button,
+                       GtkBuilder *builder)
+{
+  GtkTreeView *treeview;
+  GtkTreeModel *model;
+  GtkTreeSelection *selection;
+  GtkTreeIter iter;
+
+  treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                                    "shortcut_treeview"));
+  model = gtk_tree_view_get_model (treeview);
+
+  selection = gtk_tree_view_get_selection (treeview);
+  if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+    {
+      remove_custom_shortcut (model, &iter);
+    }
+}
+
+static int
+keyentry_sort_func (GtkTreeModel *model,
+                    GtkTreeIter  *a,
+                    GtkTreeIter  *b,
+                    gpointer      user_data)
+{
+  KeyEntry *key_entry_a;
+  KeyEntry *key_entry_b;
+  int retval;
+
+  key_entry_a = NULL;
+  gtk_tree_model_get (model, a,
+                      KEYENTRY_COLUMN, &key_entry_a,
+                      -1);
+
+  key_entry_b = NULL;
+  gtk_tree_model_get (model, b,
+                      KEYENTRY_COLUMN, &key_entry_b,
+                      -1);
+
+  if (key_entry_a && key_entry_b)
+    {
+      if ((key_entry_a->keyval || key_entry_a->keycode) &&
+          (key_entry_b->keyval || key_entry_b->keycode))
+        {
+          gchar *name_a, *name_b;
+
+          name_a = binding_name (key_entry_a->keyval,
+                                 key_entry_a->keycode,
+                                 key_entry_a->mask,
+                                 TRUE);
+
+          name_b = binding_name (key_entry_b->keyval,
+                                 key_entry_b->keycode,
+                                 key_entry_b->mask,
+                                 TRUE);
+
+          retval = g_utf8_collate (name_a, name_b);
+
+          g_free (name_a);
+          g_free (name_b);
+        }
+      else if (key_entry_a->keyval || key_entry_a->keycode)
+        retval = -1;
+      else if (key_entry_b->keyval || key_entry_b->keycode)
+        retval = 1;
+      else
+        retval = 0;
+    }
+  else if (key_entry_a)
+    retval = -1;
+  else if (key_entry_b)
+    retval = 1;
+  else
+    retval = 0;
+
+  return retval;
+}
+
 static void
 setup_dialog (CcPanel *panel, GtkBuilder *builder)
 {
@@ -764,6 +1711,7 @@ setup_dialog (CcPanel *panel, GtkBuilder *builder)
 
   model = gtk_list_store_new (1, G_TYPE_STRING);
   gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (model));
+  g_object_unref (model);
 
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
   g_signal_connect (selection, "changed",
@@ -775,16 +1723,16 @@ setup_dialog (CcPanel *panel, GtkBuilder *builder)
 
   client = gconf_client_get_default ();
 
-  /* g_signal_connect (treeview, "button_press_event", */
-  /* 		    G_CALLBACK (start_editing_cb), builder); */
-  /* g_signal_connect (treeview, "row-activated", */
-  /* 		    G_CALLBACK (start_editing_kb_cb), NULL); */
+  g_signal_connect (treeview, "button_press_event",
+   		    G_CALLBACK (start_editing_cb), builder);
+  g_signal_connect (treeview, "row-activated", 
+   		    G_CALLBACK (start_editing_kb_cb), NULL);
 
   renderer = gtk_cell_renderer_text_new ();
 
-  /* g_signal_connect (renderer, "edited", */
-  /*                   G_CALLBACK (description_edited_callback), */
-  /*                   treeview); */
+  g_signal_connect (renderer, "edited",
+		    G_CALLBACK (description_edited_callback),
+		    treeview);
 
   column = gtk_tree_view_column_new_with_attributes (_("Action"),
 						     renderer,
@@ -800,13 +1748,13 @@ setup_dialog (CcPanel *panel, GtkBuilder *builder)
 					       "accel_mode", EGG_CELL_RENDERER_KEYS_MODE_X,
 					       NULL);
 
-  /* g_signal_connect (renderer, "accel_edited", */
-  /*                   G_CALLBACK (accel_edited_callback), */
-  /*                   treeview); */
+  g_signal_connect (renderer, "accel_edited",
+		    G_CALLBACK (accel_edited_callback),
+		    treeview);
 
-  /* g_signal_connect (renderer, "accel_cleared", */
-  /*                   G_CALLBACK (accel_cleared_callback), */
-  /*                   treeview); */
+  g_signal_connect (renderer, "accel_cleared",
+		    G_CALLBACK (accel_cleared_callback),
+		    treeview);
 
   column = gtk_tree_view_column_new_with_attributes (_("Shortcut"), renderer, NULL);
   gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
@@ -817,20 +1765,25 @@ setup_dialog (CcPanel *panel, GtkBuilder *builder)
 
   gconf_client_add_dir (client, GCONF_BINDING_DIR, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
   gconf_client_add_dir (client, "/apps/metacity/general", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
-  /* gconf_client_notify_add (client, */
-  /* 			   "/apps/metacity/general/num_workspaces", */
-  /* 			   (GConfClientNotifyFunc) key_entry_controlling_key_changed, */
-  /* 			   builder, NULL, NULL); */
+  gconf_client_notify_add (client,
+   			   "/apps/metacity/general/num_workspaces",
+   			   (GConfClientNotifyFunc) key_entry_controlling_key_changed,
+   			   builder, NULL, NULL);
 
   model = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);
+  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model),
+                                       KEYENTRY_COLUMN,
+                                       keyentry_sort_func,
+                                       NULL, NULL);
   gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (model));
+  g_object_unref (model);
 
   /* set up the dialog */
   shell = cc_panel_get_shell (CC_PANEL (panel));
   widget = cc_shell_get_toplevel (shell);
 
-  /* maybe_block_accels_id = g_signal_connect (widget, "key_press_event", */
-  /* 					    G_CALLBACK (maybe_block_accels), NULL); */
+  maybe_block_accels_id = g_signal_connect (widget, "key_press_event",
+   					    G_CALLBACK (maybe_block_accels), NULL);
 
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
   g_signal_connect (selection, "changed",
@@ -858,10 +1811,10 @@ setup_dialog (CcPanel *panel, GtkBuilder *builder)
 				    "custom-shortcut-name-entry");
   custom_shortcut_command_entry = WID (builder,
 				       "custom-shortcut-command-entry");
-  /* g_signal_connect (WID (builder, "add-button"), */
-  /*                   "clicked", G_CALLBACK (add_button_clicked), builder); */
-  /* g_signal_connect (WID (builder, "remove-button"), */
-  /*                   "clicked", G_CALLBACK (remove_button_clicked), builder); */
+  g_signal_connect (WID (builder, "add-button"),
+		    "clicked", G_CALLBACK (add_button_clicked), builder);
+  g_signal_connect (WID (builder, "remove-button"),
+		    "clicked", G_CALLBACK (remove_button_clicked), builder);
 
   gtk_dialog_set_default_response (GTK_DIALOG (custom_shortcut_dialog),
 				   GTK_RESPONSE_OK);
@@ -870,11 +1823,20 @@ setup_dialog (CcPanel *panel, GtkBuilder *builder)
                                 GTK_WINDOW (widget));
 }
 
+static void
+on_window_manager_change (const char *wm_name, GtkBuilder *builder)
+{
+  reload_sections (builder);
+}
+
 void
 keyboard_shortcuts_init (CcPanel *panel, GtkBuilder *builder)
 {
   kb_sections = g_hash_table_new_full (g_str_hash, g_str_equal,
 				       g_free, (GDestroyNotify) free_key_array);
+
+  wm_common_register_window_manager_change ((GFunc) on_window_manager_change,
+                                            builder);
   setup_dialog (panel, builder);
   reload_sections (builder);
 }
@@ -882,4 +1844,18 @@ keyboard_shortcuts_init (CcPanel *panel, GtkBuilder *builder)
 void
 keyboard_shortcuts_dispose (CcPanel *panel)
 {
+  if (maybe_block_accels_id != 0)
+    {
+      CcShell *shell;
+      GtkWidget *toplevel;
+
+      shell = cc_panel_get_shell (CC_PANEL (panel));
+      toplevel = cc_shell_get_toplevel (shell);
+
+      g_signal_handler_disconnect (toplevel, maybe_block_accels_id);
+      maybe_block_accels_id = 0;
+
+      if (kb_sections != NULL)
+        g_hash_table_destroy (kb_sections);
+    }
 }



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