summary refs log tree commit diff stats
path: root/src/fe-gtk
diff options
context:
space:
mode:
authorTingPing <tingping@tingping.se>2013-10-10 14:49:37 -0400
committerTingPing <tingping@tingping.se>2014-02-14 16:29:44 -0500
commit725aed43ff7282e834a0deeb0f1136dbb909abfd (patch)
tree61529f7cc54a816931d842a488d48a3f8b81b318 /src/fe-gtk
parent655b524ee642325b3ff8a34b821c81869d1d7c83 (diff)
Redesign keyboard shortcuts window
- Convert GtkCLists to GtkTreeViews
- Save keybindings in GtkAccelerator format
- Store keybindings in a GSList
Diffstat (limited to 'src/fe-gtk')
-rw-r--r--src/fe-gtk/fkeys.c1009
-rw-r--r--src/fe-gtk/gtkutil.c70
2 files changed, 489 insertions, 590 deletions
diff --git a/src/fe-gtk/fkeys.c b/src/fe-gtk/fkeys.c
index cdbdc0d0..eea07df6 100644
--- a/src/fe-gtk/fkeys.c
+++ b/src/fe-gtk/fkeys.c
@@ -74,12 +74,10 @@ void key_action_tab_clean (void);
 
 struct key_binding
 {
-	int keyval;						  /* GDK keynumber */
-	char *keyname;					  /* String with the name of the function */
+	guint keyval;					  /* keyval from gdk */
+	GdkModifierType mod;			  /* Modifier, always ran through key_modifier_get_valid() */
 	int action;						  /* Index into key_actions */
-	int mod;							  /* Flags of STATE_* above */
 	char *data1, *data2;			  /* Pointers to strings, these must be freed */
-	struct key_binding *next;
 };
 
 struct key_action
@@ -97,7 +95,7 @@ struct gcomp_data
 };
 
 static int key_load_kbs ();
-static void key_save_kbs ();
+static int key_save_kbs ();
 static int key_action_handle_command (GtkWidget * wid, GdkEventKey * evt,
 												  char *d1, char *d2,
 												  struct session *sess);
@@ -135,8 +133,7 @@ static int key_action_put_history (GtkWidget * wid, GdkEventKey * evt,
 												  char *d1, char *d2,
 												  struct session *sess);
 
-static GtkWidget *key_dialog;
-static struct key_binding *keys_root = NULL;
+static GSList *keybind_list = NULL;
 
 static const struct key_action key_actions[KEY_MAX_ACTIONS + 1] = {
 
@@ -214,7 +211,6 @@ static const struct key_action key_actions[KEY_MAX_ACTIONS + 1] = {
 void
 key_init ()
 {
-	keys_root = NULL;
 	if (key_load_kbs () == 1)
 	{
 		fe_message (_("There was an error loading key"
@@ -222,10 +218,34 @@ key_init ()
 	}
 }
 
-static char *
-key_get_key_name (int keyval)
+static inline int
+key_get_action_from_string (char *text)
 {
-	return gdk_keyval_name (gdk_keyval_to_lower (keyval));
+	int i;
+
+	for (i = 0; i < KEY_MAX_ACTIONS + 1; i++)
+	{
+		if (strcmp (key_actions[i].name, text) == 0)
+		{
+			return i;
+		}
+	}
+
+	return 0;
+}
+
+static void
+key_free (gpointer data)
+{
+	struct key_binding *kb = (struct key_binding*)data;
+
+	g_return_if_fail (kb != NULL);
+
+	if (kb->data1)
+		g_free (kb->data1);
+	if (kb->data2)
+		g_free (kb->data2);
+	g_free (kb);
 }
 
 /* Ok, here are the NOTES
@@ -252,10 +272,9 @@ key_get_key_name (int keyval)
    }
    * key bindings are stored in a linked list of key_binding structs
    * which looks like {
-   int keyval;  GDK keynumber
-   char *keyname;  String with the name of the function 
+   guint keyval;  GDK keynumber
    int action;  Index into key_actions 
-   int mod; Flags of STATE_* above 
+   GdkModiferType mod; modifier, only ones from key_modifer_get_valid()
    char *data1, *data2;  Pointers to strings, these must be freed 
    struct key_binding *next;
    }
@@ -265,12 +284,18 @@ key_get_key_name (int keyval)
 
  */
 
+static inline GdkModifierType
+key_modifier_get_valid (GdkModifierType mod)
+{
+	/* These masks work on both Windows and Unix */
+	return mod & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK);
+}
+
 gboolean
 key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess)
 {
-	struct key_binding *kb, *last = NULL;
-	int keyval = evt->keyval;
-	int mod, n;
+	struct key_binding *kb;
+	int n;
 	GSList *list;
 
 	/* where did this event come from? */
@@ -297,23 +322,16 @@ key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess)
 	if (!is_session (sess))
 		return 1;
 
-	mod = evt->state & (STATE_CTRL | STATE_ALT | STATE_SHIFT);
-
-	kb = keys_root;
-	while (kb)
+	list = keybind_list;
+	while (list)
 	{
-		if (kb->keyval == keyval && kb->mod == mod)
+		kb = (struct key_binding*)list->data;
+
+		if (kb->keyval == evt->keyval && kb->mod == key_modifier_get_valid (evt->state))
 		{
 			if (kb->action < 0 || kb->action > KEY_MAX_ACTIONS)
 				return 0;
 
-			/* Bump this binding to the top of the list */
-			if (last != NULL)
-			{
-				last->next = kb->next;
-				kb->next = keys_root;
-				keys_root = kb;
-			}
 			/* Run the function */
 			n = key_actions[kb->action].handler (wid, evt, kb->data1,
 															 kb->data2, sess);
@@ -327,11 +345,10 @@ key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess)
 				return 1;
 			}
 		}
-		last = kb;
-		kb = kb->next;
+		list = g_slist_next (list);
 	}
 
-	switch (keyval)
+	switch (evt->keyval)
 	{
 	case GDK_KEY_space:
 		key_action_tab_clean ();
@@ -341,335 +358,419 @@ key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess)
 	return 0;
 }
 
