2 * GNT - The GLib Ncurses Toolkit
4 * GNT is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
25 #include "gntbindable.h"
29 #include "gnttextview.h"
32 #include "gntbutton.h"
33 #include "gntwindow.h"
36 static GObjectClass
*parent_class
= NULL
;
40 char * okeys
; /* Old keystrokes */
41 char * keys
; /* New Keystrokes being bound to the action */
42 GntBindableClass
* klass
; /* Class of the object that's getting keys rebound */
43 char * name
; /* The name of the action */
44 GList
* params
; /* The list of paramaters */
48 gnt_bindable_free_rebind_info(void)
50 g_free(rebind_info
.name
);
51 g_free(rebind_info
.keys
);
52 g_free(rebind_info
.okeys
);
56 gnt_bindable_rebinding_cancel(GntWidget
*button
, gpointer data
)
58 gnt_bindable_free_rebind_info();
59 gnt_widget_destroy(GNT_WIDGET(data
));
63 gnt_bindable_rebinding_rebind(GntWidget
*button
, gpointer data
)
65 if (rebind_info
.keys
) {
66 gnt_bindable_register_binding(rebind_info
.klass
,
70 gnt_bindable_register_binding(rebind_info
.klass
,
75 gnt_bindable_free_rebind_info();
76 gnt_widget_destroy(GNT_WIDGET(data
));
80 gnt_bindable_rebinding_grab_key(GntBindable
*bindable
, const char *text
, gpointer data
)
82 GntTextView
*textview
= GNT_TEXT_VIEW(data
);
87 /* Rebinding tab or enter for something is probably not that great an idea */
88 if (!strcmp(text
, GNT_KEY_CTRL_I
) || !strcmp(text
, GNT_KEY_ENTER
)) {
92 tmp
= gnt_key_lookup(text
);
93 new_text
= g_strdup_printf("KEY: \"%s\"", tmp
);
94 gnt_text_view_clear(textview
);
95 gnt_text_view_append_text_with_flags(textview
, new_text
, GNT_TEXT_FLAG_NORMAL
);
98 g_free(rebind_info
.keys
);
99 rebind_info
.keys
= g_strdup(text
);
106 gnt_bindable_rebinding_activate(GntBindable
*data
, gpointer bindable
)
108 const char *widget_name
= g_type_name(G_OBJECT_TYPE(bindable
));
110 GntWidget
*key_textview
;
112 GntWidget
*bind_button
, *cancel_button
;
113 GntWidget
*button_box
;
114 GList
*current_row_data
;
116 GntWidget
*win
= gnt_window_new();
117 GntTree
*tree
= GNT_TREE(data
);
118 GntWidget
*vbox
= gnt_box_new(FALSE
, TRUE
);
120 rebind_info
.klass
= GNT_BINDABLE_GET_CLASS(bindable
);
122 current_row_data
= gnt_tree_get_selection_text_list(tree
);
123 rebind_info
.name
= g_strdup(g_list_nth_data(current_row_data
, 1));
125 keys
= gnt_tree_get_selection_data(tree
);
126 rebind_info
.okeys
= g_strdup(gnt_key_translate(keys
));
128 rebind_info
.params
= NULL
;
130 g_list_foreach(current_row_data
, (GFunc
)g_free
, NULL
);
131 g_list_free(current_row_data
);
133 gnt_box_set_alignment(GNT_BOX(vbox
), GNT_ALIGN_MID
);
135 gnt_box_set_title(GNT_BOX(win
), "Key Capture");
137 tmp
= g_strdup_printf("Type the new bindings for %s in a %s.", rebind_info
.name
, widget_name
);
138 label
= gnt_label_new(tmp
);
140 gnt_box_add_widget(GNT_BOX(vbox
), label
);
142 tmp
= g_strdup_printf("KEY: \"%s\"", keys
);
143 key_textview
= gnt_text_view_new();
144 gnt_widget_set_size(key_textview
, key_textview
->priv
.x
, 2);
145 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(key_textview
), tmp
, GNT_TEXT_FLAG_NORMAL
);
147 gnt_widget_set_name(key_textview
, "keystroke");
148 gnt_box_add_widget(GNT_BOX(vbox
), key_textview
);
150 g_signal_connect(G_OBJECT(win
), "key_pressed", G_CALLBACK(gnt_bindable_rebinding_grab_key
), key_textview
);
152 button_box
= gnt_box_new(FALSE
, FALSE
);
154 bind_button
= gnt_button_new("BIND");
155 gnt_widget_set_name(bind_button
, "bind");
156 gnt_box_add_widget(GNT_BOX(button_box
), bind_button
);
158 cancel_button
= gnt_button_new("Cancel");
159 gnt_widget_set_name(cancel_button
, "cancel");
160 gnt_box_add_widget(GNT_BOX(button_box
), cancel_button
);
162 g_signal_connect(G_OBJECT(bind_button
), "activate", G_CALLBACK(gnt_bindable_rebinding_rebind
), win
);
163 g_signal_connect(G_OBJECT(cancel_button
), "activate", G_CALLBACK(gnt_bindable_rebinding_cancel
), win
);
165 gnt_box_add_widget(GNT_BOX(vbox
), button_box
);
167 gnt_box_add_widget(GNT_BOX(win
), vbox
);
168 gnt_widget_show(win
);
178 add_binding(gpointer key
, gpointer value
, gpointer data
)
180 BindingView
*bv
= data
;
181 GntBindableActionParam
*act
= value
;
182 const char *name
= g_hash_table_lookup(bv
->hash
, act
->action
);
184 const char *k
= gnt_key_lookup(key
);
187 gnt_tree_add_row_after(bv
->tree
, (gpointer
)k
,
188 gnt_tree_create_row(bv
->tree
, k
, name
), NULL
, NULL
);
193 add_action(gpointer key
, gpointer value
, gpointer data
)
195 BindingView
*bv
= data
;
196 g_hash_table_insert(bv
->hash
, value
, key
);
200 gnt_bindable_class_init(GntBindableClass
*klass
)
202 parent_class
= g_type_class_peek_parent(klass
);
204 klass
->actions
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
,
205 (GDestroyNotify
)gnt_bindable_action_free
);
206 klass
->bindings
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
,
207 (GDestroyNotify
)gnt_bindable_action_param_free
);
209 gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass
), GNT_BINDABLE_CLASS(klass
));
214 bindable_clone(GntBindableAction
*action
)
216 GntBindableAction
*ret
= g_new0(GntBindableAction
, 1);
217 ret
->name
= g_strdup(action
->name
);
223 binding_clone(GntBindableActionParam
*param
)
225 GntBindableActionParam
*p
= g_new0(GntBindableActionParam
, 1);
226 p
->list
= g_list_copy(param
->list
);
227 p
->action
= param
->action
;
232 duplicate_hashes(GntBindableClass
*klass
)
234 /* Duplicate the bindings from parent class */
235 if (klass
->actions
) {
236 klass
->actions
= g_hash_table_duplicate(klass
->actions
, g_str_hash
,
237 g_str_equal
, g_free
, (GDestroyNotify
)gnt_bindable_action_free
,
238 (GDupFunc
)g_strdup
, (GDupFunc
)bindable_clone
);
239 klass
->bindings
= g_hash_table_duplicate(klass
->bindings
, g_str_hash
,
240 g_str_equal
, g_free
, (GDestroyNotify
)gnt_bindable_action_param_free
,
241 (GDupFunc
)g_strdup
, (GDupFunc
)binding_clone
);
243 klass
->actions
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
,
244 (GDestroyNotify
)gnt_bindable_action_free
);
245 klass
->bindings
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
,
246 (GDestroyNotify
)gnt_bindable_action_param_free
);
252 /******************************************************************************
254 *****************************************************************************/
256 gnt_bindable_get_gtype(void)
258 static GType type
= 0;
261 static const GTypeInfo info
= {
262 sizeof(GntBindableClass
),
263 (GBaseInitFunc
)duplicate_hashes
, /* base_init */
264 NULL
, /* base_finalize */
265 (GClassInitFunc
)gnt_bindable_class_init
,
267 NULL
, /* class_data */
270 NULL
, /* instance_init */
271 NULL
/* value_table */
274 type
= g_type_register_static(G_TYPE_OBJECT
,
276 &info
, G_TYPE_FLAG_ABSTRACT
);
286 gnt_bindable_remap_keys(GntBindable
*bindable
, const char *text
)
288 const char *remap
= NULL
;
289 GType type
= G_OBJECT_TYPE(bindable
);
290 GntBindableClass
*klass
= GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bindable
));
292 if (klass
->remaps
== NULL
)
294 klass
->remaps
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
295 gnt_styles_get_keyremaps(type
, klass
->remaps
);
298 remap
= g_hash_table_lookup(klass
->remaps
, text
);
300 return (remap
? remap
: text
);
304 * Actions and Bindings
307 gnt_bindable_perform_action_named(GntBindable
*bindable
, const char *name
, ...)
309 GntBindableClass
*klass
= GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bindable
));
312 GntBindableAction
*action
;
315 va_start(args
, name
);
316 while ((p
= va_arg(args
, void *)) != NULL
)
317 list
= g_list_append(list
, p
);
320 action
= g_hash_table_lookup(klass
->actions
, name
);
321 if (action
&& action
->u
.action
) {
322 return action
->u
.action(bindable
, list
);
328 gnt_bindable_perform_action_key(GntBindable
*bindable
, const char *keys
)
330 GntBindableClass
*klass
= GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bindable
));
331 GntBindableActionParam
*param
= g_hash_table_lookup(klass
->bindings
, keys
);
333 if (param
&& param
->action
) {
335 return param
->action
->u
.action(bindable
, param
->list
);
337 return param
->action
->u
.action_noparam(bindable
);
343 register_binding(GntBindableClass
*klass
, const char *name
, const char *trigger
, GList
*list
)
345 GntBindableActionParam
*param
;
346 GntBindableAction
*action
;
348 if (name
== NULL
|| *name
== '\0') {
349 g_hash_table_remove(klass
->bindings
, (char*)trigger
);
350 gnt_keys_del_combination(trigger
);
354 action
= g_hash_table_lookup(klass
->actions
, name
);
356 g_printerr("GntBindable: Invalid action name %s for %s\n",
357 name
, g_type_name(G_OBJECT_CLASS_TYPE(klass
)));
363 param
= g_new0(GntBindableActionParam
, 1);
364 param
->action
= action
;
366 g_hash_table_replace(klass
->bindings
, g_strdup(trigger
), param
);
367 gnt_keys_add_combination(trigger
);
370 void gnt_bindable_register_binding(GntBindableClass
*klass
, const char *name
,
371 const char *trigger
, ...)
377 va_start(args
, trigger
);
378 while ((data
= va_arg(args
, void *))) {
379 list
= g_list_append(list
, data
);
383 register_binding(klass
, name
, trigger
, list
);
386 void gnt_bindable_class_register_action(GntBindableClass
*klass
, const char *name
,
387 GntBindableActionCallback callback
, const char *trigger
, ...)
391 GntBindableAction
*action
= g_new0(GntBindableAction
, 1);
394 action
->name
= g_strdup(name
);
395 action
->u
.action
= callback
;
397 g_hash_table_replace(klass
->actions
, g_strdup(name
), action
);
399 if (trigger
&& *trigger
) {
401 va_start(args
, trigger
);
402 while ((data
= va_arg(args
, void *))) {
403 list
= g_list_append(list
, data
);
407 register_binding(klass
, name
, trigger
, list
);
411 void gnt_bindable_action_free(GntBindableAction
*action
)
413 g_free(action
->name
);
417 void gnt_bindable_action_param_free(GntBindableActionParam
*param
)
419 g_list_free(param
->list
); /* XXX: There may be a leak here for string parameters */
423 GntBindable
* gnt_bindable_bindings_view(GntBindable
*bind
)
425 GntBindable
*tree
= GNT_BINDABLE(gnt_tree_new_with_columns(2));
426 GntBindableClass
*klass
= GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bind
));
427 GHashTable
*hash
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
428 BindingView bv
= {hash
, GNT_TREE(tree
)};
430 gnt_tree_set_compare_func(bv
.tree
, (GCompareFunc
)g_utf8_collate
);
431 g_hash_table_foreach(klass
->actions
, add_action
, &bv
);
432 g_hash_table_foreach(klass
->bindings
, add_binding
, &bv
);
433 if (GNT_TREE(tree
)->list
== NULL
) {
434 gnt_widget_destroy(GNT_WIDGET(tree
));
437 gnt_tree_adjust_columns(bv
.tree
);
438 g_hash_table_destroy(hash
);
444 reset_binding_window(GntBindableClass
*window
, gpointer k
)
446 GntBindableClass
*klass
= GNT_BINDABLE_CLASS(k
);
447 klass
->help_window
= NULL
;
451 gnt_bindable_build_help_window(GntBindable
*bindable
)
454 GntBindableClass
*klass
= GNT_BINDABLE_GET_CLASS(bindable
);
457 tree
= GNT_WIDGET(gnt_bindable_bindings_view(bindable
));
459 klass
->help_window
= GNT_BINDABLE(gnt_window_new());
460 title
= g_strdup_printf("Bindings for %s", g_type_name(G_OBJECT_TYPE(bindable
)));
461 gnt_box_set_title(GNT_BOX(klass
->help_window
), title
);
463 g_signal_connect(G_OBJECT(tree
), "activate", G_CALLBACK(gnt_bindable_rebinding_activate
), bindable
);
464 gnt_box_add_widget(GNT_BOX(klass
->help_window
), tree
);
466 gnt_box_add_widget(GNT_BOX(klass
->help_window
), gnt_label_new("This widget has no customizable bindings."));
468 g_signal_connect(G_OBJECT(klass
->help_window
), "destroy", G_CALLBACK(reset_binding_window
), klass
);
469 gnt_widget_show(GNT_WIDGET(klass
->help_window
));