[gnome-control-center] keyboard: Clean up GConf handling



commit 5c755904e1d6b4e0bf6f5d60a7e617f0cfa90fa5
Author: Bastien Nocera <hadess hadess net>
Date:   Wed Feb 16 01:22:38 2011 +0000

    keyboard: Clean up GConf handling
    
    Move most of the horrible GConf monitoring code to a separate
    GObject(-ish). While quite ugly, it's not as bad as the code that
    used to be there before.
    
    Also fix the setting of KeyEntry->model (or CcKeyboardItem->model now)
    to be the correct model (eg. the shortcut model rather than the section
    model)

 panels/keyboard/Makefile.am          |    2 +
 panels/keyboard/cc-keyboard-item.c   |  450 ++++++++++++++++++++++++
 panels/keyboard/cc-keyboard-item.h   |  109 ++++++
 panels/keyboard/keyboard-shortcuts.c |  640 ++++++++++++----------------------
 4 files changed, 780 insertions(+), 421 deletions(-)
---
diff --git a/panels/keyboard/Makefile.am b/panels/keyboard/Makefile.am
index ec76a1a..a19565a 100644
--- a/panels/keyboard/Makefile.am
+++ b/panels/keyboard/Makefile.am
@@ -8,6 +8,8 @@ libkeyboard_la_SOURCES =   \
 	keyboard-module.c		\
 	cc-keyboard-panel.c		\
 	cc-keyboard-panel.h		\
+	cc-keyboard-item.c		\
+	cc-keyboard-item.h		\
 	wm-common.c			\
 	wm-common.h			\
 	keyboard-general.c		\