-/* Walks keys_root and free()'s everything */
-/*static void
-key_free_all ()
-{
-	struct key_binding *cur, *next;
 
-	cur = keys_root;
-	while (cur)
-	{
-		next = cur->next;
-		if (cur->data1)
-			free (cur->data1);
-		if (cur->data2)
-			free (cur->data2);
-		free (cur);
-		cur = next;
-	}
-	keys_root = NULL;
-}*/
+/* ***** GUI code here ******************* */
 
-/* Turns mod flags into a C-A-S string */
-static char *
-key_make_mod_str (int mod, char *buf)
+enum
 {
-	int i = 0;
+	KEY_COLUMN,
+	ACTION_COLUMN,
+	D1_COLUMN,
+	D2_COLUMN,
+	N_COLUMNS
+};
 
-	if (mod & STATE_CTRL)
-	{
-		if (i)
-			buf[i++] = '-';
-		buf[i++] = 'C';
-	}
-	if (mod & STATE_ALT)
-	{
-		if (i)
-			buf[i++] = '-';
-		buf[i++] = 'A';
-	}
-	if (mod & STATE_SHIFT)
-	{
-		if (i)
-			buf[i++] = '-';
-		buf[i++] = 'S';
-	}
-	buf[i] = 0;
-	return buf;
-}
+static GtkWidget *key_dialog = NULL;
 
-/* ***** GUI code here ******************* */
+static inline GtkTreeModel *
+get_store (void)
+{
+	return gtk_tree_view_get_model (g_object_get_data (G_OBJECT (key_dialog), "view"));
+}
 
-/* NOTE: The key_dialog defin is above --AGL */
-static GtkWidget *key_dialog_act_menu, *key_dialog_kb_clist;
-static GtkWidget *key_dialog_tog_c, *key_dialog_tog_s, *key_dialog_tog_a;
-static GtkWidget *key_dialog_ent_key, *key_dialog_ent_d1, *key_dialog_ent_d2;
-static GtkWidget *key_dialog_text;
+static void
+key_dialog_print_text (GtkXText *xtext, char *text)
+{
+	unsigned int old = prefs.hex_stamp_text;
+	prefs.hex_stamp_text = 0;	/* temporarily disable stamps */
+	gtk_xtext_clear (GTK_XTEXT (xtext)->buffer, 0);
+	PrintTextRaw (GTK_XTEXT (xtext)->buffer, text, 0, 0);
+	prefs.hex_stamp_text = old;
+}
 
 static void
-key_dialog_close ()
+key_dialog_set_key (GtkCellRendererAccel *accel, gchar *pathstr, guint accel_key, 
+					GdkModifierType accel_mods, guint hardware_keycode, gpointer userdata)
 {
-	key_dialog = NULL;
-	key_save_kbs ();
+	GtkTreeModel *model = get_store ();
+	GtkTreePath *path = gtk_tree_path_new_from_string (pathstr);
+	GtkTreeIter iter;
+	gchar *key_name;
+
+	key_name = gtk_accelerator_name (accel_key, key_modifier_get_valid (accel_mods));
+
+	gtk_tree_model_get_iter (model, &iter, path);
+	gtk_list_store_set (GTK_LIST_STORE (model), &iter, KEY_COLUMN, key_name, -1);
+
+	gtk_tree_path_free (path);
+	g_free (key_name);
 }
 
 static void
-key_dialog_add_new (GtkWidget * button, GtkCList * list)
+key_dialog_combo_changed (GtkCellRendererCombo *combo, gchar *pathstr,
+						GtkTreeIter *new_iter, gpointer data)
 {
-	gchar *strs[] = { "", NULL, NULL, NULL, NULL };
-	struct key_binding *kb;
+	GtkTreeModel *model;
+	GtkXText *xtext;
+	gchar *actiontext = NULL;
+	gint action;
 
-	strs[1] = _("<none>");
-	strs[2] = _("<none>");
-	strs[3] = _("<none>");
-	strs[4] = _("<none>");
+	xtext = GTK_XTEXT (g_object_get_data (G_OBJECT (key_dialog), "xtext"));
+	model = GTK_TREE_MODEL (data);
 
-	kb = malloc (sizeof (struct key_binding));
+	gtk_tree_model_get (model, new_iter, 0, &actiontext, -1);
 
-	kb->keyval = 0;
-	kb->keyname = NULL;
-	kb->action = -1;
-	kb->mod = 0;
-	kb->data1 = kb->data2 = NULL;
-	kb->next = keys_root;
+	if (actiontext)
+	{
+#ifdef WIN32
+		/* We need to manually update the store */
+		GtkTreePath *path;
+		GtkTreeIter iter;
 
-	keys_root = kb;
+		path = gtk_tree_path_new_from_string (pathstr);
+		model = get_store ();
+
+		gtk_tree_model_get_iter (model, &iter, path);
+		gtk_list_store_set (GTK_LIST_STORE (model), &iter, ACTION_COLUMN, actiontext, -1);
+
+		gtk_tree_path_free (path);
+#endif
 
-	gtk_clist_set_row_data (GTK_CLIST (list),
-									gtk_clist_append (GTK_CLIST (list), strs), kb);
+		action = key_get_action_from_string (actiontext);
+		key_dialog_print_text (xtext, key_actions[action].help);
 
+		g_free (actiontext);
+	}
 }
 
 static void
-key_dialog_delete (GtkWidget * button, GtkCList * list)
+key_dialog_entry_edited (GtkCellRendererText *render, gchar *pathstr, gchar *new_text, gpointer data)
 {
-	struct key_binding *kb, *cur, *last;
-	int row = gtkutil_clist_selection ((GtkWidget *) list);
+	GtkTreeModel *model = get_store ();
+	GtkTreePath *path = gtk_tree_path_new_from_string (pathstr);
+	GtkTreeIter iter;
+	gint column = GPOINTER_TO_INT (data);
+
+	gtk_tree_model_get_iter (model, &iter, path);
+	gtk_list_store_set (GTK_LIST_STORE (model), &iter, column, new_text, -1);
+
+	gtk_tree_path_free (path);
+}
 
