qulogic/libgnt

closing merged branch
panel_window
2019-05-07, Gary Kramlich
219a8f1aa985
closing merged branch
/*
* 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
* source distribution.
*
* 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 02110-1301, USA.
*/
#include <string.h>
#include "gntinternal.h"
#undef GNT_LOG_DOMAIN
#define GNT_LOG_DOMAIN "Bindable"
#include "gntbindable.h"
#include "gntstyle.h"
#include "gnt.h"
#include "gntutils.h"
#include "gnttextview.h"
#include "gnttree.h"
#include "gntbox.h"
#include "gntbutton.h"
#include "gntwindow.h"
#include "gntlabel.h"
static struct
{
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 */
} rebind_info;
typedef struct
{
GHashTable *hash;
GntTree *tree;
} BindingView;
/******************************************************************************
* Helpers
*****************************************************************************/
static void
gnt_bindable_free_rebind_info(void)
{
g_free(rebind_info.name);
g_free(rebind_info.keys);
g_free(rebind_info.okeys);
}
static void
gnt_bindable_rebinding_cancel(G_GNUC_UNUSED GntWidget *button, gpointer data)
{
gnt_bindable_free_rebind_info();
gnt_widget_destroy(GNT_WIDGET(data));
}
static void
gnt_bindable_rebinding_rebind(G_GNUC_UNUSED GntWidget *button, gpointer data)
{
if (rebind_info.keys) {
gnt_bindable_register_binding(rebind_info.klass,
NULL,
rebind_info.okeys,
rebind_info.params);
gnt_bindable_register_binding(rebind_info.klass,
rebind_info.name,
rebind_info.keys,
rebind_info.params);
}
gnt_bindable_free_rebind_info();
gnt_widget_destroy(GNT_WIDGET(data));
}
static gboolean
gnt_bindable_rebinding_grab_key(G_GNUC_UNUSED GntBindable *bindable,
const char *text, gpointer data)
{
GntTextView *textview = GNT_TEXT_VIEW(data);
char *new_text;
const char *tmp;
if (text && *text) {
/* 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)) {
return FALSE;
}
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(new_text);
g_free(rebind_info.keys);
rebind_info.keys = g_strdup(text);
return TRUE;
}
return FALSE;
}
static void
gnt_bindable_rebinding_activate(GntBindable *data, gpointer bindable)
{
const char *widget_name = g_type_name(G_OBJECT_TYPE(bindable));
char *keys;
GntWidget *key_textview;
gint x;
GntWidget *label;
GntWidget *bind_button, *cancel_button;
GntWidget *button_box;
GList *current_row_data;
char *tmp;
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_free_full(current_row_data, g_free);
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);
g_free(tmp);
gnt_box_add_widget(GNT_BOX(vbox), label);
tmp = g_strdup_printf("KEY: \"%s\"", keys);
key_textview = gnt_text_view_new();
gnt_widget_get_position(key_textview, &x, NULL);
gnt_widget_set_size(key_textview, x, 2);
gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(key_textview), tmp, GNT_TEXT_FLAG_NORMAL);
g_free(tmp);
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);
gnt_widget_show(win);
}
static void
add_binding(const gchar *key, GntBindableActionParam *act, BindingView *bv)
{
const char *name = g_hash_table_lookup(bv->hash, act->action);
if (name && *name) {
const char *k = gnt_key_lookup(key);
if (!k)
k = key;
gnt_tree_add_row_after(bv->tree, (gpointer)k,
gnt_tree_create_row(bv->tree, k, name), NULL, NULL);
}
}
static void
add_action(gpointer key, gpointer value, BindingView *bv)
{
g_hash_table_insert(bv->hash, value, key);
}
static gpointer
bindable_clone(GntBindableAction *action)
{
GntBindableAction *ret = g_new0(GntBindableAction, 1);
ret->name = g_strdup(action->name);
ret->u = action->u;
return ret;
}
static gpointer
binding_clone(GntBindableActionParam *param)
{
GntBindableActionParam *p = g_new0(GntBindableActionParam, 1);
p->list = g_list_copy(param->list);
p->action = param->action;
return p;
}
/******************************************************************************
* GObject Implementation
*****************************************************************************/
static void
gnt_bindable_base_init(GntBindableClass *klass)
{
/* Duplicate the bindings from parent class */
if (klass->actions) {
klass->actions = gnt_hash_table_duplicate(klass->actions, g_str_hash,
g_str_equal, g_free, (GDestroyNotify)gnt_bindable_action_free,
(GntDuplicateFunc)g_strdup, (GntDuplicateFunc)bindable_clone);
klass->bindings = gnt_hash_table_duplicate(klass->bindings, g_str_hash,
g_str_equal, g_free, (GDestroyNotify)gnt_bindable_action_param_free,
(GntDuplicateFunc)g_strdup, (GntDuplicateFunc)binding_clone);
} else {
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);
}
}
static void
gnt_bindable_class_init(GntBindableClass *klass, G_GNUC_UNUSED gpointer data)
{
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));
}
/******************************************************************************
* GntBindable API
*****************************************************************************/
GType
gnt_bindable_get_type(void)
{
static volatile gsize g_define_type_id__volatile = 0;
if (g_once_init_enter(&g_define_type_id__volatile)) {
static const GTypeInfo info = {
sizeof(GntBindableClass),
(GBaseInitFunc)gnt_bindable_base_init, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc)gnt_bindable_class_init,
NULL,
NULL, /* class_data */
sizeof(GntBindable),
0, /* n_preallocs */
NULL, /* instance_init */
NULL /* value_table */
};
GType g_define_type_id =
g_type_register_static(G_TYPE_OBJECT, "GntBindable",
&info, G_TYPE_FLAG_ABSTRACT);
g_once_init_leave(&g_define_type_id__volatile,
g_define_type_id);
}
return g_define_type_id__volatile;
}
/*
* Key Remaps
*/
const char *
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);
}
/*
* Actions and Bindings
*/
gboolean
gnt_bindable_perform_action_named(GntBindable *bindable, const char *name, ...)
{
GntBindableClass *klass = GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bindable));
GList *list = NULL;
va_list args;
GntBindableAction *action;
void *p;
va_start(args, name);
while ((p = va_arg(args, void *)) != NULL)
list = g_list_append(list, p);
va_end(args);
action = g_hash_table_lookup(klass->actions, name);
if (action && action->u.action) {
return action->u.action(bindable, list);
}
return FALSE;
}
gboolean
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) {
if (param->list)
return param->action->u.action(bindable, param->list);
else
return param->action->u.action_noparam(bindable);
}
return FALSE;
}
gboolean
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);
}
static void
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);
return;
}
action = g_hash_table_lookup(klass->actions, name);
if (!action) {
gnt_warning("Invalid action name %s for %s",
name, g_type_name(G_OBJECT_CLASS_TYPE(klass)));
if (list)
g_list_free(list);
return;
}
param = g_new0(GntBindableActionParam, 1);
param->action = action;
param->list = list;
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, ...)
{
GList *list = NULL;
va_list args;
void *data;
va_start(args, trigger);
while ((data = va_arg(args, void *))) {
list = g_list_append(list, data);
}
va_end(args);
register_binding(klass, name, trigger, list);
}
void gnt_bindable_class_register_action(GntBindableClass *klass, const char *name,
GntBindableActionCallback callback, const char *trigger, ...)
{
void *data;
va_list args;
GntBindableAction *action = g_new0(GntBindableAction, 1);
GList *list;
action->name = g_strdup(name);
action->u.action = callback;
g_hash_table_replace(klass->actions, g_strdup(name), action);
if (trigger && *trigger) {
list = NULL;
va_start(args, trigger);
while ((data = va_arg(args, void *))) {
list = g_list_append(list, data);
}
va_end(args);
register_binding(klass, name, trigger, list);
}
}
void gnt_bindable_action_free(GntBindableAction *action)
{
g_free(action->name);
g_free(action);
}
void gnt_bindable_action_param_free(GntBindableActionParam *param)
{
g_list_free(param->list); /* XXX: There may be a leak here for string parameters */
g_free(param);
}
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, (GHFunc)add_action, &bv);
g_hash_table_foreach(klass->bindings, (GHFunc)add_binding, &bv);
if (gnt_tree_get_rows(GNT_TREE(tree)) == NULL) {
gnt_widget_destroy(GNT_WIDGET(tree));
tree = NULL;
} else
gnt_tree_adjust_columns(bv.tree);
g_hash_table_destroy(hash);
return tree;
}
static void
reset_binding_window(G_GNUC_UNUSED GntBindableClass *window, gpointer k)
{
GntBindableClass *klass = GNT_BINDABLE_CLASS(k);
klass->help_window = NULL;
}
gboolean
gnt_bindable_build_help_window(GntBindable *bindable)
{
GntWidget *tree;
GntBindableClass *klass = GNT_BINDABLE_GET_CLASS(bindable);
char *title;
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);
if (tree) {
g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(gnt_bindable_rebinding_activate), bindable);
gnt_box_add_widget(GNT_BOX(klass->help_window), tree);
} else
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));
g_free(title);
return TRUE;
}