diff --git a/panels/keyboard/cc-keyboard-item.c b/panels/keyboard/cc-keyboard-item.c
new file mode 100644
index 0000000..dfe222d
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-item.c
@@ -0,0 +1,450 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Red Hat, 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+
+#include <gconf/gconf-client.h>
+
+#include "cc-keyboard-item.h"
+
+#define CC_KEYBOARD_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_KEYBOARD_ITEM, CcKeyboardItemPrivate))
+
+struct CcKeyboardItemPrivate
+{
+  /* properties */
+  int foo;
+
+  /* internal */
+};
+
+enum {
+  PROP_0,
+  PROP_DESCRIPTION,
+  PROP_BINDING,
+  PROP_EDITABLE,
+  PROP_TYPE,
+  PROP_COMMAND
+};
+
+static void     cc_keyboard_item_class_init     (CcKeyboardItemClass *klass);
+static void     cc_keyboard_item_init           (CcKeyboardItem      *keyboard_item);
+static void     cc_keyboard_item_finalize       (GObject               *object);
+
+G_DEFINE_TYPE (CcKeyboardItem, cc_keyboard_item, G_TYPE_OBJECT)
+
+static void
+_set_description (CcKeyboardItem *item,
+                  const char       *value)
+{
+  g_free (item->description);
+  item->description = g_strdup (value);
+}
+
+const char *
+cc_keyboard_item_get_description (CcKeyboardItem *item)
+{
+  g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
+
+  return item->description;
+}
+
+static void
+_set_binding (CcKeyboardItem *item,
+              const char       *value)
+{
+  g_free (item->binding);
+  item->binding = g_strdup (value);
+}
+
+const char *
+cc_keyboard_item_get_binding (CcKeyboardItem *item)
+{
+  g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
+
+  return item->binding;
+}
+
+static void
+_set_type (CcKeyboardItem *item,
+           gint            value)
+{
+  item->type = value;
+}
+
+static void
+_set_command (CcKeyboardItem *item,
+              const char       *value)
+{
+  g_free (item->command);
+  item->command = g_strdup (value);
+}
+
+const char *
+cc_keyboard_item_get_command (CcKeyboardItem *item)
+{
+  g_return_val_if_fail (CC_IS_KEYBOARD_ITEM (item), NULL);
+
+  return item->command;
+}
+
+static void
+cc_keyboard_item_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  CcKeyboardItem *self;
+
+  self = CC_KEYBOARD_ITEM (object);
+
+  switch (prop_id) {
+  case PROP_DESCRIPTION:
+    _set_description (self, g_value_get_string (value));
+    break;
+  case PROP_BINDING:
+    _set_binding (self, g_value_get_string (value));
+    break;
+  case PROP_COMMAND:
+    _set_command (self, g_value_get_string (value));
+    break;
+  case PROP_TYPE:
+    _set_type (self, g_value_get_int (value));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+cc_keyboard_item_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  CcKeyboardItem *self;
+
+  self = CC_KEYBOARD_ITEM (object);
+
+  switch (prop_id) {
+  case PROP_DESCRIPTION:
+    g_value_set_string (value, self->description);
+    break;
+  case PROP_BINDING:
+    g_value_set_string (value, self->binding);
+    break;
+  case PROP_EDITABLE:
+    g_value_set_boolean (value, self->editable);
+    break;
+  case PROP_COMMAND:
+    g_value_set_string (value, self->command);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    break;
+  }
+}
+
+static GObject *
+cc_keyboard_item_constructor (GType                  type,
+                              guint                  n_construct_properties,
+                              GObjectConstructParam *construct_properties)
+{
+  CcKeyboardItem      *keyboard_item;
+
+  keyboard_item = CC_KEYBOARD_ITEM (G_OBJECT_CLASS (cc_keyboard_item_parent_class)->constructor (type,
+                                                                                                 n_construct_properties,
+                                                                                                 construct_properties));
+
+  return G_OBJECT (keyboard_item);
+}
+
+static void
+cc_keyboard_item_class_init (CcKeyboardItemClass *klass)
+{
+  GObjectClass  *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = cc_keyboard_item_get_property;
+  object_class->set_property = cc_keyboard_item_set_property;
+  object_class->constructor = cc_keyboard_item_constructor;
+  object_class->finalize = cc_keyboard_item_finalize;
+
+  g_object_class_install_property (object_class,
+                                   PROP_DESCRIPTION,
+                                   g_param_spec_string ("description",
+                                                        "description",
+                                                        "description",
+                                                        NULL,
+                                                        G_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class,
+                                   PROP_BINDING,
+                                   g_param_spec_string ("binding",
+                                                        "binding",
+                                                        "binding",
+                                                        NULL,
+                                                        G_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class,
+                                   PROP_EDITABLE,
+                                   g_param_spec_boolean ("editable",
+                                                         NULL,
+                                                         NULL,
+                                                         FALSE,
+                                                         G_PARAM_READABLE));
+
+  g_object_class_install_property (object_class,
+                                   PROP_TYPE,
+                                   g_param_spec_int ("type",
+                                                     NULL,
+                                                     NULL,
+                                                     CC_KEYBOARD_ITEM_TYPE_NONE,
+                                                     CC_KEYBOARD_ITEM_TYPE_GSETTINGS,
+                                                     CC_KEYBOARD_ITEM_TYPE_NONE,
+                                                     G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
+
+  g_object_class_install_property (object_class,
+                                   PROP_COMMAND,
+                                   g_param_spec_string ("command",
+                                                        "command",
+                                                        "command",
+                                                        NULL,
+                                                        G_PARAM_READWRITE));
+
+  g_type_class_add_private (klass, sizeof (CcKeyboardItemPrivate));
+}
+
+static void
+cc_keyboard_item_init (CcKeyboardItem *item)
+{
+  item->priv = CC_KEYBOARD_ITEM_GET_PRIVATE (item);
+}
+
+static void
+cc_keyboard_item_finalize (GObject *object)
+{
+  CcKeyboardItem *item;
+  GConfClient *client;
+
+  g_return_if_fail (object != NULL);
+  g_return_if_fail (CC_IS_KEYBOARD_ITEM (object));
+
+  item = CC_KEYBOARD_ITEM (object);
+
+  g_return_if_fail (item->priv != NULL);
+
+  /* Remove GConf watches */
+  client = gconf_client_get_default ();
+
+  /* FIXME what if we didn't add a watch? */
+  if (item->gconf_key_dir != NULL)
+    gconf_client_remove_dir (client, item->gconf_key_dir, NULL);
+  else if (item->gconf_key != NULL)
+    gconf_client_remove_dir (client, item->gconf_key, NULL);
+
+  if (item->gconf_cnxn != 0)
+    gconf_client_notify_remove (client, item->gconf_cnxn);
+  if (item->gconf_cnxn_desc != 0)
+    gconf_client_notify_remove (client, item->gconf_cnxn_desc);
+  if (item->gconf_cnxn_cmd != 0)
+    gconf_client_notify_remove (client, item->gconf_cnxn_cmd);
+
+  g_object_unref (client);
+
+  /* Free memory */
+  g_free (item->gconf_key);
+  g_free (item->description);
+  g_free (item->desc_gconf_key);
+  g_free (item->command);
+  g_free (item->cmd_gconf_key);
+
+
+  G_OBJECT_CLASS (cc_keyboard_item_parent_class)->finalize (object);
+}
+
+CcKeyboardItem *
+cc_keyboard_item_new (CcKeyboardItemType type)
+{
+  GObject *object;
+
+  object = g_object_new (CC_TYPE_KEYBOARD_ITEM,
+                         "type", type,
+                         NULL);
+
+  return CC_KEYBOARD_ITEM (object);
+}
+
+static void
+keybinding_description_changed (GConfClient    *client,
+                                guint           cnxn_id,
+                                GConfEntry     *entry,
+                                CcKeyboardItem *item)
+{
+  const gchar *key_value;
+
+  key_value = entry->value ? gconf_value_get_string (entry->value) : NULL;
+  _set_description (item, key_value);
+  item->desc_editable = gconf_entry_get_is_writable (entry);
+  g_object_notify (G_OBJECT (item), "description");
+}
+
+static void
+keybinding_key_changed (GConfClient    *client,
+                        guint           cnxn_id,
+                        GConfEntry     *entry,
+                        CcKeyboardItem *item)
+{
+  const gchar *key_value;
+
+  key_value = entry->value ? gconf_value_get_string (entry->value) : NULL;
+  _set_binding (item, key_value);
+  item->editable = gconf_entry_get_is_writable (entry);
+  g_object_notify (G_OBJECT (item), "binding");
+}
+
+static void
+keybinding_command_changed (GConfClient    *client,
+                            guint           cnxn_id,
+                            GConfEntry     *entry,
+                            CcKeyboardItem *item)
+{
+  const gchar *key_value;
+
+  key_value = entry->value ? gconf_value_get_string (entry->value) : NULL;
+  _set_command (item, key_value);
+  item->cmd_editable = gconf_entry_get_is_writable (entry);
+  g_object_notify (G_OBJECT (item), "command");
+}
+
+gboolean
+cc_keyboard_item_load_from_gconf (CcKeyboardItem *item,
+                                  const char *key)
+{
+  GConfClient *client;
+  GConfEntry *entry;
+
+  client = gconf_client_get_default ();
+
+  item->gconf_key = g_strdup (key);
+  entry = gconf_client_get_entry (client,
+                                  item->gconf_key,
+                                  NULL,
+                                  TRUE,
+                                  NULL);
+  if (entry == NULL) {
+    g_object_unref (client);
+    return FALSE;
+  }
+
+  if (gconf_entry_get_schema_name (entry)) {
+    GConfSchema *schema;
+
+    schema = gconf_client_get_schema (client,
+                                      gconf_entry_get_schema_name (entry),
+                                      NULL);
+    if (schema != NULL) {
+      item->description = g_strdup (gconf_schema_get_short_desc (schema));
+      gconf_schema_free (schema);
+    }
+  }
+  if (item->description == NULL) {
+    /* Only print a warning for keys that should have a schema */
+    g_warning ("No description for key '%s'", item->gconf_key);
+  }
+  item->editable = gconf_entry_get_is_writable (entry);
+  gconf_client_add_dir (client, item->gconf_key, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+  item->gconf_cnxn = gconf_client_notify_add (client,
+                                              item->gconf_key,
+                                              (GConfClientNotifyFunc) &keybinding_key_changed,
+                                              item, NULL, NULL);
+  item->binding = gconf_client_get_string (client, item->gconf_key, NULL);
+
+  gconf_entry_free (entry);
+  g_object_unref (client);
+
+  return TRUE;
+}
+
+gboolean
+cc_keyboard_item_load_from_gconf_dir (CcKeyboardItem *item,
+                                      const char *key_dir)
+{
+  GConfClient *client;
+  GConfEntry *entry;
+
+  /* FIXME add guards:
+   * key_dir finishing with '/' */
+
+  client = gconf_client_get_default ();
+
+  item->gconf_key_dir = g_strdup (key_dir);
+  item->binding_gconf_key = g_strdup_printf ("%s/binding", item->gconf_key_dir);
+  item->desc_gconf_key = g_strdup_printf ("%s/name", item->gconf_key_dir);
+  item->cmd_gconf_key = g_strdup_printf ("%s/action", item->gconf_key_dir);
+  item->description = gconf_client_get_string (client, item->desc_gconf_key, NULL);
+
+  entry = gconf_client_get_entry (client,
+                                  item->binding_gconf_key,
+                                  NULL,
+                                  TRUE,
+                                  NULL);
+  if (entry == NULL)
+    return FALSE;
+
+  item->command = gconf_client_get_string (client, item->cmd_gconf_key, NULL);
+  item->editable = gconf_entry_get_is_writable (entry);
+  gconf_client_add_dir (client, item->gconf_key_dir, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+  item->desc_editable = gconf_client_key_is_writable (client, item->desc_gconf_key, NULL);
+  item->gconf_cnxn_desc = gconf_client_notify_add (client,
+                                                   item->desc_gconf_key,
+                                                   (GConfClientNotifyFunc) &keybinding_description_changed,
+                                                   item, NULL, NULL);
+
+  item->cmd_editable = gconf_client_key_is_writable (client, item->cmd_gconf_key, NULL);
+  item->gconf_cnxn_cmd = gconf_client_notify_add (client,
+                                                  item->cmd_gconf_key,
+                                                  (GConfClientNotifyFunc) &keybinding_command_changed,
+                                                  item, NULL, NULL);
+
+  item->cmd_editable = gconf_client_key_is_writable (client, item->binding_gconf_key, NULL);
+  item->gconf_cnxn = gconf_client_notify_add (client,
+                                              item->binding_gconf_key,
+                                              (GConfClientNotifyFunc) &keybinding_key_changed,
+                                              item, NULL, NULL);
+
+  item->binding = gconf_client_get_string (client, item->binding_gconf_key, NULL);
+
+  gconf_entry_free (entry);
+  g_object_unref (client);
+
+  return TRUE;
+}
+
+/*
+ * vim: sw=2 ts=8 cindent noai bs=2
+ */
diff --git a/panels/keyboard/cc-keyboard-item.h b/panels/keyboard/cc-keyboard-item.h
new file mode 100644
index 0000000..16484a5
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-item.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Red Hat, 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __CC_KEYBOARD_ITEM_H
+#define __CC_KEYBOARD_ITEM_H
+
+#include <glib-object.h>
+#include "eggaccelerators.h"
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_KEYBOARD_ITEM         (cc_keyboard_item_get_type ())
+#define CC_KEYBOARD_ITEM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), CC_TYPE_KEYBOARD_ITEM, CcKeyboardItem))
+#define CC_KEYBOARD_ITEM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), CC_TYPE_KEYBOARD_ITEM, CcKeyboardItemClass))
+#define CC_IS_KEYBOARD_ITEM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), CC_TYPE_KEYBOARD_ITEM))
+#define CC_IS_KEYBOARD_ITEM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), CC_TYPE_KEYBOARD_ITEM))
+#define CC_KEYBOARD_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CC_TYPE_KEYBOARD_ITEM, CcKeyboardItemClass))
+
+typedef enum
+{
+  BINDING_GROUP_SYSTEM,
+  BINDING_GROUP_APPS,
+  BINDING_GROUP_USER,
+} BindingGroupType;
+
+typedef enum {
+	CC_KEYBOARD_ITEM_TYPE_NONE = 0,
+	CC_KEYBOARD_ITEM_TYPE_GCONF,
+	CC_KEYBOARD_ITEM_TYPE_GCONF_DIR,
+	CC_KEYBOARD_ITEM_TYPE_GSETTINGS
+} CcKeyboardItemType;
+
+typedef struct CcKeyboardItemPrivate CcKeyboardItemPrivate;
+
+typedef struct
+{
+  GObject                parent;
+  CcKeyboardItemPrivate *priv;
+
+  /* Move to priv */
+  CcKeyboardItemType type;
+
+  /* common */
+  /* FIXME move to priv? */
+  guint keyval;
+  guint keycode;
+  EggVirtualModifierType mask;
+  BindingGroupType group;
+  GtkTreeModel *model;
+  char *description;
+  char *binding;
+
+  /* GCONF */
+  char *gconf_key;
+  guint gconf_cnxn;
+  gboolean editable;
+
+  /* GCONF DIR */
+  char *gconf_key_dir;
+
+  char *binding_gconf_key;
+
+  char *desc_gconf_key;
+  gboolean desc_editable;
+  guint gconf_cnxn_desc;
+
+  char *command;
+  char *cmd_gconf_key;
+  gboolean cmd_editable;
+  guint gconf_cnxn_cmd;
+} CcKeyboardItem;
+
+typedef struct
+{
+  GObjectClass   parent_class;
+} CcKeyboardItemClass;
+
+GType              cc_keyboard_item_get_type (void);
+
+CcKeyboardItem * cc_keyboard_item_new         (CcKeyboardItemType type);
+gboolean cc_keyboard_item_load_from_gconf     (CcKeyboardItem *item,
+					       const char *key);
+gboolean cc_keyboard_item_load_from_gconf_dir (CcKeyboardItem *item,
+					       const char *key_dir);
+
+const char * cc_keyboard_item_get_description (CcKeyboardItem *item);
+const char * cc_keyboard_item_get_binding     (CcKeyboardItem *item);
+const char * cc_keyboard_item_get_command     (CcKeyboardItem *item);
+
+G_END_DECLS
+
+#endif /* __CC_KEYBOARD_ITEM_H */
diff --git a/panels/keyboard/keyboard-shortcuts.c b/panels/keyboard/keyboard-shortcuts.c
index 7a0100c..f8b7a9c 100644
--- a/panels/keyboard/keyboard-shortcuts.c
+++ b/panels/keyboard/keyboard-shortcuts.c
@@ -23,6 +23,7 @@
 #include <gconf/gconf-client.h>
 #include "eggcellrendererkeys.h"
 #include "keyboard-shortcuts.h"
+#include "cc-keyboard-item.h"
 #include "wm-common.h"
 
 #define MAX_CUSTOM_SHORTCUTS 1000
@@ -50,41 +51,13 @@ typedef enum {
 
 typedef struct
 {
-  char *name;
-  int value;
-  char *key;
-  char *description_name;
-  char *cmd_name;
+  CcKeyboardItemType type;
+  char *name; /* GConf key, or directory, depending on type */
+  int value; /* Value for comparison */
+  char *key; /* GConf key name for the comparison */
   Comparison comparison;
 } KeyListEntry;
 
-typedef enum
-{
-  BINDING_GROUP_SYSTEM,
-  BINDING_GROUP_APPS,
-  BINDING_GROUP_USER,
-} BindingGroupType;
-
-typedef struct
-{
-  char *gconf_key;
-  guint keyval;
-  guint keycode;
-  EggVirtualModifierType mask;
-  BindingGroupType group;
-  gboolean editable;
-  GtkTreeModel *model;
-  char *description;
-  char *desc_gconf_key;
-  gboolean desc_editable;
-  char *command;
-  char *cmd_gconf_key;
-  gboolean cmd_editable;
-  guint gconf_cnxn;
-  guint gconf_cnxn_desc;
-  guint gconf_cnxn_cmd;
-} KeyEntry;
-
 enum
 {
   DETAIL_DESCRIPTION_COLUMN,
@@ -117,30 +90,11 @@ free_key_array (GPtrArray *keys)
 
       for (i = 0; i < keys->len; i++)
         {
-          KeyEntry *key_entry;
-          GConfClient *client;
-
-          key_entry = g_ptr_array_index (keys, i);
+          CcKeyboardItem *item;
 
-          /* 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);
+          item = g_ptr_array_index (keys, i);
 
-          /* 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_object_unref (item);
         }
 
       g_ptr_array_free (keys, TRUE);
@@ -181,9 +135,9 @@ have_key_for_group (int group, const gchar *name)
     {
       for (i = 0; i < keys->len; i++)
         {
-          KeyEntry *entry = g_ptr_array_index (keys, i);
+          CcKeyboardItem *item = g_ptr_array_index (keys, i);
 
-          if (g_strcmp0 (name, entry->gconf_key) == 0)
+          if (g_strcmp0 (name, item->gconf_key) == 0)
             return TRUE;
         }
     }
@@ -253,84 +207,33 @@ binding_from_string (const char             *str,
 }
 
 static gboolean
-keybinding_key_changed_foreach (GtkTreeModel *model,
-                                GtkTreePath  *path,
-                                GtkTreeIter  *iter,
-                                gpointer      user_data)
+keybinding_key_changed_foreach (GtkTreeModel   *model,
+                                GtkTreePath    *path,
+                                GtkTreeIter    *iter,
+                                CcKeyboardItem *item)
 {
-  KeyEntry *key_entry;
-  KeyEntry *tmp_key_entry;
+  CcKeyboardItem *tmp_item;
 
-  key_entry = (KeyEntry *)user_data;
-  gtk_tree_model_get (key_entry->model, iter,
-                      DETAIL_KEYENTRY_COLUMN, &tmp_key_entry,
+  gtk_tree_model_get (item->model, iter,
+                      DETAIL_KEYENTRY_COLUMN, &tmp_item,
                       -1);
 
-  if (key_entry == tmp_key_entry)
+  if (item == tmp_item)
     {
-      gtk_tree_model_row_changed (key_entry->model, path, iter);
+      gtk_tree_model_row_changed (item->model, path, iter);
       return TRUE;
     }
   return FALSE;
 }
 
 static void
-keybinding_description_changed (GConfClient *client,
-                                guint        cnxn_id,
-                                GConfEntry  *entry,
-                                gpointer     user_data)
+item_changed (CcKeyboardItem *item,
+	      GParamSpec     *pspec,
+	      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);
+  binding_from_string (item->binding, &item->keyval, &item->keycode, &item->mask);
+  gtk_tree_model_foreach (item->model, (GtkTreeModelForeachFunc) keybinding_key_changed_foreach, item);
 }
 
 static void
@@ -341,8 +244,7 @@ append_section (GtkBuilder         *builder,
 {
   GPtrArray *keys_array;
   GtkTreeModel *sort_model;
-  GtkTreeModel *model;
-  GConfClient *client;
+  GtkTreeModel *model, *shortcut_model;
   GtkTreeIter iter;
   gint i;
   GHashTable *hash;
@@ -350,11 +252,12 @@ append_section (GtkBuilder         *builder,
 
   hash = get_hash_for_group (group);
 
-  client = gconf_client_get_default ();
   sort_model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder, "section_treeview")));
   model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model));
 
-  /* Add all KeyEntry's for this section */
+  shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder, "shortcut_treeview")));
+
+  /* Add all CcKeyboardItems for this section */
   is_new = FALSE;
   keys_array = g_hash_table_lookup (hash, title);
   if (keys_array == NULL)
@@ -365,10 +268,8 @@ append_section (GtkBuilder         *builder,
 
   for (i = 0; keys_list[i].name != NULL; i++)
     {
-      GConfEntry *entry;
-      const gchar *key_string;
-      gchar *description, *command, *key_value;
-      KeyEntry *key_entry;
+      CcKeyboardItem *item;
+      gboolean ret;
 
       if (!should_show_key (&keys_list[i]))
         continue;
@@ -376,101 +277,29 @@ append_section (GtkBuilder         *builder,
       if (have_key_for_group (group, keys_list[i].name))
         continue;
 
-      key_string = keys_list[i].name;
+      item = cc_keyboard_item_new (keys_list[i].type);
+      if (keys_list[i].type == CC_KEYBOARD_ITEM_TYPE_GCONF)
+        ret = cc_keyboard_item_load_from_gconf (item, keys_list[i].name);
+      else
+        ret = cc_keyboard_item_load_from_gconf_dir (item, keys_list[i].name);
 
-      entry = gconf_client_get_entry (client,
-                                      key_string,
-                                      NULL,
-                                      TRUE,
-                                      NULL);
-      if (entry == NULL)
+      if (ret == FALSE)
         {
           /* We don't actually want to popup a dialog - just skip this one */
+          g_object_unref (item);
           continue;
         }
 
-      if (keys_list[i].description_name != NULL)
-        {
-          description = gconf_client_get_string (client, keys_list[i].description_name, NULL);
-        }
-      else
-        {
-          description = NULL;
-
-          if (gconf_entry_get_schema_name (entry))
-            {
-              GConfSchema *schema;
-
-              schema = gconf_client_get_schema (client,
-                                                gconf_entry_get_schema_name (entry),
-                                                NULL);
-              if (schema != NULL)
-                {
-                  description = g_strdup (gconf_schema_get_short_desc (schema));
-                  gconf_schema_free (schema);
-                }
-            }
-        }
-
-      if (description == NULL)
-        {
-          /* Only print a warning for keys that should have a schema */
-          if (keys_list[i].description_name == NULL)
-            g_warning ("No description for key '%s'", key_string);
-        }
+      item->model = shortcut_model;
+      item->group = group;
+      binding_from_string (item->binding, &item->keyval, &item->keycode, &item->mask);
 
-      if (keys_list[i].cmd_name != NULL)
-        {
-          command = gconf_client_get_string (client, keys_list[i].cmd_name, NULL);
-        }
-      else
-        {
-          command = NULL;
-        }
+      g_signal_connect (G_OBJECT (item), "notify",
+			G_CALLBACK (item_changed), NULL);
 
-      key_entry = g_new0 (KeyEntry, 1);
-      key_entry->gconf_key = g_strdup (key_string);
-      key_entry->editable = gconf_entry_get_is_writable (entry);
-      key_entry->model = model;
-      key_entry->group = group;
-      key_entry->description = description;
-      key_entry->command = command;
-      if (keys_list[i].description_name != NULL)
-        {
-          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);
-        }
-      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);
-        }
-
-      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_value = gconf_client_get_string (client, key_string, NULL);
-      binding_from_string (key_value, &key_entry->keyval, &key_entry->keycode, &key_entry->mask);
-      g_free (key_value);
-
-      gconf_entry_free (entry);
-
-      g_ptr_array_add (keys_array, key_entry);
+      g_ptr_array_add (keys_array, item);
     }
 
-  g_object_unref (client);
-
   /* Add the keys to the hash table */
   if (is_new && keys_array->len > 0)
     {
@@ -615,11 +444,10 @@ parse_start_tag (GMarkupParseContext *ctx,
     return;
 
   key.name = g_strdup (name);
-  key.description_name = NULL;
+  key.type = CC_KEYBOARD_ITEM_TYPE_GCONF;
   key.value = value;
   key.key = g_strdup (gconf_key);
   key.comparison = comparison;
-  key.cmd_name = NULL;
   g_array_append_val (keylist->entries, key);
 }
 
@@ -692,7 +520,6 @@ append_sections_from_file (GtkBuilder *builder, const gchar *path, gchar **wm_ke
 
   /* Empty KeyListEntry to end the array */
   key.name = NULL;
-  key.description_name = NULL;
   key.key = NULL;
   key.value = 0;
   key.comparison = COMPARISON_NONE;
@@ -742,11 +569,10 @@ append_sections_from_gconf (GtkBuilder *builder, const gchar *gconf_path)
 
   for (l = custom_list; l != NULL; l = l->next)
     {
-      key.name = g_strconcat (l->data, "/binding", NULL);
+      key.name = g_strdup (l->data);
       if (!have_key_for_group (BINDING_GROUP_USER, key.name))
         {
-          key.cmd_name = g_strconcat (l->data, "/action", NULL);
-          key.description_name = g_strconcat (l->data, "/name", NULL);
+          key.type = CC_KEYBOARD_ITEM_TYPE_GCONF_DIR;
           g_array_append_val (entries, key);
         }
       else
@@ -765,7 +591,6 @@ append_sections_from_gconf (GtkBuilder *builder, const gchar *gconf_path)
 
       /* Empty KeyListEntry to end the array */
       key.name = NULL;
-      key.description_name = NULL;
       g_array_append_val (entries, key);
 
       keys = (KeyListEntry *) entries->data;
@@ -773,7 +598,6 @@ append_sections_from_gconf (GtkBuilder *builder, const gchar *gconf_path)
       for (i = 0; i < entries->len; ++i)
         {
           g_free (keys[i].name);
-          g_free (keys[i].description_name);
         }
     }
 
@@ -834,32 +658,32 @@ accel_set_func (GtkTreeViewColumn *tree_column,
                 GtkTreeIter       *iter,
                 gpointer           data)
 {
-  KeyEntry *key_entry;
+  CcKeyboardItem *item;
 
   gtk_tree_model_get (model, iter,
-                      DETAIL_KEYENTRY_COLUMN, &key_entry,
+                      DETAIL_KEYENTRY_COLUMN, &item,
                       -1);
 
-  if (key_entry == NULL)
+  if (item == NULL)
     g_object_set (cell,
                   "visible", FALSE,
                   NULL);
-  else if (! key_entry->editable)
+  else if (! item->editable)
     g_object_set (cell,
                   "visible", TRUE,
                   "editable", FALSE,
-                  "accel_key", key_entry->keyval,
-                  "accel_mask", key_entry->mask,
-                  "keycode", key_entry->keycode,
+                  "accel_key", item->keyval,
+                  "accel_mask", item->mask,
+                  "keycode", item->keycode,
                   "style", PANGO_STYLE_ITALIC,
                   NULL);
   else
     g_object_set (cell,
                   "visible", TRUE,
                   "editable", TRUE,
-                  "accel_key", key_entry->keyval,
-                  "accel_mask", key_entry->mask,
-                  "keycode", key_entry->keycode,
+                  "accel_key", item->keyval,
+                  "accel_mask", item->mask,
+                  "keycode", item->keycode,
                   "style", PANGO_STYLE_NORMAL,
                   NULL);
 }
@@ -871,17 +695,17 @@ description_set_func (GtkTreeViewColumn *tree_column,
                       GtkTreeIter       *iter,
                       gpointer           data)
 {
-  KeyEntry *key_entry;
+  CcKeyboardItem *item;
 
   gtk_tree_model_get (model, iter,
-                      DETAIL_KEYENTRY_COLUMN, &key_entry,
+                      DETAIL_KEYENTRY_COLUMN, &item,
                       -1);
 
-  if (key_entry != NULL)
+  if (item != NULL)
     g_object_set (cell,
                   "editable", FALSE,
-                  "text", key_entry->description != NULL ?
-                          key_entry->description : _("<Unknown Action>"),
+                  "text", item->description != NULL ?
+                          item->description : _("<Unknown Action>"),
                   NULL);
   else
     g_object_set (cell,
@@ -894,14 +718,14 @@ shortcut_selection_changed (GtkTreeSelection *selection, gpointer data)
   GtkWidget *button = data;
   GtkTreeModel *model;
   GtkTreeIter iter;
-  KeyEntry *key;
+  CcKeyboardItem *item;
   gboolean can_remove;
 
   can_remove = FALSE;
   if (gtk_tree_selection_get_selected (selection, &model, &iter))
     {
-      gtk_tree_model_get (model, &iter, DETAIL_KEYENTRY_COLUMN, &key, -1);
-      if (key && key->command != NULL && key->editable)
+      gtk_tree_model_get (model, &iter, DETAIL_KEYENTRY_COLUMN, &item, -1);
+      if (item && item->command != NULL && item->editable)
         can_remove = TRUE;
     }
 
@@ -947,12 +771,12 @@ section_selection_changed (GtkTreeSelection *selection, gpointer data)
       for (i = 0; i < keys->len; i++)
         {
           GtkTreeIter new_row;
-          KeyEntry *entry = g_ptr_array_index (keys, i);
+          CcKeyboardItem *item = g_ptr_array_index (keys, i);
 
           gtk_list_store_append (GTK_LIST_STORE (shortcut_model), &new_row);
           gtk_list_store_set (GTK_LIST_STORE (shortcut_model), &new_row,
-                              DETAIL_DESCRIPTION_COLUMN, entry->description,
-                              DETAIL_KEYENTRY_COLUMN, entry,
+                              DETAIL_DESCRIPTION_COLUMN, item->description,
+                              DETAIL_KEYENTRY_COLUMN, item,
                               -1);
         }
     }
@@ -966,29 +790,29 @@ typedef struct
 } IdleData;
 
 static gboolean
-edit_custom_shortcut (KeyEntry *key)
+edit_custom_shortcut (CcKeyboardItem *item)
 {
   gint result;
-  const gchar *text;
+  const gchar *description, *command;
   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_entry_set_text (GTK_ENTRY (custom_shortcut_name_entry), item->description ? item->description : "");
+  gtk_widget_set_sensitive (custom_shortcut_name_entry, item->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_entry_set_text (GTK_ENTRY (custom_shortcut_command_entry), item->command ? item->command : "");
+  gtk_widget_set_sensitive (custom_shortcut_command_entry, item->cmd_editable);
 
   gtk_window_present (GTK_WINDOW (custom_shortcut_dialog));
   result = gtk_dialog_run (GTK_DIALOG (custom_shortcut_dialog));
   switch (result)
     {
     case GTK_RESPONSE_OK:
-      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);
+      description = gtk_entry_get_text (GTK_ENTRY (custom_shortcut_name_entry));
+      command = gtk_entry_get_text (GTK_ENTRY (custom_shortcut_command_entry));
+      g_object_set (G_OBJECT (item),
+		    "description", description,
+		    "command", command,
+		    NULL);
       ret = TRUE;
       break;
     default:
@@ -1006,26 +830,22 @@ remove_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
 {
   GConfClient *client;
   gchar *base;
-  KeyEntry *key;
+  CcKeyboardItem *item;
   GPtrArray *keys_array;
 
   gtk_tree_model_get (model, iter,
-                      DETAIL_KEYENTRY_COLUMN, &key,
+                      DETAIL_KEYENTRY_COLUMN, &item,
                       -1);
 
   /* not a custom shortcut */
-  if (key->command == NULL)
+  if (item->type != CC_KEYBOARD_ITEM_TYPE_GCONF_DIR)
     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 (item->gconf_key_dir);
+  g_object_unref (item);
 
-  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;
@@ -1035,14 +855,7 @@ remove_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
   g_object_unref (client);
 
   keys_array = g_hash_table_lookup (get_hash_for_group (BINDING_GROUP_USER), _("Custom Shortcuts"));
-  g_ptr_array_remove (keys_array, key);
-
-  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);
+  g_ptr_array_remove (keys_array, item);
 
   gtk_list_store_remove (GTK_LIST_STORE (model), iter);
 
@@ -1052,14 +865,14 @@ remove_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
 static void
 update_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
 {
-  KeyEntry *key;
+  CcKeyboardItem *item;
 
   gtk_tree_model_get (model, iter,
-                      DETAIL_KEYENTRY_COLUMN, &key,
+                      DETAIL_KEYENTRY_COLUMN, &item,
                       -1);
 
-  edit_custom_shortcut (key);
-  if (key->command == NULL || key->command[0] == '\0')
+  edit_custom_shortcut (item);
+  if (item->command == NULL || item->command[0] == '\0')
     {
       remove_custom_shortcut (model, iter);
     }
@@ -1068,13 +881,13 @@ update_custom_shortcut (GtkTreeModel *model, GtkTreeIter *iter)
       GConfClient *client;
 
       gtk_list_store_set (GTK_LIST_STORE (model), iter,
-                          DETAIL_KEYENTRY_COLUMN, key, -1);
+                          DETAIL_KEYENTRY_COLUMN, item, -1);
       client = gconf_client_get_default ();
-      if (key->description != NULL)
-        gconf_client_set_string (client, key->desc_gconf_key, key->description, NULL);
+      if (item->description != NULL)
+        gconf_client_set_string (client, item->desc_gconf_key, item->description, NULL);
       else
-        gconf_client_unset (client, key->desc_gconf_key, NULL);
-      gconf_client_set_string (client, key->cmd_gconf_key, key->command, NULL);
+        gconf_client_unset (client, item->desc_gconf_key, NULL);
+      gconf_client_set_string (client, item->cmd_gconf_key, item->command, NULL);
       g_object_unref (client);
     }
 }
@@ -1112,7 +925,7 @@ start_editing_cb (GtkTreeView    *tree_view,
       IdleData *idle_data;
       GtkTreeModel *model;
       GtkTreeIter iter;
-      KeyEntry *key;
+      CcKeyboardItem *item;
 
       if (gtk_tree_path_get_depth (path) == 1)
         {
@@ -1123,12 +936,12 @@ start_editing_cb (GtkTreeView    *tree_view,
       model = gtk_tree_view_get_model (tree_view);
       gtk_tree_model_get_iter (model, &iter, path);
       gtk_tree_model_get (model, &iter,
-                          DETAIL_KEYENTRY_COLUMN, &key,
+                          DETAIL_KEYENTRY_COLUMN, &item,
                          -1);
 
       /* if only the accel can be edited on the selected row
        * always select the accel column */
-      if (key->desc_editable &&
+      if (item->desc_editable &&
           column == gtk_tree_view_get_column (tree_view, 0))
         {
           gtk_widget_grab_focus (GTK_WIDGET (tree_view));
@@ -1142,7 +955,7 @@ start_editing_cb (GtkTreeView    *tree_view,
           idle_data = g_new (IdleData, 1);
           idle_data->tree_view = tree_view;
           idle_data->path = path;
-          idle_data->column = key->desc_editable ? column :
+          idle_data->column = item->desc_editable ? column :
                               gtk_tree_view_get_column (tree_view, 1);
           g_idle_add ((GSourceFunc) real_start_editing_cb, idle_data);
           block_accels = TRUE;
@@ -1160,15 +973,15 @@ start_editing_kb_cb (GtkTreeView *treeview,
 {
   GtkTreeModel *model;
   GtkTreeIter iter;
-  KeyEntry *key;
+  CcKeyboardItem *item;
 
   model = gtk_tree_view_get_model (treeview);
   gtk_tree_model_get_iter (model, &iter, path);
   gtk_tree_model_get (model, &iter,
-                      DETAIL_KEYENTRY_COLUMN, &key,
+                      DETAIL_KEYENTRY_COLUMN, &item,
                       -1);
 
-  if (key == NULL)
+  if (item == NULL)
     {
       /* This is a section heading - expand or collapse */
       if (gtk_tree_view_row_expanded (treeview, path))
@@ -1180,7 +993,7 @@ start_editing_kb_cb (GtkTreeView *treeview,
 
   /* if only the accel can be edited on the selected row
    * always select the accel column */
-  if (key->desc_editable &&
+  if (item->desc_editable &&
       column == gtk_tree_view_get_column (treeview, 0))
     {
       gtk_widget_grab_focus (GTK_WIDGET (treeview));
@@ -1210,23 +1023,23 @@ description_edited_callback (GtkCellRendererText *renderer,
   GtkTreeModel *model;
   GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
   GtkTreeIter iter;
-  KeyEntry *key_entry;
+  CcKeyboardItem *item;
 
   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, &key_entry,
+                      DETAIL_KEYENTRY_COLUMN, &item,
                       -1);
 
   /* sanity check */
-  if (key_entry == NULL || key_entry->desc_gconf_key == NULL)
+  if (item == NULL || item->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;
+  if (!gconf_client_set_string (client, item->desc_gconf_key, new_text, NULL))
+    item->desc_editable = FALSE;
 
   g_object_unref (client);
 }
@@ -1297,33 +1110,33 @@ show_error (GtkWindow *parent,
 }
 
 static gboolean
-cb_check_for_uniqueness (GtkTreeModel *model,
-                         GtkTreePath  *path,
-                         GtkTreeIter  *iter,
-                         KeyEntry *new_key)
+cb_check_for_uniqueness (GtkTreeModel   *model,
+                         GtkTreePath    *path,
+                         GtkTreeIter    *iter,
+                         CcKeyboardItem *new_item)
 {
-  KeyEntry *element;
+  CcKeyboardItem *element;
 
-  gtk_tree_model_get (new_key->model, iter,
+  gtk_tree_model_get (new_item->model, iter,
                       DETAIL_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))
+  if (element == NULL || new_item->mask != element->mask ||
+      !strcmp (new_item->gconf_key, element->gconf_key))
     return FALSE;
 
-  if (new_key->keyval != 0) {
-      if (new_key->keyval != element->keyval)
+  if (new_item->keyval != 0) {
+      if (new_item->keyval != element->keyval)
           return FALSE;
-  } else if (element->keyval != 0 || new_key->keycode != element->keycode)
+  } else if (element->keyval != 0 || new_item->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;
+  new_item->editable = FALSE;
+  new_item->gconf_key = element->gconf_key;
+  new_item->description = element->description;
+  new_item->desc_gconf_key = element->desc_gconf_key;
+  new_item->desc_editable = element->desc_editable;
   return TRUE;
 }
 
@@ -1340,7 +1153,7 @@ accel_edited_callback (GtkCellRendererText   *cell,
   GtkTreeModel *model;
   GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
   GtkTreeIter iter;
-  KeyEntry *key_entry, tmp_key;
+  CcKeyboardItem *item, tmp_item;
   GError *err = NULL;
   char *str;
 
@@ -1350,44 +1163,44 @@ accel_edited_callback (GtkCellRendererText   *cell,
   gtk_tree_model_get_iter (model, &iter, path);
   gtk_tree_path_free (path);
   gtk_tree_model_get (model, &iter,
-                      DETAIL_KEYENTRY_COLUMN, &key_entry,
+                      DETAIL_KEYENTRY_COLUMN, &item,
                       -1);
 
   /* sanity check */
-  if (key_entry == NULL)
+  if (item == 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 */
+  tmp_item.model  = model;
+  tmp_item.keyval = keyval;
+  tmp_item.keycode = keycode;
+  tmp_item.mask   = mask;
+  tmp_item.gconf_key = item->gconf_key;
+  tmp_item.description = NULL;
+  tmp_item.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);
+      &tmp_item);
 
   /* Check for unmodified keys */
-  if (tmp_key.mask == 0 && tmp_key.keycode != 0)
+  if (tmp_item.mask == 0 && tmp_item.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)) {
+      if ((tmp_item.keyval >= GDK_KEY_a && tmp_item.keyval <= GDK_KEY_z)
+           || (tmp_item.keyval >= GDK_KEY_A && tmp_item.keyval <= GDK_KEY_Z)
+           || (tmp_item.keyval >= GDK_KEY_0 && tmp_item.keyval <= GDK_KEY_9)
+           || (tmp_item.keyval >= GDK_KEY_kana_fullstop && tmp_item.keyval <= GDK_KEY_semivoicedsound)
+           || (tmp_item.keyval >= GDK_KEY_Arabic_comma && tmp_item.keyval <= GDK_KEY_Arabic_sukun)
+           || (tmp_item.keyval >= GDK_KEY_Serbian_dje && tmp_item.keyval <= GDK_KEY_Cyrillic_HARDSIGN)
+           || (tmp_item.keyval >= GDK_KEY_Greek_ALPHAaccent && tmp_item.keyval <= GDK_KEY_Greek_omega)
+           || (tmp_item.keyval >= GDK_KEY_hebrew_doublelowline && tmp_item.keyval <= GDK_KEY_hebrew_taf)
+           || (tmp_item.keyval >= GDK_KEY_Thai_kokai && tmp_item.keyval <= GDK_KEY_Thai_lekkao)
+           || (tmp_item.keyval >= GDK_KEY_Hangul && tmp_item.keyval <= GDK_KEY_Hangul_Special)
+           || (tmp_item.keyval >= GDK_KEY_Hangul_Kiyeog && tmp_item.keyval <= GDK_KEY_Hangul_J_YeorinHieuh)
+           || keyval_is_forbidden (tmp_item.keyval)) {
         GtkWidget *dialog;
         char *name;
 
@@ -1409,13 +1222,13 @@ accel_edited_callback (GtkCellRendererText   *cell,
         /* 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);
+           item->keyval, item->keycode, item->mask);
         return;
       }
     }
 
   /* flag to see if the new accelerator was in use by something */
-  if (!tmp_key.editable)
+  if (!tmp_item.editable)
     {
       GtkWidget *dialog;
       char *name;
@@ -1429,17 +1242,17 @@ accel_edited_callback (GtkCellRendererText   *cell,
                                 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);
+                                name, tmp_item.description ?
+                                tmp_item.description : tmp_item.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);
+          item->description ?
+          item->description : item->gconf_key,
+          tmp_item.description ?
+          tmp_item.description : tmp_item.gconf_key);
 
       gtk_dialog_add_button (GTK_DIALOG (dialog),
                              _("_Reassign"),
@@ -1458,7 +1271,7 @@ accel_edited_callback (GtkCellRendererText   *cell,
           client = gconf_client_get_default ();
 
           gconf_client_set_string (client,
-                                   tmp_key.gconf_key,
+                                   tmp_item.gconf_key,
                                    "", &err);
 
           if (err != NULL)
@@ -1472,7 +1285,7 @@ accel_edited_callback (GtkCellRendererText   *cell,
 
           str = binding_name (keyval, keycode, mask, FALSE);
           gconf_client_set_string (client,
-                                   key_entry->gconf_key,
+                                   item->gconf_key,
                                    str, &err);
 
           if (err != NULL)
@@ -1483,7 +1296,7 @@ accel_edited_callback (GtkCellRendererText   *cell,
 
                /* reset the previous shortcut */
                gconf_client_set_string (client,
-                                        tmp_key.gconf_key,
+                                        tmp_item.gconf_key,
                                         str, NULL);
             }
 
@@ -1494,9 +1307,9 @@ accel_edited_callback (GtkCellRendererText   *cell,
         {
           /* 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);
+                                                  item->keyval,
+                                                  item->keycode,
+                                                  item->mask);
         }
 
       return;
@@ -1506,7 +1319,7 @@ accel_edited_callback (GtkCellRendererText   *cell,
 
   client = gconf_client_get_default ();
   gconf_client_set_string (client,
-                           key_entry->gconf_key,
+                           item->gconf_key,
                            str,
                            &err);
   g_free (str);
@@ -1516,7 +1329,7 @@ accel_edited_callback (GtkCellRendererText   *cell,
     {
       show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))), err);
       g_error_free (err);
-      key_entry->editable = FALSE;
+      item->editable = FALSE;
     }
 }
 
@@ -1528,7 +1341,7 @@ accel_cleared_callback (GtkCellRendererText *cell,
   GConfClient *client;
   GtkTreeView *view = (GtkTreeView *) data;
   GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
-  KeyEntry *key_entry;
+  CcKeyboardItem *item;
   GtkTreeIter iter;
   GError *err = NULL;
   GtkTreeModel *model;
@@ -1539,18 +1352,18 @@ accel_cleared_callback (GtkCellRendererText *cell,
   gtk_tree_model_get_iter (model, &iter, path);
   gtk_tree_path_free (path);
   gtk_tree_model_get (model, &iter,
-                      DETAIL_KEYENTRY_COLUMN, &key_entry,
+                      DETAIL_KEYENTRY_COLUMN, &item,
                       -1);
 
   /* sanity check */
-  if (key_entry == NULL)
+  if (item == NULL)
     return;
 
   /* Unset the key */
   client = gconf_client_get_default();
   gconf_client_set_string (client,
-                           key_entry->gconf_key,
-                           "",
+			   item->gconf_key,
+			   "",
                            &err);
   g_object_unref (client);
 
@@ -1568,7 +1381,7 @@ accel_cleared_callback (GtkCellRendererText *cell,
 
       gtk_widget_destroy (dialog);
       g_error_free (err);
-      key_entry->editable = FALSE;
+      item->editable = FALSE;
     }
 }
 
@@ -1632,7 +1445,7 @@ static void
 add_custom_shortcut (GtkTreeView  *tree_view,
                      GtkTreeModel *model)
 {
-  KeyEntry *key_entry;
+  CcKeyboardItem *item;
   GtkTreePath *path;
   gchar *dir;
   GConfClient *client;
@@ -1648,25 +1461,38 @@ add_custom_shortcut (GtkTreeView  *tree_view,
       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])
+  /* FIXME this is way ugly */
+  item = cc_keyboard_item_new (CC_KEYBOARD_ITEM_TYPE_GCONF_DIR);
+  item->gconf_key_dir = g_strdup (dir);
+  item->gconf_key = g_strconcat (dir, "/binding", NULL);
+  item->editable = TRUE;
+  item->model = model;
+  item->desc_gconf_key = g_strconcat (dir, "/name", NULL);
+  item->description = g_strdup ("");
+  item->desc_editable = TRUE;
+  item->cmd_gconf_key = g_strconcat (dir, "/action", NULL);
+  item->command = g_strdup ("");
+  item->cmd_editable = TRUE;
+
+  if (edit_custom_shortcut (item) &&
+      item->command && item->command[0])
     {
       GPtrArray *keys_array;
       GtkTreeIter iter;
       GHashTable *hash;
 
+      /* store in gconf */
+      client = gconf_client_get_default ();
+      gconf_client_set_string (client, item->gconf_key, "", NULL);
+      gconf_client_set_string (client, item->desc_gconf_key, item->description, NULL);
+      gconf_client_set_string (client, item->cmd_gconf_key, item->command, NULL);
+      g_object_unref (client);
+      g_object_unref (item);
+
+      /* Now setup the actual item we'll be adding */
+      item = cc_keyboard_item_new (CC_KEYBOARD_ITEM_TYPE_GCONF_DIR);
+      cc_keyboard_item_load_from_gconf_dir (item, dir);
+
       hash = get_hash_for_group (BINDING_GROUP_USER);
       keys_array = g_hash_table_lookup (hash, _("Custom Shortcuts"));
       if (keys_array == NULL)
@@ -1675,33 +1501,10 @@ add_custom_shortcut (GtkTreeView  *tree_view,
           g_hash_table_insert (hash, g_strdup (_("Custom Shortcuts")), keys_array);
         }
 
-      g_ptr_array_add (keys_array, key_entry);
+      g_ptr_array_add (keys_array, item);
 
       gtk_list_store_append (GTK_LIST_STORE (model), &iter);
-      gtk_list_store_set (GTK_LIST_STORE (model), &iter, DETAIL_KEYENTRY_COLUMN, 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);
+      gtk_list_store_set (GTK_LIST_STORE (model), &iter, DETAIL_KEYENTRY_COLUMN, item, -1);
 
       /* make the new shortcut visible */
       path = gtk_tree_model_get_path (model, &iter);
@@ -1711,12 +1514,7 @@ add_custom_shortcut (GtkTreeView  *tree_view,
     }
   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);
+      g_object_unref (item);
     }
 }
 
@@ -1760,35 +1558,35 @@ keyentry_sort_func (GtkTreeModel *model,
                     GtkTreeIter  *b,
                     gpointer      user_data)
 {
-  KeyEntry *key_entry_a;
-  KeyEntry *key_entry_b;
+  CcKeyboardItem *item_a;
+  CcKeyboardItem *item_b;
   int retval;
 
-  key_entry_a = NULL;
+  item_a = NULL;
   gtk_tree_model_get (model, a,
-                      DETAIL_KEYENTRY_COLUMN, &key_entry_a,
+                      DETAIL_KEYENTRY_COLUMN, &item_a,
                       -1);
 
-  key_entry_b = NULL;
+  item_b = NULL;
   gtk_tree_model_get (model, b,
-                      DETAIL_KEYENTRY_COLUMN, &key_entry_b,
+                      DETAIL_KEYENTRY_COLUMN, &item_b,
                       -1);
 
-  if (key_entry_a && key_entry_b)
+  if (item_a && item_b)
     {
-      if ((key_entry_a->keyval || key_entry_a->keycode) &&
-          (key_entry_b->keyval || key_entry_b->keycode))
+      if ((item_a->keyval || item_a->keycode) &&
+          (item_b->keyval || item_b->keycode))
         {
           gchar *name_a, *name_b;
 
-          name_a = binding_name (key_entry_a->keyval,
-                                 key_entry_a->keycode,
-                                 key_entry_a->mask,
+          name_a = binding_name (item_a->keyval,
+                                 item_a->keycode,
+                                 item_a->mask,
                                  TRUE);
 
-          name_b = binding_name (key_entry_b->keyval,
-                                 key_entry_b->keycode,
-                                 key_entry_b->mask,
+          name_b = binding_name (item_b->keyval,
+                                 item_b->keycode,
+                                 item_b->mask,
                                  TRUE);
 
           retval = g_utf8_collate (name_a, name_b);
@@ -1796,16 +1594,16 @@ keyentry_sort_func (GtkTreeModel *model,
           g_free (name_a);
           g_free (name_b);
         }
-      else if (key_entry_a->keyval || key_entry_a->keycode)
+      else if (item_a->keyval || item_a->keycode)
         retval = -1;
-      else if (key_entry_b->keyval || key_entry_b->keycode)
+      else if (item_b->keyval || item_b->keycode)
         retval = 1;
       else
         retval = 0;
     }
-  else if (key_entry_a)
+  else if (item_a)
     retval = -1;
-  else if (key_entry_b)
+  else if (item_b)
     retval = 1;
   else
     retval = 0;



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