-	if (row != -1)
+static gboolean
+key_dialog_keypress (GtkWidget *wid, GdkEventKey *evt, gpointer userdata)
+{
+	GtkTreeView *view = g_object_get_data (G_OBJECT (key_dialog), "view");
+	GtkTreeModel *store;
+	GtkTreeIter iter1, iter2;
+	GtkTreeSelection *sel;
+	GtkTreePath *path;
+	gboolean handled = FALSE;
+	int delta;
+
+	if (evt->state & GDK_SHIFT_MASK)
 	{
-		kb = gtk_clist_get_row_data (list, row);
-		cur = keys_root;
-		last = NULL;
-		while (cur)
+		if (evt->keyval == GDK_KEY_Up)
 		{
-			if (cur == kb)
-			{
-				if (last)
-					last->next = kb->next;
-				else
-					keys_root = kb->next;
-
-				if (kb->data1)
-					free (kb->data1);
-				if (kb->data2)
-					free (kb->data2);
-				free (kb);
-				gtk_clist_remove (list, row);
-				return;
-			}
-			last = cur;
-			cur = cur->next;
+			handled = TRUE;
+			delta = -1;
+		}
+		else if (evt->keyval == GDK_KEY_Down)
+		{
+			handled = TRUE;
+			delta = 1;
 		}
-		printf ("*** key_dialog_delete: couldn't find kb in list!\n");
-		/*if (getenv ("HEXCHAT_DEBUG"))
-			abort ();*/
 	}
-}
 
-static void
-key_print_text (GtkXText *xtext, char *text)
-{
-	unsigned int old = prefs.hex_stamp_text;
-	prefs.hex_stamp_text = 0;	/* temporarily disable stamps */
-	gtk_xtext_clear (GTK_XTEXT (xtext)->buffer, 0);
-	PrintTextRaw (GTK_XTEXT (xtext)->buffer, text, 0, 0);
-	prefs.hex_stamp_text = old;
+	if (handled)
+	{
+		sel = gtk_tree_view_get_selection (view);
+		gtk_tree_selection_get_selected (sel, &store, &iter1);
+		path = gtk_tree_model_get_path (store, &iter1);
+		if (delta == 1)
+			gtk_tree_path_next (path);
+		else
+			gtk_tree_path_prev (path);
+		gtk_tree_model_get_iter (store, &iter2, path);
+		gtk_tree_path_free (path);
+		gtk_list_store_swap (GTK_LIST_STORE (store), &iter1, &iter2);
+	}
+
+	return handled;
 }
 
 static void
-key_dialog_sel_act (GtkWidget * un, int num)
+key_dialog_selection_changed (GtkTreeSelection *sel, gpointer userdata)
 {
-	int row = gtkutil_clist_selection (key_dialog_kb_clist);
-	struct key_binding *kb;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	GtkXText *xtext;
+	char *actiontext;
+	int action;
 
-	if (row != -1)
+	if (!gtk_tree_selection_get_selected (sel, &model, &iter) || model == NULL)
+		return;
+
+	xtext = GTK_XTEXT (g_object_get_data (G_OBJECT (key_dialog), "xtext"));
+	gtk_tree_model_get (model, &iter, ACTION_COLUMN, &actiontext, -1);
+
+	if (actiontext)
 	{
-		kb = gtk_clist_get_row_data (GTK_CLIST (key_dialog_kb_clist), row);
-		kb->action = num;
-		gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 2,
-								  _(key_actions[num].name));
-		if (key_actions[num].help)
-		{
-			key_print_text (GTK_XTEXT (key_dialog_text), _(key_actions[num].help));
-		}
+		action = key_get_action_from_string (actiontext);
+		key_dialog_print_text (xtext, key_actions[action].help);
+		g_free (actiontext);
 	}
+	else
+		key_dialog_print_text (xtext, _("Select a row to get help information on its Action."));
+}
+
+static void
+key_dialog_close (GtkWidget *wid, gpointer userdata)
+{
+	gtk_widget_destroy (key_dialog);
+	key_dialog = NULL;
 }
 
 static void
-key_dialog_sel_row (GtkWidget * clist, gint row, gint column,
-						  GdkEventButton * evt, gpointer data)
+key_dialog_save (GtkWidget *wid, gpointer userdata)
 {
-	struct key_binding *kb = gtk_clist_get_row_data (GTK_CLIST (clist), row);
+	GtkTreeModel *store = get_store ();
+	GtkTreeIter iter;
+	struct key_binding *kb;
+	char *data1, *data2, *accel, *actiontext;
+	guint keyval;
+	GdkModifierType mod;
 
-	if (kb == NULL)
+	if (keybind_list)
 	{
-		printf ("*** key_dialog_sel_row: kb == NULL\n");
-		abort ();
+		g_slist_free_full (keybind_list, key_free);
+		keybind_list = NULL;
 	}
-	if (kb->action > -1 && kb->action <= KEY_MAX_ACTIONS)
+
+	if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
 	{
-		gtk_option_menu_set_history (GTK_OPTION_MENU (key_dialog_act_menu),
-											  kb->action);
-		if (key_actions[kb->action].help)
+		do
 		{
-			key_print_text (GTK_XTEXT (key_dialog_text), _(key_actions[kb->action].help));
-		}
-	}
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (key_dialog_tog_c),
-										  (kb->mod & STATE_CTRL) == STATE_CTRL);
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (key_dialog_tog_s),
-										  (kb->mod & STATE_SHIFT) == STATE_SHIFT);
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (key_dialog_tog_a),
-										  (kb->mod & STATE_ALT) == STATE_ALT);
+			kb = (struct key_binding *) g_malloc0 (sizeof (struct key_binding));
 
-	if (kb->data1)
-		gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_d1), kb->data1);
-	else
-		gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_d1), "");
+			gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, KEY_COLUMN, &accel,
+															ACTION_COLUMN, &actiontext,
+															D1_COLUMN, &data1,
+															D2_COLUMN, &data2,
+															-1);
+			kb->data1 = data1;
+			kb->data2 = data2;
 
-	if (kb->data2)
-		gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_d2), kb->data2);
-	else
-		gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_d2), "");
+			if (accel)
+			{
+				gtk_accelerator_parse (accel, &keyval, &mod);
 
-	if (kb->keyname)
-		gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_key), kb->keyname);
-	else
-		gtk_entry_set_text (GTK_ENTRY (key_dialog_ent_key), "");
-}
+				kb->keyval = keyval;
+				kb->mod = key_modifier_get_valid (mod);
 
-static void
-key_dialog_tog_key (GtkWidget * tog, int kstate)
-{
-	int state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (tog));
-	int row = gtkutil_clist_selection (key_dialog_kb_clist);
-	struct key_binding *kb;
-	char buf[32];
+				g_free (accel);
+			}
 
