* GNT - The GLib Ncurses Toolkit * GNT is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * This library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA #define GNT_LOG_DOMAIN "Bindable" static GObjectClass *parent_class = NULL; char * okeys; /* Old keystrokes */ char * keys; /* New Keystrokes being bound to the action */ GntBindableClass * klass; /* Class of the object that's getting keys rebound */ char * name; /* The name of the action */ GList * params; /* The list of paramaters */ gnt_bindable_free_rebind_info(void) g_free(rebind_info.name); g_free(rebind_info.keys); g_free(rebind_info.okeys); gnt_bindable_rebinding_cancel(GntWidget *button, gpointer data) gnt_bindable_free_rebind_info(); gnt_widget_destroy(GNT_WIDGET(data)); gnt_bindable_rebinding_rebind(GntWidget *button, gpointer data) gnt_bindable_register_binding(rebind_info.klass, gnt_bindable_register_binding(rebind_info.klass, gnt_bindable_free_rebind_info(); gnt_widget_destroy(GNT_WIDGET(data)); gnt_bindable_rebinding_grab_key(GntBindable *bindable, const char *text, gpointer data) GntTextView *textview = GNT_TEXT_VIEW(data); /* Rebinding tab or enter for something is probably not that great an idea */ if (!strcmp(text, GNT_KEY_CTRL_I) || !strcmp(text, GNT_KEY_ENTER)) { tmp = gnt_key_lookup(text); new_text = g_strdup_printf("KEY: \"%s\"", tmp); gnt_text_view_clear(textview); gnt_text_view_append_text_with_flags(textview, new_text, GNT_TEXT_FLAG_NORMAL); g_free(rebind_info.keys); rebind_info.keys = g_strdup(text); gnt_bindable_rebinding_activate(GntBindable *data, gpointer bindable) const char *widget_name = g_type_name(G_OBJECT_TYPE(bindable)); GntWidget *bind_button, *cancel_button; GntWidget *win = gnt_window_new(); GntTree *tree = GNT_TREE(data); GntWidget *vbox = gnt_box_new(FALSE, TRUE); rebind_info.klass = GNT_BINDABLE_GET_CLASS(bindable); current_row_data = gnt_tree_get_selection_text_list(tree); rebind_info.name = g_strdup(g_list_nth_data(current_row_data, 1)); keys = gnt_tree_get_selection_data(tree); rebind_info.okeys = g_strdup(gnt_key_translate(keys)); rebind_info.params = NULL; g_list_foreach(current_row_data, (GFunc)g_free, NULL); g_list_free(current_row_data); gnt_box_set_alignment(GNT_BOX(vbox), GNT_ALIGN_MID); gnt_box_set_title(GNT_BOX(win), "Key Capture"); tmp = g_strdup_printf("Type the new bindings for %s in a %s.", rebind_info.name, widget_name); label = gnt_label_new(tmp); gnt_box_add_widget(GNT_BOX(vbox), label); tmp = g_strdup_printf("KEY: \"%s\"", keys); key_textview = gnt_text_view_new(); gnt_widget_set_size(key_textview, key_textview->priv.x, 2); gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(key_textview), tmp, GNT_TEXT_FLAG_NORMAL); gnt_widget_set_name(key_textview, "keystroke"); gnt_box_add_widget(GNT_BOX(vbox), key_textview); g_signal_connect(G_OBJECT(win), "key_pressed", G_CALLBACK(gnt_bindable_rebinding_grab_key), key_textview); button_box = gnt_box_new(FALSE, FALSE); bind_button = gnt_button_new("BIND"); gnt_widget_set_name(bind_button, "bind"); gnt_box_add_widget(GNT_BOX(button_box), bind_button); cancel_button = gnt_button_new("Cancel"); gnt_widget_set_name(cancel_button, "cancel"); gnt_box_add_widget(GNT_BOX(button_box), cancel_button); g_signal_connect(G_OBJECT(bind_button), "activate", G_CALLBACK(gnt_bindable_rebinding_rebind), win); g_signal_connect(G_OBJECT(cancel_button), "activate", G_CALLBACK(gnt_bindable_rebinding_cancel), win); gnt_box_add_widget(GNT_BOX(vbox), button_box); gnt_box_add_widget(GNT_BOX(win), vbox); add_binding(gpointer key, gpointer value, gpointer data) GntBindableActionParam *act = value; const char *name = g_hash_table_lookup(bv->hash, act->action); const char *k = gnt_key_lookup(key); gnt_tree_add_row_after(bv->tree, (gpointer)k, gnt_tree_create_row(bv->tree, k, name), NULL, NULL); add_action(gpointer key, gpointer value, gpointer data) g_hash_table_insert(bv->hash, value, key); gnt_bindable_class_init(GntBindableClass *klass) parent_class = g_type_class_peek_parent(klass); klass->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnt_bindable_action_free); klass->bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnt_bindable_action_param_free); gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass)); bindable_clone(GntBindableAction *action) GntBindableAction *ret = g_new0(GntBindableAction, 1); ret->name = g_strdup(action->name); binding_clone(GntBindableActionParam *param) GntBindableActionParam *p = g_new0(GntBindableActionParam, 1); p->list = g_list_copy(param->list); p->action = param->action; duplicate_hashes(GntBindableClass *klass) /* Duplicate the bindings from parent class */ klass->actions = g_hash_table_duplicate(klass->actions, g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnt_bindable_action_free, (GDupFunc)g_strdup, (GDupFunc)bindable_clone); klass->bindings = g_hash_table_duplicate(klass->bindings, g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnt_bindable_action_param_free, (GDupFunc)g_strdup, (GDupFunc)binding_clone); klass->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnt_bindable_action_free); klass->bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnt_bindable_action_param_free); /****************************************************************************** *****************************************************************************/ gnt_bindable_get_gtype(void) static const GTypeInfo info = { sizeof(GntBindableClass), (GBaseInitFunc)duplicate_hashes, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc)gnt_bindable_class_init, NULL, /* instance_init */ type = g_type_register_static(G_TYPE_OBJECT, &info, G_TYPE_FLAG_ABSTRACT); gnt_bindable_remap_keys(GntBindable *bindable, const char *text) const char *remap = NULL; GType type = G_OBJECT_TYPE(bindable); GntBindableClass *klass = GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bindable)); if (klass->remaps == NULL) klass->remaps = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); gnt_styles_get_keyremaps(type, klass->remaps); remap = g_hash_table_lookup(klass->remaps, text); return (remap ? remap : text); gnt_bindable_perform_action_named(GntBindable *bindable, const char *name, ...) GntBindableClass *klass = GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bindable)); GntBindableAction *action; while ((p = va_arg(args, void *)) != NULL) list = g_list_append(list, p); action = g_hash_table_lookup(klass->actions, name); if (action && action->u.action) { return action->u.action(bindable, list); gnt_bindable_perform_action_key(GntBindable *bindable, const char *keys) GntBindableClass *klass = GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bindable)); GntBindableActionParam *param = g_hash_table_lookup(klass->bindings, keys); if (param && param->action) { return param->action->u.action(bindable, param->list); return param->action->u.action_noparam(bindable); gnt_bindable_check_key(GntBindable *bindable, const char *keys) { GntBindableClass *klass = GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bindable)); GntBindableActionParam *param = g_hash_table_lookup(klass->bindings, keys); return (param && param->action); register_binding(GntBindableClass *klass, const char *name, const char *trigger, GList *list) GntBindableActionParam *param; GntBindableAction *action; if (name == NULL || *name == '\0') { g_hash_table_remove(klass->bindings, (char*)trigger); gnt_keys_del_combination(trigger); action = g_hash_table_lookup(klass->actions, name); gnt_warning("Invalid action name %s for %s", name, g_type_name(G_OBJECT_CLASS_TYPE(klass))); param = g_new0(GntBindableActionParam, 1); g_hash_table_replace(klass->bindings, g_strdup(trigger), param); gnt_keys_add_combination(trigger); void gnt_bindable_register_binding(GntBindableClass *klass, const char *name, const char *trigger, ...) while ((data = va_arg(args, void *))) { list = g_list_append(list, data); register_binding(klass, name, trigger, list); void gnt_bindable_class_register_action(GntBindableClass *klass, const char *name, GntBindableActionCallback callback, const char *trigger, ...) GntBindableAction *action = g_new0(GntBindableAction, 1); action->name = g_strdup(name); action->u.action = callback; g_hash_table_replace(klass->actions, g_strdup(name), action); if (trigger && *trigger) { while ((data = va_arg(args, void *))) { list = g_list_append(list, data); register_binding(klass, name, trigger, list); void gnt_bindable_action_free(GntBindableAction *action) void gnt_bindable_action_param_free(GntBindableActionParam *param) g_list_free(param->list); /* XXX: There may be a leak here for string parameters */ GntBindable * gnt_bindable_bindings_view(GntBindable *bind) GntBindable *tree = GNT_BINDABLE(gnt_tree_new_with_columns(2)); GntBindableClass *klass = GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bind)); GHashTable *hash = g_hash_table_new(g_direct_hash, g_direct_equal); BindingView bv = {hash, GNT_TREE(tree)}; gnt_tree_set_compare_func(bv.tree, (GCompareFunc)g_utf8_collate); g_hash_table_foreach(klass->actions, add_action, &bv); g_hash_table_foreach(klass->bindings, add_binding, &bv); if (GNT_TREE(tree)->list == NULL) { gnt_widget_destroy(GNT_WIDGET(tree)); gnt_tree_adjust_columns(bv.tree); g_hash_table_destroy(hash); reset_binding_window(GntBindableClass *window, gpointer k) GntBindableClass *klass = GNT_BINDABLE_CLASS(k); klass->help_window = NULL; gnt_bindable_build_help_window(GntBindable *bindable) GntBindableClass *klass = GNT_BINDABLE_GET_CLASS(bindable); tree = GNT_WIDGET(gnt_bindable_bindings_view(bindable)); klass->help_window = GNT_BINDABLE(gnt_window_new()); title = g_strdup_printf("Bindings for %s", g_type_name(G_OBJECT_TYPE(bindable))); gnt_box_set_title(GNT_BOX(klass->help_window), title); g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(gnt_bindable_rebinding_activate), bindable); gnt_box_add_widget(GNT_BOX(klass->help_window), tree); gnt_box_add_widget(GNT_BOX(klass->help_window), gnt_label_new("This widget has no customizable bindings.")); g_signal_connect(G_OBJECT(klass->help_window), "destroy", G_CALLBACK(reset_binding_window), klass); gnt_widget_show(GNT_WIDGET(klass->help_window));