From 725aed43ff7282e834a0deeb0f1136dbb909abfd Mon Sep 17 00:00:00 2001 From: TingPing Date: Thu, 10 Oct 2013 14:49:37 -0400 Subject: Redesign keyboard shortcuts window - Convert GtkCLists to GtkTreeViews - Save keybindings in GtkAccelerator format - Store keybindings in a GSList --- src/fe-gtk/fkeys.c | 1009 ++++++++++++++++++++++++-------------------------- src/fe-gtk/gtkutil.c | 70 ---- 2 files changed, 489 insertions(+), 590 deletions(-) (limited to 'src') 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] = _(""); - strs[2] = _(""); - strs[3] = _(""); - strs[4] = _(""); + 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] = _(""); - else - titles[2] = key_actions[kb->action].name; - if (kb->data1) - titles[3] = kb->data1; - else - titles[3] = _(""); + 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] = _(""); - - 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) @@ -1028,15 +1015,10 @@ 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) { -- cgit 1.4.1