-	if (row == -1)
-		return;
+			if (actiontext)
+			{
+				kb->action = key_get_action_from_string (actiontext);
+				g_free (actiontext);
+			}
 
-	kb = gtk_clist_get_row_data (GTK_CLIST (key_dialog_kb_clist), row);
-	if (state)
-		kb->mod |= kstate;
-	else
-		kb->mod &= ~kstate;
+			if (!accel || !actiontext)
+				key_free (kb);
+			else
+				keybind_list = g_slist_append (keybind_list, kb);
+
+		} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
+	}
 
-	gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 0,
-							  key_make_mod_str (kb->mod, buf));
+	if (key_save_kbs () == 0)
+		key_dialog_close (wid, NULL);
 }
 
-static GtkWidget *
-key_dialog_make_toggle (char *label, void *callback, void *option,
-								GtkWidget * box)
+static void
+key_dialog_add (GtkWidget *wid, gpointer userdata)
 {
-	GtkWidget *wid;
+	GtkTreeView *view = g_object_get_data (G_OBJECT (key_dialog), "view");
+	GtkTreeViewColumn *col;
+	GtkListStore *store = GTK_LIST_STORE (get_store ());
+	GtkTreeIter iter;
+	GtkTreePath *path;
+
+	gtk_list_store_append (store, &iter);
+
+	/* make sure the new row is visible and selected */
+	path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
+	col = gtk_tree_view_get_column (view, ACTION_COLUMN);
+	gtk_tree_view_scroll_to_cell (view, path, NULL, FALSE, 0.0, 0.0);
+	gtk_tree_view_set_cursor (view, path, col, TRUE);
+	gtk_tree_path_free (path);
+}
 
-	wid = gtk_check_button_new_with_label (label);
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), 0);
-	g_signal_connect (G_OBJECT (wid), "toggled",
-							  G_CALLBACK (callback), option);
-	gtk_box_pack_end (GTK_BOX (box), wid, 0, 0, 0);
-	gtk_widget_show (wid);
+static void
+key_dialog_delete (GtkWidget *wid, gpointer userdata)
+{
+	GtkTreeView *view = g_object_get_data (G_OBJECT (key_dialog), "view");
+	GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
+	GtkTreeIter iter;
+	GtkTreePath *path;
 
-	return wid;
+	if (gtkutil_treeview_get_selected (view, &iter, -1))
+	{
+		/* delete this row, select next one */
+		if (gtk_list_store_remove (store, &iter))
+		{
+			path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
+			gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 1.0, 0.0);
+			gtk_tree_view_set_cursor (view, path, NULL, FALSE);
+			gtk_tree_path_free (path);
+		}
+	}
 }
 
 static GtkWidget *
-key_dialog_make_entry (char *label, char *act, void *callback, void *option,
-							  GtkWidget * box)
+key_dialog_treeview_new (GtkWidget *box)
 {
-	GtkWidget *wid, *hbox;;
+	GtkWidget *scroll;
+	GtkListStore *store, *combostore;
+	GtkTreeViewColumn *col;
+	GtkWidget *view;
+	GtkCellRenderer *render;
+	int i;
 
-	hbox = gtk_hbox_new (0, 2);
-	if (label)
-	{
-		wid = gtk_label_new (label);
-		gtk_widget_show (wid);
-		gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
-	}
-	wid = gtk_entry_new ();
-	if (act)
-	{
-		g_signal_connect (G_OBJECT (wid), act, G_CALLBACK (callback),
-								  option);
-	}
-	gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
-	gtk_widget_show (wid);
-	gtk_widget_show (hbox);
+	scroll = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN);
 
-	gtk_box_pack_start (GTK_BOX (box), hbox, 0, 0, 0);
+	store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
+								G_TYPE_STRING, G_TYPE_STRING);
+	g_return_val_if_fail (store != NULL, NULL);
 
-	return wid;
-}
+	view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+	gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE);
+	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (view), FALSE);
 
-static void
-key_dialog_set_key (GtkWidget * entry, GdkEventKey * evt, void *none)
-{
-	int row = gtkutil_clist_selection (key_dialog_kb_clist);
-	struct key_binding *kb;
+	g_signal_connect (G_OBJECT (view), "key-press-event",
+					G_CALLBACK (key_dialog_keypress), NULL);
+	g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW(view))),
+					"changed", G_CALLBACK (key_dialog_selection_changed), NULL);
 
-	gtk_entry_set_text (GTK_ENTRY (entry), "");
+	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
 
-	if (row == -1)
-		return;
+	render = gtk_cell_renderer_accel_new ();
+	g_object_set (render, "editable", TRUE,
+#ifndef WIN32
+					"accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
+#endif
+					NULL);
+	g_signal_connect (G_OBJECT (render), "accel-edited",
+					G_CALLBACK (key_dialog_set_key), NULL);
+	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), KEY_COLUMN,
+												"Key", render,
+												"text", KEY_COLUMN,
+												NULL);
+
+	combostore = gtk_list_store_new (1, G_TYPE_STRING);
+	for (i = 0; i <= KEY_MAX_ACTIONS; i++)
+	{
+		GtkTreeIter iter;
+
+		if (key_actions[i].name[0])
+		{
+			gtk_list_store_append (combostore, &iter);
+			gtk_list_store_set (combostore, &iter, 0, key_actions[i].name, -1);
+		}
+	}
 
-	kb = gtk_clist_get_row_data (GTK_CLIST (key_dialog_kb_clist), row);
-	kb->keyval = evt->keyval;
-	kb->keyname = key_get_key_name (kb->keyval);
-	gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 1, kb->keyname);
-	gtk_entry_set_text (GTK_ENTRY (entry), kb->keyname);
-	g_signal_stop_emission_by_name (G_OBJECT (entry), "key_press_event");
+	render = gtk_cell_renderer_combo_new ();
+	g_object_set (G_OBJECT (render), "model", combostore,
+									"has-entry", FALSE,
+									"editable", TRUE, 
+									"text-column", 0,
+									NULL);
+	g_signal_connect (G_OBJECT (render), "edited",
+					G_CALLBACK (key_dialog_entry_edited), GINT_TO_POINTER (ACTION_COLUMN));
+	g_signal_connect (G_OBJECT (render), "changed",
+					G_CALLBACK (key_dialog_combo_changed), combostore);
+	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), ACTION_COLUMN,
+													"Action", render,
+													"text", ACTION_COLUMN, 
+													NULL);
+
+	render = gtk_cell_renderer_text_new ();
+	g_object_set (render, "editable", TRUE, NULL);
+	g_signal_connect (G_OBJECT (render), "edited",
+				G_CALLBACK (key_dialog_entry_edited), GINT_TO_POINTER (D1_COLUMN));
+	gtk_tree_view_insert_column_with_attributes (
+							GTK_TREE_VIEW (view), D1_COLUMN,
+							"Data1", render,
+							"text", D1_COLUMN,
+							NULL);
+
+	render = gtk_cell_renderer_text_new ();
+	g_object_set (render, "editable", TRUE, NULL);
+	g_signal_connect (G_OBJECT (render), "edited",
+				G_CALLBACK (key_dialog_entry_edited), GINT_TO_POINTER (D2_COLUMN));
+	gtk_tree_view_insert_column_with_attributes (
+							GTK_TREE_VIEW (view), D2_COLUMN,
+							"Data2", render,
+							"text", D2_COLUMN,
+							NULL);
+
+	col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), KEY_COLUMN);
+	gtk_tree_view_column_set_fixed_width (col, 200);
+	gtk_tree_view_column_set_resizable (col, TRUE);
+	col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), ACTION_COLUMN);
+	gtk_tree_view_column_set_fixed_width (col, 160);
+	col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), D1_COLUMN);
+	gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+	gtk_tree_view_column_set_min_width (col, 80);
+	gtk_tree_view_column_set_resizable (col, TRUE);
+	col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), D2_COLUMN);
+	gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+	gtk_tree_view_column_set_min_width (col, 80);
+	gtk_tree_view_column_set_resizable (col, TRUE);
+
+	gtk_container_add (GTK_CONTAINER (scroll), view);
+	gtk_container_add (GTK_CONTAINER (box), scroll);
+
+	return view;
 }
 
 static void
-key_dialog_set_data (GtkWidget * entry, int d)
+key_dialog_load (GtkListStore *store)
 {
-	const char *text = gtk_entry_get_text (GTK_ENTRY (entry));
-	int row = gtkutil_clist_selection (key_dialog_kb_clist);
-	struct key_binding *kb;
-	char *buf;
-	int len = strlen (text);
+	struct key_binding *kb = NULL;
+	char *accel_text;
+	GtkTreeIter iter;
+	GSList *list = keybind_list;
 
-	len++;
+	while (list)
+	{
+		kb = (struct key_binding*)list->data;
 
-	if (row == -1)
-		return;
+		accel_text = gtk_accelerator_name (kb->keyval, kb->mod);
 
-	kb = gtk_clist_get_row_data (GTK_CLIST (key_dialog_kb_clist), row);
-	if (d == 0)
-	{									  /* using data1 */
-		if (kb->data1)
-			free (kb->data1);
-		buf = (char *) malloc (len);
-		memcpy (buf, text, len);
-		kb->data1 = buf;
-		gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 3, text);
-	} else
-	{
-		if (kb->data2)
-			free (kb->data2);
-		buf = (char *) malloc (len);
-		memcpy (buf, text, len);
-		kb->data2 = buf;
-		gtk_clist_set_text (GTK_CLIST (key_dialog_kb_clist), row, 4, text);
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter,
+							KEY_COLUMN, accel_text,
+							ACTION_COLUMN, key_actions[kb->action].name,
+							D1_COLUMN, kb->data1,
+							D2_COLUMN, kb->data2, -1);
+
+		g_free (accel_text);
+
+		list = g_slist_next (list);
 	}
 }
 
 void
 key_dialog_show ()
 {
-	GtkWidget *vbox, *hbox, *list, *vbox2, *wid, *wid2, *wid3;
-	struct key_binding *kb;
-	gchar *titles[] = { NULL, NULL, NULL, "1", "2" };
-	char temp[32];
-	int i;
-
-	titles[0] = _("Mod");
-	titles[1] = _("Key");
-	titles[2] = _("Action");
+	GtkWidget *vbox, *box;
+	GtkWidget *view, *xtext;
+	GtkListStore *store;
 
 	if (key_dialog)
 	{
@@ -677,164 +778,62 @@ key_dialog_show ()
 		return;
 	}
 
-	key_dialog =
-			  mg_create_generic_tab ("editkeys", _(DISPLAY_NAME": Keyboard Shortcuts"),
-							TRUE, FALSE, key_dialog_close, NULL, 560, 330, &vbox, 0);
-
-	hbox = gtk_hbox_new (0, 2);
-	gtk_box_pack_start (GTK_BOX (vbox), hbox, 1, 1, 0);
-
-	list = gtkutil_clist_new (5, titles, hbox, 0, key_dialog_sel_row, 0, NULL,
-									  0, GTK_SELECTION_SINGLE);
-	gtk_widget_set_usize (list, 400, 0);
-	key_dialog_kb_clist = list;
+	key_dialog = mg_create_generic_tab ("editkeys", _(DISPLAY_NAME": Keyboard Shortcuts"),
+									TRUE, FALSE, key_dialog_close, NULL, 600, 360, &vbox, 0);
 
-	gtk_widget_show (hbox);
+	view = key_dialog_treeview_new (vbox);
+	xtext = gtk_xtext_new (colors, 0);
+	gtk_box_pack_start (GTK_BOX (vbox), xtext, FALSE, TRUE, 2);
+	gtk_xtext_set_font (GTK_XTEXT (xtext), prefs.hex_text_font);
 
-	kb = keys_root;
+	g_object_set_data (G_OBJECT (key_dialog), "view", view);
+	g_object_set_data (G_OBJECT (key_dialog), "xtext", xtext);
 
-	gtk_clist_set_column_width (GTK_CLIST (list), 1, 50);
-	gtk_clist_set_column_width (GTK_CLIST (list), 2, 120);
-	gtk_clist_set_column_width (GTK_CLIST (list), 3, 50);
-	gtk_clist_set_column_width (GTK_CLIST (list), 4, 50);
-
-	while (kb)
-	{
-		titles[0] = key_make_mod_str (kb->mod, temp);
-		titles[1] = kb->keyname;
-		if (kb->action < 0 || kb->action > KEY_MAX_ACTIONS)
-			titles[2] = _("<none>");
-		else
-			titles[2] = key_actions[kb->action].name;
-		if (kb->data1)
-			titles[3] = kb->data1;
-		else
-			titles[3] = _("<none>");
+	box = gtk_hbutton_box_new ();
+	gtk_button_box_set_layout (GTK_BUTTON_BOX (box), GTK_BUTTONBOX_SPREAD);
+	gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 2);
+	gtk_container_set_border_width (GTK_CONTAINER (box), 5);
 
-		if (kb->data2)
-			titles[4] = kb->data2;
-		else
-			titles[4] = _("<none>");
-
-		gtk_clist_set_row_data (GTK_CLIST (list),
-										gtk_clist_append (GTK_CLIST (list), titles),
-										kb);
-
-		kb = kb->next;
-	}
-
-	vbox2 = gtk_vbox_new (0, 2);
-	gtk_box_pack_end (GTK_BOX (hbox), vbox2, 1, 1, 0);
-	wid = gtk_button_new_with_label (_("Add New"));
-	gtk_box_pack_start (GTK_BOX (vbox2), wid, 0, 0, 0);
-	g_signal_connect (G_OBJECT (wid), "clicked",
-							  G_CALLBACK (key_dialog_add_new), list);
-	gtk_widget_show (wid);
-	wid = gtk_button_new_with_label (_("Delete"));
-	gtk_box_pack_start (GTK_BOX (vbox2), wid, 0, 0, 0);
-	g_signal_connect (G_OBJECT (wid), "clicked",
-							  G_CALLBACK (key_dialog_delete), list);
-	gtk_widget_show (wid);
-	gtk_widget_show (vbox2);
-
-	wid = gtk_option_menu_new ();
-	wid2 = gtk_menu_new ();
-
-	for (i = 0; i <= KEY_MAX_ACTIONS; i++)
-	{
-		wid3 = gtk_menu_item_new_with_label (_(key_actions[i].name));
-		gtk_widget_show (wid3);
-		gtk_menu_shell_append (GTK_MENU_SHELL (wid2), wid3);
-		g_signal_connect (G_OBJECT (wid3), "activate",
-								  G_CALLBACK (key_dialog_sel_act),
-								  GINT_TO_POINTER (i));
-	}
+	gtkutil_button (box, GTK_STOCK_NEW, NULL, key_dialog_add,
+					NULL, _("Add"));
+	gtkutil_button (box, GTK_STOCK_DELETE, NULL, key_dialog_delete,
+					NULL, _("Delete"));
+	gtkutil_button (box, GTK_STOCK_CANCEL, NULL, key_dialog_close,
+					NULL, _("Cancel"));
+	gtkutil_button (box, GTK_STOCK_SAVE, NULL, key_dialog_save,
+					NULL, _("Save"));
 
-	gtk_option_menu_set_menu (GTK_OPTION_MENU (wid), wid2);
-	gtk_option_menu_set_history (GTK_OPTION_MENU (wid), 0);
-	gtk_box_pack_end (GTK_BOX (vbox2), wid, 0, 0, 0);
-	gtk_widget_show (wid);
-	key_dialog_act_menu = wid;
-
-	key_dialog_tog_s = key_dialog_make_toggle (_("Shift"), key_dialog_tog_key,
-															 (void *) STATE_SHIFT, vbox2);
-	key_dialog_tog_a = key_dialog_make_toggle (_("Alt"), key_dialog_tog_key,
-															 (void *) STATE_ALT, vbox2);
-	key_dialog_tog_c = key_dialog_make_toggle (_("Ctrl"), key_dialog_tog_key,
-															 (void *) STATE_CTRL, vbox2);
-
-	key_dialog_ent_key = key_dialog_make_entry (_("Key"), "key_press_event",
-															  key_dialog_set_key, NULL,
-															  vbox2);
-
-	key_dialog_ent_d1 = key_dialog_make_entry (_("Data 1"), "activate",
-															 key_dialog_set_data, NULL,
-															 vbox2);
-	key_dialog_ent_d2 = key_dialog_make_entry (_("Data 2"), "activate",
-															 key_dialog_set_data,
-															 (void *) 1, vbox2);
-
-	wid2 = gtk_scrolled_window_new (NULL, NULL);
-	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (wid2), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
-	gtk_container_add (GTK_CONTAINER (vbox), wid2);
-
-	key_dialog_text = gtk_xtext_new (colors, 0);
-	gtk_container_add (GTK_CONTAINER (wid2), key_dialog_text);
-	gtk_xtext_set_font (GTK_XTEXT (key_dialog_text), prefs.hex_text_font);
+	store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
+	key_dialog_load (store);
 
 	gtk_widget_show_all (key_dialog);
 }
 
-static void
+static int
 key_save_kbs (void)
 {
-	int fd, i;
+	int fd;
 	char buf[512];
+	char *accel_text;
+	GSList *list = keybind_list;
 	struct key_binding *kb;
 
 	fd = hexchat_open_file ("keybindings.conf", O_CREAT | O_TRUNC | O_WRONLY,
 									 0x180, XOF_DOMODE);
 	if (fd < 0)
+		return 1;
+	write (fd, buf, snprintf (buf, 510, "# HexChat key bindings config file\n\n"));
+
+	while (list)
 	{
-		fe_message (_("Error opening keys config file\n"), FE_MSG_ERROR);
-		return;
-	}
-	write (fd, buf,
-			 snprintf (buf, 510, "# HexChat key bindings config file\n\n"));
+		kb = list->data;
 
-	kb = keys_root;
-	i = 0;
+		accel_text = gtk_accelerator_name (kb->keyval, kb->mod);
 
-	while (kb)
-	{
-		if (kb->keyval == -1 || kb->keyname == NULL || kb->action < 0)
-		{
-			kb = kb->next;
-			continue;
-		}
-		i = 0;
-		if (kb->mod & STATE_CTRL)
-		{
-			i++;
-			write (fd, "C", 1);
-		}
-		if (kb->mod & STATE_ALT)
-		{
-			i++;
-			write (fd, "A", 1);
-		}
-		if (kb->mod & STATE_SHIFT)
-		{
-			i++;
-			write (fd, "S", 1);
-		}
-		if (i == 0)
-			write (fd, "None\n", 5);
-		else
-			write (fd, "\n", 1);
+		snprintf (buf, 510, "ACCEL=%s\n%s\n", accel_text, key_actions[kb->action].name);
+		write (fd, buf, strlen (buf));
+		g_free (accel_text);
 
-		write (fd, buf, snprintf (buf, 510, "%s\n%s\n", kb->keyname,
-										  key_actions[kb->action].name));
 		if (kb->data1 && kb->data1[0])
 			write (fd, buf, snprintf (buf, 510, "D1:%s\n", kb->data1));
 		else
@@ -847,47 +846,54 @@ key_save_kbs (void)
 
 		write (fd, "\n", 1);
 
-		kb = kb->next;
+		list = g_slist_next (list);
 	}
 
 	close (fd);
+	return 0;
 }
 
-/* I just know this is going to be a nasty parse, if you think it's bugged
-   it almost certainly is so contact the HexChat dev team --AGL */
+#define KBSTATE_MOD 0
+#define KBSTATE_KEY 1
+#define KBSTATE_ACT 2
+#define KBSTATE_DT1 3
+#define KBSTATE_DT2 4
+
+#define STRIP_WHITESPACE \
+	while (buf[0] == ' ' || buf[0] == '\t') \
+		buf++; \
+		len = strlen (buf); \
+	while (buf[len] == ' ' || buf[len] == '\t') \
+	{ \
+		buf[len] = 0; \
+		len--; \
+	} \
 
 static inline int
-key_load_kbs_helper_mod (char *in, int *out)
+key_load_kbs_helper_mod (char *buf, GdkModifierType *out)
 {
 	int n, len, mod = 0;
 
 	/* First strip off the fluff */
-	while (in[0] == ' ' || in[0] == '\t')
-		in++;
-	len = strlen (in);
-	while (in[len] == ' ' || in[len] == '\t')
-	{
-		in[len] = 0;
-		len--;
-	}
+	STRIP_WHITESPACE
 
-	if (strcmp (in, "None") == 0)
+	if (strcmp (buf, "None") == 0)
 	{
 		*out = 0;
 		return 0;
 	}
 	for (n = 0; n < len; n++)
 	{
-		switch (in[n])
+		switch (buf[n])
 		{
 		case 'C':
-			mod |= STATE_CTRL;
+			mod |= GDK_CONTROL_MASK;
 			break;
 		case 'A':
-			mod |= STATE_ALT;
+			mod |= GDK_MOD1_MASK;
 			break;
 		case 'S':
-			mod |= STATE_SHIFT;
+			mod |= GDK_SHIFT_MASK;
 			break;
 		default:
 			return 1;
@@ -898,23 +904,15 @@ key_load_kbs_helper_mod (char *in, int *out)
 	return 0;
 }
 
-/* These are just local defines to keep me sane --AGL */
-
-#define KBSTATE_MOD 0
-#define KBSTATE_KEY 1
-#define KBSTATE_ACT 2
-#define KBSTATE_DT1 3
-#define KBSTATE_DT2 4
-
-/* *** Warning, Warning! - massive function ahead! --AGL */
-
 static int
 key_load_kbs (void)
 {
 	char *buf, *ibuf;
 	struct stat st;
-	struct key_binding *kb = NULL, *last = NULL;
-	int fd, len, pnt = 0, state = 0, n;
+	struct key_binding *kb = NULL;
+	int fd, len, state = 0, pnt = 0;
+	guint keyval;
+	GdkModifierType mod = 0;
 	off_t size;
 
 	fd = hexchat_open_file ("keybindings.conf", O_RDONLY, 0, 0);
@@ -937,8 +935,14 @@ key_load_kbs (void)
 		close (fd);
 	}
 
-	while (buf_get_line (ibuf, &buf, &pnt, size))
+	if (keybind_list)
 	{
+		g_slist_free_full (keybind_list, key_free);
+		keybind_list = NULL;
+	}
+
+	while (buf_get_line (ibuf, &buf, &pnt, size))
+	{		
 		if (buf[0] == '#')
 			continue;
 		if (strlen (buf) == 0)
@@ -947,77 +951,60 @@ key_load_kbs (void)
 		switch (state)
 		{
 		case KBSTATE_MOD:
-			kb = (struct key_binding *) malloc (sizeof (struct key_binding));
-			if (key_load_kbs_helper_mod (buf, &kb->mod))
+			kb = (struct key_binding *) g_malloc0 (sizeof (struct key_binding));
+
+			/* New format */
+			if (strncmp (buf, "ACCEL=", 6) == 0)
+			{
+				buf += 6;
+
+				gtk_accelerator_parse (buf, &keyval, &mod);
+
+
+				kb->keyval = keyval;
+				kb->mod = key_modifier_get_valid (mod);
+
+				state = KBSTATE_ACT; 
+				continue;
+			}
+
+			if (key_load_kbs_helper_mod (buf, &mod))
 				goto corrupt_file;
+
+			kb->mod = mod;
+
 			state = KBSTATE_KEY;
 			continue;
+
 		case KBSTATE_KEY:
-			/* First strip off the fluff */
-			while (buf[0] == ' ' || buf[0] == '\t')
-				buf++;
-			len = strlen (buf);
-			while (buf[len] == ' ' || buf[len] == '\t')
-			{
-				buf[len] = 0;
-				len--;
-			}
+			STRIP_WHITESPACE
 
-			n = gdk_keyval_from_name (buf);
-			if (n == 0)
+			keyval = gdk_keyval_from_name (buf);
+			if (keyval == 0)
 			{
-				/* Unknown keyname, abort */
-				if (last)
-					last->next = NULL;
-				free (ibuf);
-				ibuf = malloc (1024);
-				snprintf (ibuf, 1024,
-							 _("Unknown keyname %s in key bindings config file\nLoad aborted, please fix %s" G_DIR_SEPARATOR_S "keybindings.conf\n"),
-							 buf, get_xdir ());
-				fe_message (ibuf, FE_MSG_ERROR);
 				free (ibuf);
 				return 2;
 			}
-			kb->keyname = gdk_keyval_name (n);
-			kb->keyval = n;
+
+			kb->keyval = keyval;
 
 			state = KBSTATE_ACT;
 			continue;
+
 		case KBSTATE_ACT:
-			/* First strip off the fluff */
-			while (buf[0] == ' ' || buf[0] == '\t')
-				buf++;
-			len = strlen (buf);
-			while (buf[len] == ' ' || buf[len] == '\t')
-			{
-				buf[len] = 0;
-				len--;
-			}
+			STRIP_WHITESPACE
 
-			for (n = 0; n < KEY_MAX_ACTIONS + 1; n++)
-			{
-				if (strcmp (key_actions[n].name, buf) == 0)
-				{
-					kb->action = n;
-					break;
-				}
-			}
+			kb->action = key_get_action_from_string (buf);
 
-			if (n == KEY_MAX_ACTIONS + 1)
+			if (kb->action == KEY_MAX_ACTIONS + 1)
 			{
-				if (last)
-					last->next = NULL;
-				free (ibuf);
-				ibuf = malloc (1024);
-				snprintf (ibuf, 1024,
-							 _("Unknown action %s in key bindings config file\nLoad aborted, Please fix %s" G_DIR_SEPARATOR_S "keybindings\n"),
-							 buf, get_xdir ());
-				fe_message (ibuf, FE_MSG_ERROR);
 				free (ibuf);
 				return 3;
 			}
+
 			state = KBSTATE_DT1;
 			continue;
+
 		case KBSTATE_DT1:
 		case KBSTATE_DT2:
 			if (state == KBSTATE_DT1)
@@ -1029,14 +1016,9 @@ key_load_kbs (void)
 			if (buf[0] != 'D')
 			{
 				free (ibuf);
-				ibuf = malloc (1024);
-				snprintf (ibuf, 1024,
-							 _("Expecting Data line (beginning Dx{:|!}) but got:\n%s\n\nLoad aborted, Please fix %s" G_DIR_SEPARATOR_S "keybindings\n"),
-							 buf, get_xdir ());
-				fe_message (ibuf, FE_MSG_ERROR);
-				free (ibuf);
 				return 4;
 			}
+
 			switch (buf[1])
 			{
 			case '1':
@@ -1059,11 +1041,11 @@ key_load_kbs (void)
 				len -= 3;
 				if (state == KBSTATE_DT1)
 				{
-					kb->data1 = malloc (len);
+					kb->data1 = g_malloc (len);
 					memcpy (kb->data1, &buf[3], len);
 				} else
 				{
-					kb->data2 = malloc (len);
+					kb->data2 = g_malloc (len);
 					memcpy (kb->data2, &buf[3], len);
 				}
 			} else if (buf[2] == '!')
@@ -1079,11 +1061,7 @@ key_load_kbs (void)
 				continue;
 			} else
 			{
-				if (last)
-					last->next = kb;
-				else
-					keys_root = kb;
-				last = kb;
+				keybind_list = g_slist_append (keybind_list, kb);
 
 				state = KBSTATE_MOD;
 			}
@@ -1091,19 +1069,10 @@ key_load_kbs (void)
 			continue;
 		}
 	}
-	if (last)
-		last->next = NULL;
 	free (ibuf);
 	return 0;
 
- corrupt_file:
-	/*if (getenv ("HEXCHAT_DEBUG"))
-		abort ();*/
-	snprintf (ibuf, 1024,
-						_("Key bindings config file is corrupt, load aborted\n"
-								 "Please fix %s" G_DIR_SEPARATOR_S "keybindings.conf\n"),
-						 get_xdir ());
-	fe_message (ibuf, FE_MSG_ERROR);
+corrupt_file:
 	free (ibuf);
 	free (kb);
 	return 5;
diff --git a/src/fe-gtk/gtkutil.c b/src/fe-gtk/gtkutil.c
index a6870253..3bb247a2 100644
--- a/src/fe-gtk/gtkutil.c
+++ b/src/fe-gtk/gtkutil.c
@@ -530,76 +530,6 @@ gtkutil_entry_new (int max, GtkWidget * box, void *callback,
 	return entry;
 }
 
-GtkWidget *
-gtkutil_clist_new (int columns, char *titles[],
-						 GtkWidget * box, int policy,
-						 void *select_callback, gpointer select_userdata,
-						 void *unselect_callback,
-						 gpointer unselect_userdata, int selection_mode)
-{
-	GtkWidget *clist, *win;
-
-	win = gtk_scrolled_window_new (0, 0);
-	gtk_container_add (GTK_CONTAINER (box), win);
-	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (win),
-											  GTK_POLICY_AUTOMATIC, policy);
-	gtk_widget_show (win);
-
-	if (titles)
-		clist = gtk_clist_new_with_titles (columns, titles);
-	else
-		clist = gtk_clist_new (columns);
-
-	gtk_clist_set_selection_mode (GTK_CLIST (clist), selection_mode);
-	gtk_clist_column_titles_passive (GTK_CLIST (clist));
-	gtk_container_add (GTK_CONTAINER (win), clist);
-	if (select_callback)
-	{
-		g_signal_connect (G_OBJECT (clist), "select_row",
-								G_CALLBACK (select_callback), select_userdata);
-	}
-	if (unselect_callback)
-	{
-		g_signal_connect (G_OBJECT (clist), "unselect_row",
-								G_CALLBACK (unselect_callback), unselect_userdata);
-	}
-	gtk_widget_show (clist);
-
-	return clist;
-}
-
-int
-gtkutil_clist_selection (GtkWidget * clist)
-{
-	if (GTK_CLIST (clist)->selection)
-		return GPOINTER_TO_INT(GTK_CLIST (clist)->selection->data);
-	return -1;
-}
-
-static int
-int_compare (const int * elem1, const int * elem2)
-{
-	return (*elem1) - (*elem2);
-}
-
-int
-gtkutil_clist_multiple_selection (GtkWidget * clist, int ** rows, const int max_rows)
-{
-	int i = 0;
-	GList *tmp_clist;
-	*rows = malloc (sizeof (int) * max_rows );
-	memset( *rows, -1, max_rows * sizeof(int) );
-
-	for( tmp_clist = GTK_CLIST(clist)->selection;
-			tmp_clist && i < max_rows; tmp_clist = tmp_clist->next, i++)
-	{
-		(*rows)[i] = GPOINTER_TO_INT( tmp_clist->data );
-	}
-	qsort(*rows, i, sizeof(int), (void *)int_compare);
-	return i;
-
-}
-
 void
 show_and_unfocus (GtkWidget * wid)
 {