diff options
Diffstat (limited to 'gui_frame.c')
-rw-r--r-- | gui_frame.c | 1875 |
1 files changed, 1875 insertions, 0 deletions
diff --git a/gui_frame.c b/gui_frame.c new file mode 100644 index 0000000..e52425d --- /dev/null +++ b/gui_frame.c @@ -0,0 +1,1875 @@ +/* + * gui_frame.c - GUI, frame window + * + * Written 2009, 2010, 2012 by Werner Almesberger + * Copyright 2009, 2010, 2012 by Werner Almesberger + * + * This program 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. + */ + + +#include <string.h> +#include <gtk/gtk.h> + +#include "util.h" +#include "error.h" +#include "dump.h" +#include "inst.h" +#include "obj.h" +#include "delete.h" +#include "unparse.h" +#include "gui_util.h" +#include "gui_style.h" +#include "gui_status.h" +#include "gui_tool.h" +#include "gui_canvas.h" +#include "gui.h" +#include "gui_frame_drag.h" +#include "gui_frame.h" + + +int show_vars = 1; + + +/* ----- add elements, shared ---------------------------------------------- */ + + +/* @@@ merge with fpd.y */ + +static void add_table(struct frame *frame, struct table **anchor) +{ + struct table *table, **walk; + + table = zalloc_type(struct table); + table->vars = zalloc_type(struct var); + table->vars->name = unique("_"); + table->vars->frame = frame; + table->vars->table = table; + table->rows = zalloc_type(struct row); + table->rows->table = table; + table->rows->values = zalloc_type(struct value); + table->rows->values->expr = parse_expr("0"); + table->rows->values->row = table->rows; + table->active_row = table->rows; + if (anchor) { + table->next = *anchor; + *anchor = table; + } else { + for (walk = &frame->tables; *walk; walk = &(*walk)->next); + *walk = table; + } + change_world(); +} + + +static void add_loop(struct frame *frame, struct loop **anchor) +{ + struct loop *loop, **walk; + + loop = zalloc_type(struct loop); + loop->var.name = unique("_"); + loop->var.frame = frame; + loop->from.expr = parse_expr("0"); + loop->to.expr = parse_expr("0"); + if (anchor) { + loop->next = *anchor; + *anchor = loop; + } else { + loop->next = NULL; + for (walk = &frame->loops; *walk; walk = &(*walk)->next); + *walk = loop; + } + change_world(); +} + + +/* ----- popup dispatcher -------------------------------------------------- */ + + +static void *popup_data; + + +static void pop_up(GtkWidget *menu, GdkEventButton *event, void *data) +{ + popup_data = data; + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, + event->button, event->time); +} + + +/* ----- popup: frame ------------------------------------------------------ */ + + +static GtkItemFactory *factory_frame; +static GtkWidget *popup_frame_widget; + + +static void popup_add_frame(void) +{ + struct frame *parent = popup_data; + struct frame *new; + + new = zalloc_type(struct frame); + new->name = unique("_"); + new->next = parent->next; + parent->next = new; + change_world(); +} + + +static void popup_del_frame(void) +{ + struct frame *frame = popup_data; + + assert(frame != frames); + delete_frame(frame); + if (active_frame == frame) + select_frame(frames); + change_world(); +} + + +static void popup_add_table(void) +{ + add_table(popup_data, NULL); +} + + +static void popup_add_loop(void) +{ + add_loop(popup_data, NULL); +} + + +static GtkItemFactoryEntry popup_frame_entries[] = { + { "/Add frame", NULL, popup_add_frame,0, "<Item>" }, + { "/sep0", NULL, NULL, 0, "<Separator>" }, + { "/Add variable", NULL, popup_add_table,0, "<Item>" }, + { "/Add loop", NULL, popup_add_loop, 0, "<Item>" }, + { "/sep1", NULL, NULL, 0, "<Separator>" }, + { "/Delete frame", NULL, popup_del_frame,0, "<Item>" }, + { "/sep2", NULL, NULL, 0, "<Separator>" }, + { "/Close", NULL, NULL, 0, "<Item>" }, + { NULL } +}; + + +static gboolean can_add_frame(void) +{ + const struct frame *frame; + + for (frame = frames->next; frame; frame = frame->next) + if (!strcmp(frame->name, "_")) + return FALSE; + return TRUE; +} + + +static gboolean can_add_var(const struct frame *frame) +{ + const struct table *table; + const struct var *var; + const struct loop *loop; + + for (table = frame->tables; table; table = table->next) + for (var = table->vars; var; var = var->next) + if (!strcmp(var->name, "_")) + return FALSE; + for (loop = frame->loops; loop; loop = loop->next) + if (!strcmp(loop->var.name, "_")) + return FALSE; + return TRUE; +} + + +static void enable_add_var(struct frame *frame, GtkItemFactory *factory) +{ + gboolean add_var; + + add_var = can_add_var(frame); + gtk_widget_set_sensitive( + gtk_item_factory_get_item(factory, "/Add variable"), add_var); + gtk_widget_set_sensitive( + gtk_item_factory_get_item(factory, "/Add loop"), add_var); +} + + +static void pop_up_frame(struct frame *frame, GdkEventButton *event) +{ + gtk_widget_set_sensitive( + gtk_item_factory_get_item(factory_frame, "/Delete frame"), + frame != frames); + + gtk_widget_set_sensitive( + gtk_item_factory_get_item(factory_frame, "/Add frame"), + can_add_frame()); + + enable_add_var(frame, factory_frame); + + pop_up(popup_frame_widget, event, frame); +} + + +/* ----- popup: single variable -------------------------------------------- */ + + +static GtkItemFactory *factory_single_var; +static GtkWidget *popup_single_var_widget; + + +static void add_row_here(struct table *table, struct row **anchor) +{ + struct row *row; + const struct value *walk; + struct value *value; + + row = zalloc_type(struct row); + row->table = table; + /* @@@ future: adjust type */ + for (walk = table->rows->values; walk; walk = walk->next) { + value = zalloc_type(struct value); + value->expr = parse_expr("0"); + value->row = row; + value->next = row->values; + row->values = value; + } + row->next = *anchor; + *anchor = row; + change_world(); +} + + +static void add_column_here(struct table *table, struct var **anchor) +{ + const struct var *walk; + struct var *var; + struct row *row; + struct value *value; + struct value **value_anchor; + int n = 0, i; + + for (walk = table->vars; walk != *anchor; walk = walk->next) + n++; + var = zalloc_type(struct var); + var->name = unique("_"); + var->frame = table->vars->frame; + var->table = table; + var->next = *anchor; + *anchor = var; + for (row = table->rows; row; row = row->next) { + value_anchor = &row->values; + for (i = 0; i != n; i++) + value_anchor = &(*value_anchor)->next; + value = zalloc_type(struct value); + value->expr = parse_expr("0"); + value->row = row; + value->next = *value_anchor; + *value_anchor = value; + } + change_world(); +} + + +static void popup_add_row(void) +{ + struct var *var = popup_data; + + add_row_here(var->table, &var->table->rows); +} + + +static void popup_add_column(void) +{ + struct var *var = popup_data; + + add_column_here(var->table, &var->next); +} + + +static void popup_del_table(void) +{ + struct var *var = popup_data; + + delete_table(var->table); + change_world(); +} + + +static void popup_add_table_from_var(void) +{ + struct var *var = popup_data; + + add_table(var->frame, &var->table->next); +} + + +static void popup_add_loop_from_var(void) +{ + struct var *var = popup_data; + + add_loop(var->frame, NULL); +} + + +static GtkItemFactoryEntry popup_single_var_entries[] = { + { "/Add row", NULL, popup_add_row, 0, "<Item>" }, + { "/Add column", NULL, popup_add_column, 0, "<Item>" }, + { "/sep1", NULL, NULL, 0, "<Separator>" }, + { "/Delete variable", NULL, popup_del_table,0, "<Item>" }, + { "/sep2", NULL, NULL, 0, "<Separator>" }, + { "/Add variable", NULL, popup_add_table_from_var, + 0, "<Item>" }, + { "/Add loop", NULL, popup_add_loop_from_var, + 0, "<Item>" }, + { "/sep3", NULL, NULL, 0, "<Separator>" }, + { "/Close", NULL, NULL, 0, "<Item>" }, + { NULL } +}; + + +static void pop_up_single_var(struct var *var, GdkEventButton *event) +{ + gtk_widget_set_sensitive( + gtk_item_factory_get_item(factory_single_var, "/Add column"), + can_add_var(var->frame)); + enable_add_var(var->frame, factory_single_var); + pop_up(popup_single_var_widget, event, var); +} + + +/* ----- popup: table variable --------------------------------------------- */ + + +static GtkItemFactory *factory_table_var; +static GtkWidget *popup_table_var_widget; + + +static void popup_del_column(void) +{ + struct var *var = popup_data; + const struct var *walk; + int n = 0; + + for (walk = var->table->vars; walk != var; walk = walk->next) + n++; + delete_column(var->table, n); + change_world(); +} + + +static GtkItemFactoryEntry popup_table_var_entries[] = { + { "/Add row", NULL, popup_add_row, 0, "<Item>" }, + { "/Add column", NULL, popup_add_column, 0, "<Item>" }, + { "/sep1", NULL, NULL, 0, "<Separator>" }, + { "/Delete table", NULL, popup_del_table,0, "<Item>" }, + { "/Delete column", NULL, popup_del_column, 0, "<Item>" }, + { "/sep2", NULL, NULL, 0, "<Separator>" }, + { "/Add variable", NULL, popup_add_table_from_var, + 0, "<Item>" }, + { "/Add loop", NULL, popup_add_loop_from_var, + 0, "<Item>" }, + { "/sep3", NULL, NULL, 0, "<Separator>" }, + { "/Close", NULL, NULL, 0, "<Item>" }, + { NULL } +}; + + +static void pop_up_table_var(struct var *var, GdkEventButton *event) +{ + gtk_widget_set_sensitive( + gtk_item_factory_get_item(factory_table_var, "/Delete column"), + var->table->vars->next != NULL); + gtk_widget_set_sensitive( + gtk_item_factory_get_item(factory_table_var, "/Add column"), + can_add_var(var->frame)); + enable_add_var(var->frame, factory_table_var); + pop_up(popup_table_var_widget, event, var); +} + + +/* ----- popup: table value ------------------------------------------------ */ + + +static GtkItemFactory *factory_table_value; +static GtkWidget *popup_table_value_widget; + + +static void popup_add_column_by_value(void) +{ + struct value *value = popup_data; + const struct value *walk; + struct table *table = value->row->table; + struct var *var = table->vars; + + for (walk = value->row->values; walk != value; walk = walk->next) + var = var->next; + add_column_here(table, &var->next); +} + + +static void popup_add_row_by_value(void) +{ + struct value *value = popup_data; + + add_row_here(value->row->table, &value->row->next); +} + + +static void popup_del_row(void) +{ + struct value *value = popup_data; + struct table *table = value->row->table; + + delete_row(value->row); + if (table->active_row == value->row) + table->active_row = table->rows; + change_world(); +} + + +static void popup_del_column_by_value(void) +{ + struct value *value = popup_data; + const struct value *walk; + int n = 0; + + for (walk = value->row->values; walk != value; walk = walk->next) + n++; + delete_column(value->row->table, n); + change_world(); +} + + +static GtkItemFactoryEntry popup_table_value_entries[] = { + { "/Add row", NULL, popup_add_row_by_value, 0, "<Item>" }, + { "/Add column", NULL, popup_add_column_by_value, + 0, "<Item>" }, + { "/sep1", NULL, NULL, 0, "<Separator>" }, + { "/Delete row", NULL, popup_del_row, 0, "<Item>" }, + { "/Delete column", NULL, popup_del_column_by_value, + 0, "<Item>" }, + { "/sep2", NULL, NULL, 0, "<Separator>" }, + { "/Close", NULL, NULL, 0, "<Item>" }, + { NULL } +}; + + +static void pop_up_table_value(struct value *value, GdkEventButton *event) +{ + gtk_widget_set_sensitive( + gtk_item_factory_get_item(factory_table_value, "/Delete row"), + value->row->table->rows->next != NULL); + gtk_widget_set_sensitive( + gtk_item_factory_get_item(factory_table_value, "/Delete column"), + value->row->table->vars->next != NULL); + pop_up(popup_table_value_widget, event, value); +} + + +/* ----- popup: loop ------------------------------------------------------- */ + + +static GtkItemFactory *factory_loop_var; +static GtkWidget *popup_loop_var_widget; + + +static void popup_del_loop(void) +{ + struct loop *loop = popup_data; + + delete_loop(loop); + change_world(); +} + + +static void popup_add_table_from_loop(void) +{ + struct loop *loop = popup_data; + + add_table(loop->var.frame, NULL); +} + + +static void popup_add_loop_from_loop(void) +{ + struct loop *loop = popup_data; + + add_loop(loop->var.frame, &loop->next); +} + + +static GtkItemFactoryEntry popup_loop_var_entries[] = { + { "/Delete loop", NULL, popup_del_loop, 0, "<Item>" }, + { "/sep1", NULL, NULL, 0, "<Separator>" }, + { "/Add variable", NULL, popup_add_table_from_loop, + 0, "<Item>" }, + { "/Add loop", NULL, popup_add_loop_from_loop, + 0, "<Item>" }, + { "/sep2", NULL, NULL, 0, "<Separator>" }, + { "/Close", NULL, NULL, 0, "<Item>" }, + { NULL } +}; + + +static void pop_up_loop_var(struct loop *loop, GdkEventButton *event) +{ + enable_add_var(loop->var.frame, factory_loop_var); + pop_up(popup_loop_var_widget, event, loop); +} + + +/* ----- make popups ------------------------------------------------------- */ + + +static GtkWidget *make_popup(const char *name, GtkItemFactory **factory, + GtkItemFactoryEntry *entries) +{ + GtkWidget *popup; + int n; + + n = 0; + for (n = 0; entries[n].path; n++); + + *factory = gtk_item_factory_new(GTK_TYPE_MENU, name, NULL); + gtk_item_factory_create_items(*factory, n, entries, NULL); + popup = gtk_item_factory_get_widget(*factory, name); + return popup; +} + + +void make_popups(void) +{ + popup_frame_widget = make_popup("<FpedFramePopUp>", + &factory_frame, popup_frame_entries); + popup_single_var_widget = make_popup("<FpedSingleVarPopUp>", + &factory_single_var, popup_single_var_entries); + popup_table_var_widget = make_popup("<FpedTableVarPopUp>", + &factory_table_var, popup_table_var_entries); + popup_table_value_widget = make_popup("<FpedTableValusPopUp>", + &factory_table_value, popup_table_value_entries); + popup_loop_var_widget = make_popup("<FpedLoopVarPopUp>", + &factory_loop_var, popup_loop_var_entries); +} + + +/* ----- variable list ----------------------------------------------------- */ + + +static void add_sep(GtkWidget *box, int size) +{ + GtkWidget *sep; + GdkColor black = { 0, 0, 0, 0 }; + + sep = gtk_drawing_area_new(); + gtk_box_pack_start(GTK_BOX(box), sep, FALSE, TRUE, size); + gtk_widget_modify_bg(sep, GTK_STATE_NORMAL, &black); +} + + +/* ----- variable name editor ---------------------------------------------- */ + + +static int find_var_in_frame(const struct frame *frame, const char *name, + const struct var *self) +{ + const struct table *table; + const struct loop *loop; + const struct var *var; + + for (table = frame->tables; table; table = table->next) + for (var = table->vars; var; var = var->next) + if (var != self && !var->key && + !strcmp(var->name, name)) + return 1; + for (loop = frame->loops; loop; loop = loop->next) + if (&loop->var != self && !strcmp(loop->var.name, name)) + return 1; + return 0; +} + + +static int validate_var_name(const char *s, void *ctx) +{ + struct var *var = ctx; + + if (!is_id(s)) + return 0; + if (var->key) + return 1; + return !find_var_in_frame(var->frame, s, var); +} + + +static void unselect_var(void *data) +{ + struct var *var = data; + + label_in_box_bg(var->widget, COLOR_VAR_PASSIVE); +} + + +static void show_value(const struct expr *expr, const struct frame *frame) +{ + const char *value_string; + struct num value; + + status_set_type_x(NULL, "value ="); + value_string = eval_str(expr, frame); + if (value_string) { + status_set_x(NULL, "\"%s\"", value_string); + } else { + value = eval_num(expr, frame); + if (is_undef(value)) + status_set_x(NULL, "undefined"); + else + status_set_x(NULL, "%lg%s", value.n, str_unit(value)); + } +} + + +static void show_var_value(const struct var *var, const struct frame *frame) +{ + const struct var *walk; + const struct value *value; + + if (!var->table) + return; + value = var->table->active_row->values; + for (walk = var->table->vars; walk != var; walk = walk->next) + value = value->next; + show_value(value->expr, frame); +} + + +static void edit_var(struct var *var, + void (*set_values)(void *user, const struct value *values, int n_values), + void *user, int max_values) +{ + inst_select_outside(var, unselect_var); + label_in_box_bg(var->widget, COLOR_VAR_EDITING); + status_set_type_entry(NULL, "name ="); + status_set_name("Variable name", "%s", var->name); + show_var_value(var, var->frame); + edit_nothing(); + edit_var_type(var); + edit_unique_with_values(&var->name, validate_var_name, var, + set_values, user, max_values, + "Variable name. " + "Shortcut:<b><i>name</i>=<i>value</i>,<i>...</i> </b>"); +} + + +static void set_col_values(void *user, const struct value *values, + int n_values); + + +void reselect_var(struct var *var) +{ + edit_var(var, set_col_values, var, -1); +} + + +/* ----- value editor ------------------------------------------------------ */ + + +static void unselect_value(void *data) +{ + struct value *value = data; + + /* + * This condition is a little cryptic. Here is what it does: + * + * IF table/assignment (not loop) + * AND the current row is the active (selected) row + * AND it's an assignment (not a table). + * + * We need the last condition because the expressions of assignments + * are drawn with COLOR_EXPR_PASSIVE. (See build_assignment.) + */ + label_in_box_bg(value->widget, + value->row && value->row->table->active_row == value->row && + (value->row->table->rows->next || value->row->table->vars->next) ? + COLOR_CHOICE_SELECTED : COLOR_EXPR_PASSIVE); +} + + +static void edit_value(struct value *value, const struct frame *frame) +{ + inst_select_outside(value, unselect_value); + label_in_box_bg(value->widget, COLOR_EXPR_EDITING); + show_value(value->expr, frame); + edit_nothing(); + edit_expr(&value->expr, "Value"); +} + + +static void edit_value_list(struct value *value, const struct frame *frame, + void (*set_values)(void *user, const struct value *values, int n_values), + void *user) +{ + inst_select_outside(value, unselect_value); + label_in_box_bg(value->widget, COLOR_EXPR_EDITING); + show_value(value->expr, frame); + edit_nothing(); + edit_expr_list(value->expr, set_values, user, "Value(s)"); +} + + +/* ----- activator --------------------------------------------------------- */ + + +static GtkWidget *add_activator(GtkWidget *hbox, int active, + gboolean (*cb)(GtkWidget *widget, GdkEventButton *event, gpointer data), + gpointer user, const char *tooltip, const char *fmt, ...) +{ + GtkWidget *label; + va_list ap; + char buf[100]; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + label = label_in_box_new(buf, tooltip); + gtk_misc_set_padding(GTK_MISC(label), 2, 2); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + label_in_box_bg(label, + active ? COLOR_CHOICE_SELECTED : COLOR_CHOICE_UNSELECTED); + gtk_box_pack_start(GTK_BOX(hbox), box_of_label(label), + FALSE, FALSE, 2); + g_signal_connect(G_OBJECT(box_of_label(label)), + "button_press_event", G_CALLBACK(cb), user); + return label; +} + + +/* ----- assignments ------------------------------------------------------- */ + + +static void set_col_values(void *user, const struct value *values, + int n_values) +{ + struct var *var = user; + struct table *table = var->table; + struct value *value; + const struct var *walk; + struct row **row; + + row = &table->rows; + while (values) { + if (!*row) + add_row_here(table, row); + value = (*row)->values; + for (walk = table->vars; walk != var; walk = walk->next) + value = value->next; + free_expr(value->expr); + value->expr = values->expr; + values = values->next; + row = &(*row)->next; + } +} + + +static gboolean assignment_var_select_event(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + struct var *var = data; + + switch (event->button) { + case 1: + edit_var(var, set_col_values, var, -1); + break; + case 3: + pop_up_single_var(var, event); + break; + } + return TRUE; +} + + +static gboolean assignment_value_select_event(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + struct value *value = data; + + switch (event->button) { + case 1: + edit_nothing(); + edit_value(value, value->row->table->vars->frame); + break; + } + return TRUE; +} + + +/* + * In tables, expressions in the active row have a COLOR_CHOICE_SELECTED + * background. While expressions in assignments are technically on the active + * (and only) row, we use COLOR_VAR_PASSIVE for better readability. + */ + +static void build_assignment(GtkWidget *vbox, struct frame *frame, + struct table *table) +{ + GtkWidget *hbox, *field; + char *name, *expr; + + if (!table->vars || table->vars->next) + return; + if (!table->rows || table->rows->next) + return; + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + name = stralloc_printf("%s%s", table->vars->key ? "?" : "", + table->vars->name); + field = label_in_box_new(name, "Variable name. Click to edit."); + free(name); + + gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0); + label_in_box_bg(field, COLOR_VAR_PASSIVE); + table->vars->widget = field; + g_signal_connect(G_OBJECT(box_of_label(field)), + "button_press_event", + G_CALLBACK(assignment_var_select_event), table->vars); + + gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" = "), + FALSE, FALSE, 0); + + expr = unparse(table->rows->values->expr); + field = label_in_box_new(expr, "Variable value. Click to edit."); + free(expr); + gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0); + label_in_box_bg(field, COLOR_EXPR_PASSIVE); + table->rows->values->widget = field; + g_signal_connect(G_OBJECT(box_of_label(field)), + "button_press_event", + G_CALLBACK(assignment_value_select_event), table->rows->values); +} + + +/* ----- tables ------------------------------------------------------------ */ + + +static void select_row(struct row *row) +{ + struct table *table = row->table; + struct value *value; + + for (value = table->active_row->values; value; value = value->next) + label_in_box_bg(value->widget, COLOR_ROW_UNSELECTED); + table->active_row = row; + for (value = table->active_row->values; value; value = value->next) + label_in_box_bg(value->widget, COLOR_ROW_SELECTED); +} + + +static void set_row_values(void *user, const struct value *values, + int n_values) +{ + struct value *value = user; + struct row *row = value->row; + struct table *table = row->table; + struct var **var; + const struct value *walk; + int first = 1; + + var = &table->vars; + for (walk = row->values; walk != value; walk = walk->next) + var = &(*var)->next; + + while (values) { + if (!*var) + add_column_here(table, var); + if (first) + first = 0; + else + value = value->next; + free_expr(value->expr); + value->expr = values->expr; + values = values->next; + var = &(*var)->next; + } +} + + +static gboolean table_var_press_event(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + struct var *var = data; + + switch (event->button) { + case 3: + pop_up_table_var(var, event); + return TRUE; + } + return FALSE; +} + + +static gboolean table_var_release_event(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + struct var *var = data; + + switch (event->button) { + case 1: + if (is_dragging(var)) + return FALSE; + edit_var(var, set_col_values, var, -1); + return TRUE; + } + return FALSE; +} + + +static gboolean table_value_press_event(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + struct value *value = data; + + switch (event->button) { + case 3: + pop_up_table_value(value, event); + return TRUE; + } + return FALSE; +} + + +static gboolean table_value_release_event(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + struct value *value = data; + + switch (event->button) { + case 1: + if (is_dragging(value)) + return FALSE; + if (!value->row || + value->row->table->active_row == value->row) { + edit_nothing(); + edit_value_list(value, value->row->table->vars->frame, + set_row_values, value); + } else { + select_row(value->row); + change_world(); + } + return TRUE; + } + return FALSE; +} + + +static gboolean table_scroll_event(GtkWidget *widget, GdkEventScroll *event, + gpointer data) +{ + struct table *table = data; + struct row *row, *last; + + switch (event->direction) { + case GDK_SCROLL_UP: + last = NULL; + for (row = table->rows; + row && (!last || row != table->active_row); row = row->next) + last = row; + table->active_row = last; + change_world(); + break; + case GDK_SCROLL_DOWN: + table->active_row = table->active_row->next; + if (!table->active_row) + table->active_row = table->rows; + change_world(); + break; + default: + /* ignore */; + } + return TRUE; +} + + +/* @@@ this function is too long */ + +static void build_table(GtkWidget *vbox, struct frame *frame, + struct table *table, int wrap_width) +{ + GtkWidget *tab, *field; + GtkWidget *evbox, *align, *sep; + struct var *var; + struct row *row; + struct value *value; + int n_vars = 0, n_rows = 0; + int n_var, n_row, pos; + char *name, *expr; + GdkColor color; + + for (var = table->vars; var; var = var->next) + n_vars++; + for (row = table->rows; row; row = row->next) + n_rows++; + + if (n_vars == 1 && n_rows == 1) + return; + + var = table->vars; + n_var = 0; + n_vars = 0; + while (var) { + if (n_vars) { + gtk_table_resize(GTK_TABLE(tab), n_rows, n_vars+1); + } else { + evbox = gtk_event_box_new(); + align = gtk_alignment_new(0, 0, 0, 0); + gtk_container_add(GTK_CONTAINER(align), evbox); + gtk_box_pack_start(GTK_BOX(vbox), align, + FALSE, FALSE, 0); + + tab = gtk_table_new(n_rows+1, n_vars, FALSE); + gtk_container_add(GTK_CONTAINER(evbox), tab); + color = get_color(COLOR_VAR_TABLE_SEP); + gtk_widget_modify_bg(GTK_WIDGET(evbox), + GTK_STATE_NORMAL, &color); + + gtk_table_set_row_spacings(GTK_TABLE(tab), 1); + gtk_table_set_col_spacings(GTK_TABLE(tab), 1); + } + + name = stralloc_printf("%s%s", var->key ? "?" : "", var->name); + field = label_in_box_new(name, + "Variable (column) name. Click to edit."); + free(name); + + gtk_table_attach_defaults(GTK_TABLE(tab), box_of_label(field), + n_vars, n_vars+1, 0, 1); + label_in_box_bg(field, COLOR_VAR_PASSIVE); + g_signal_connect(G_OBJECT(box_of_label(field)), + "button_press_event", + G_CALLBACK(table_var_press_event), var); + g_signal_connect(G_OBJECT(box_of_label(field)), + "button_release_event", + G_CALLBACK(table_var_release_event), var); + g_signal_connect(G_OBJECT(box_of_label(field)), + "scroll_event", + G_CALLBACK(table_scroll_event), table); + var->widget = field; + + setup_var_drag(var); + + n_row = 0; + for (row = table->rows; row; row = row->next) { + value = row->values; + for (pos = 0; pos != n_var; pos++) + value = value->next; + expr = unparse(value->expr); + field = label_in_box_new(expr, + "Variable value. Click to select row or to edit."); + free(expr); + gtk_table_attach_defaults(GTK_TABLE(tab), + box_of_label(field), + n_vars, n_vars+1, + n_row+1, n_row+2); + label_in_box_bg(field, table->active_row == row ? + COLOR_ROW_SELECTED : COLOR_ROW_UNSELECTED); + g_signal_connect(G_OBJECT(box_of_label(field)), + "button_press_event", + G_CALLBACK(table_value_press_event), value); + g_signal_connect(G_OBJECT(box_of_label(field)), + "button_release_event", + G_CALLBACK(table_value_release_event), value); + g_signal_connect(G_OBJECT(box_of_label(field)), + "scroll_event", + G_CALLBACK(table_scroll_event), table); + value->widget = field; + setup_value_drag(value); + n_row++; + } + + /* + * Wrap tables wider than the screen area available for + * variables and tables. Don't wrap before having output at + * least one column. + */ + if (n_vars && get_widget_width(tab) > wrap_width) { + /* + * Resizing alone doesn't hide extra columns. We have + * to explicitly remove their content as well. + */ + gtk_container_remove(GTK_CONTAINER(tab), + box_of_label(var->widget)); + for (row = table->rows; row; row = row->next) { + value = row->values; + for (pos = 0; pos != n_var; pos++) + value = value->next; + gtk_container_remove(GTK_CONTAINER(tab), + box_of_label(value->widget)); + } + gtk_table_resize(GTK_TABLE(tab), n_rows, n_vars); + + sep = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), sep, + FALSE, FALSE, 1); + + n_vars = 0; + continue; + } + + var = var->next; + n_var++; + n_vars++; + } +} + + +/* ----- loops ------------------------------------------------------------- */ + + +static void set_loop_values(void *user, const struct value *values, + int n_values) +{ + struct loop *loop = user; + + switch (n_values) { + case 2: + if (loop->to.expr) + free_expr(loop->to.expr); + loop->to.expr = values->next->expr; + /* fall through */ + case 1: + if (loop->from.expr) + free_expr(loop->from.expr); + loop->from.expr = values->expr; + break; + case 0: + break; + default: + abort(); + } +} + + +static gboolean loop_var_select_event(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + struct loop *loop = data; + + switch (event->button) { + case 1: + edit_var(&loop->var, set_loop_values, loop, 2); + break; + case 3: + pop_up_loop_var(loop, event); + break; + } + return TRUE; +} + + +static gboolean loop_from_select_event(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + struct loop *loop = data; + + switch (event->button) { + case 1: + edit_nothing(); + edit_value(&loop->from, loop->var.frame); + break; + } + return TRUE; +} + + +static gboolean loop_to_select_event(GtkWidget *widget, + GdkEventButton *event, gpointer data) +{ + struct loop *loop = data; + + switch (event->button) { + case 1: + edit_nothing(); + edit_value(&loop->to, loop->var.frame); + break; + } + return TRUE; +} + + +static gboolean loop_select_event(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + struct loop *loop = data; + + switch (event->button) { + case 1: + loop->active = + (long) gtk_object_get_data(GTK_OBJECT(widget), "value"); + change_world(); + break; + } + return TRUE; +} + + +static gboolean loop_scroll_event(GtkWidget *widget, GdkEventScroll *event, + gpointer data) +{ + struct loop *loop = data; + + switch (event->direction) { + case GDK_SCROLL_UP: + if (loop->active < loop->iterations-1) { + loop->active++; + change_world(); + } + break; + case GDK_SCROLL_DOWN: + if (loop->active) { + loop->active--; + change_world(); + } + break; + default: + /* ignore */; + } + return TRUE; +} + + +static void build_loop(GtkWidget *vbox, struct frame *frame, + struct loop *loop) +{ + GtkWidget *hbox, *field, *label; + char *expr; + int i; + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + field = label_in_box_new(loop->var.name, + "Variable name. Click to edit."); + gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0); + label_in_box_bg(field, COLOR_VAR_PASSIVE); + if (instantiation_error == loop) + label_in_box_fg(field, COLOR_ITEM_ERROR); + g_signal_connect(G_OBJECT(box_of_label(field)), + "button_press_event", + G_CALLBACK(loop_var_select_event), loop); + loop->var.widget = field; + + gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" = "), + FALSE, FALSE, 0); + + expr = unparse(loop->from.expr); + field = label_in_box_new(expr, + "Start value of loop. Click to edit."); + free(expr); + gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0); + label_in_box_bg(field, COLOR_EXPR_PASSIVE); + g_signal_connect(G_OBJECT(box_of_label(field)), + "button_press_event", + G_CALLBACK(loop_from_select_event), loop); + loop->from.widget = field; + + gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" ... "), + FALSE, FALSE, 0); + + expr = unparse(loop->to.expr); + field = label_in_box_new(expr, "End value of loop. Click to edit."); + free(expr); + gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0); + label_in_box_bg(field, COLOR_EXPR_PASSIVE); + g_signal_connect(G_OBJECT(box_of_label(field)), + "button_press_event", + G_CALLBACK(loop_to_select_event), loop); + loop->to.widget = field; + + gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" ("), + FALSE, FALSE, 0); + + for (i = 0; i != loop->iterations; i++) { + label = add_activator(hbox, loop->active == i, + loop_select_event, loop, + "Loop value. Click to make active.", + "%g", loop->n+i); + gtk_object_set_data(GTK_OBJECT(box_of_label(label)), "value", + (gpointer) (long) i); + + g_signal_connect(G_OBJECT(box_of_label(label)), + "scroll_event", + G_CALLBACK(loop_scroll_event), loop); + } + + gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(")"), + FALSE, FALSE, 0); +} + + +/* ----- the list of variables, tables, and loops -------------------------- */ + + +static GtkWidget *build_vars(struct frame *frame, int wrap_width) +{ + GtkWidget *vbox; + struct table *table; + struct loop *loop; + + vbox = gtk_vbox_new(FALSE, 0); + for (table = frame->tables; table; table = table->next) { + add_sep(vbox, 3); + build_assignment(vbox, frame, table); + build_table(vbox, frame, table, wrap_width); + } + for (loop = frame->loops; loop; loop = loop->next) { + add_sep(vbox, 3); + build_loop(vbox, frame, loop); + } + return vbox; +} + + +/* ----- items ------------------------------------------------------------- */ + + +static void set_item_color(struct inst *inst, const char *color) +{ + GtkWidget *label; + + if (inst->vec) + label = inst->vec->list_widget; + else + label = inst->obj->list_widget; + if (label) + label_in_box_bg(box_of_label(label), color); +} + + +void gui_frame_select_inst(struct inst *inst) +{ + set_item_color(inst, COLOR_ITEM_SELECTED); +} + + +void gui_frame_deselect_inst(struct inst *inst) +{ + set_item_color(inst, COLOR_ITEM_NORMAL); +} + + +static gboolean item_select_vec(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + struct vec *vec = data; + + switch (event->button) { + case 1: + inst_select_vec(vec); + redraw(); + break; + } + return TRUE; +} + + +static gboolean item_select_obj(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + struct obj *obj = data; + + switch (event->button) { + case 1: + inst_select_obj(obj); + redraw(); + break; + } + return TRUE; +} + + +static GtkWidget *item_label(GtkWidget *tab, char *s, int col, int row, + gboolean (*cb)(GtkWidget *widget, GdkEventButton *event, gpointer data), + gpointer data) +{ + GtkWidget *label; + + label = label_in_box_new(s, "Click to select."); + gtk_misc_set_padding(GTK_MISC(label), 0, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_widget_modify_font(label, item_list_font); + gtk_table_attach_defaults(GTK_TABLE(tab), box_of_label(label), + col, col+1, row, row+1); + label_in_box_bg(box_of_label(label), COLOR_ITEM_NORMAL); + + if (cb) + g_signal_connect(G_OBJECT(box_of_label(label)), + "button_press_event", G_CALLBACK(cb), data); + + free(s); + return label; +} + + +static GtkWidget *build_items(struct frame *frame) +{ + GtkWidget *vbox, *hbox, *tab; + struct order *order, *item; + struct vec *vec; + struct obj *obj; + int n; + char *s, *t; + + n = 0; + for (vec = frame->vecs; vec; vec = vec->next) + n++; + for (obj = frame->objs; obj; obj = obj->next) + if (obj->type != ot_meas) + n++; + + vbox = gtk_vbox_new(FALSE, 0); + add_sep(vbox, 3); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + tab = gtk_table_new(n, 2, FALSE); + gtk_box_pack_start(GTK_BOX(hbox), tab, FALSE, FALSE, 0); + + order = order_frame(frame); + n = 0; + for (item = order; item->vec || item->obj; item++) { + if (item->obj) { + s = print_obj(item->obj, item->vec); + item->obj->list_widget = item_label(tab, s, 1, n, + item_select_obj, item->obj); + if (item->obj == instantiation_error) + label_in_box_fg(item->obj->list_widget, + COLOR_ITEM_ERROR); + } else { + t = stralloc_printf("%s: ", print_label(item->vec)); + item_label(tab, t, 0, n, NULL, NULL); + + s = print_vec(item->vec); + item->vec->list_widget = item_label(tab, s, 1, n, + item_select_vec, item->vec); + if (item->vec == instantiation_error) + label_in_box_fg(item->vec->list_widget, + COLOR_ITEM_ERROR); + } + n++; + } + free(order); + + return vbox; +} + + +static GtkWidget *build_meas(struct frame *frame) +{ + GtkWidget *vbox, *hbox, *tab; + struct obj *obj; + int n; + char *s; + + n = 0; + for (obj = frame->objs; obj; obj = obj->next) + if (obj->type == ot_meas) + n++; + + vbox = gtk_vbox_new(FALSE, 0); + add_sep(vbox, 3); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + tab = gtk_table_new(n, 2, FALSE); + gtk_box_pack_start(GTK_BOX(hbox), tab, FALSE, FALSE, 0); + + n = 0; + for (obj = frame->objs; obj; obj = obj->next) { + if (obj->type != ot_meas) + continue; + s = print_meas(obj); + obj->list_widget = item_label(tab, s, 0, n, + item_select_obj, obj); + if (obj == instantiation_error) + label_in_box_fg(obj->list_widget, COLOR_ITEM_ERROR); + n++; + } + + return vbox; +} + + +static void dont_build_items(struct frame *frame) +{ + struct vec *vec; + struct obj *obj; + + for (vec = frame->vecs; vec; vec = vec->next) + vec->list_widget = NULL; + for (obj = frame->objs; obj; obj = obj->next) + obj->list_widget = NULL; +} + + +/* ----- package name ------------------------------------------------------ */ + + +static int validate_pkg_name(const char *s, void *ctx) +{ + if (!*s) + return 0; + while (*s) { + if (*s < 32 || *s > 126) + return 0; + s++; + } + return 1; +} + +static void unselect_pkg_name(void *data) +{ + GtkWidget *widget = data; + + label_in_box_bg(widget, COLOR_PART_NAME); +} + + +static gboolean pkg_scroll_event(GtkWidget *widget, GdkEventScroll *event, + gpointer data) +{ + struct pkg *pkg, *last; + + switch (event->direction) { + case GDK_SCROLL_UP: + if (active_pkg->next) + active_pkg = active_pkg->next; + else + active_pkg = pkgs->next; + change_world(); + break; + case GDK_SCROLL_DOWN: + last = NULL; + for (pkg = pkgs->next; pkg && (!last || pkg != active_pkg); + pkg = pkg->next) + last = pkg; + active_pkg = last; + change_world(); + break; + default: + /* ignore */; + } + return TRUE; +} + + +static gboolean pkg_name_edit_event(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + switch (event->button) { + case 1: + inst_select_outside(widget, unselect_pkg_name); + label_in_box_bg(widget, COLOR_PART_NAME_EDITING); + status_set_type_entry(NULL, "package ="); + status_set_name("Package name (actual)", "%s", pkg_name); + edit_nothing(); + edit_name(&pkg_name, validate_pkg_name, NULL, + "Package name (template)"); + break; + } + return TRUE; +} + + +static GtkWidget *build_pkg_name(void) +{ + GtkWidget *label; + + label = label_in_box_new(pkg_name, + "Package name. (Template) Click to edit."); + gtk_misc_set_padding(GTK_MISC(label), 2, 2); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + + label_in_box_bg(label, COLOR_PART_NAME); + + g_signal_connect(G_OBJECT(box_of_label(label)), + "button_press_event", G_CALLBACK(pkg_name_edit_event), NULL); + g_signal_connect(G_OBJECT(box_of_label(label)), + "scroll_event", G_CALLBACK(pkg_scroll_event), NULL); + + return box_of_label(label); +} + + +/* ----- packages ---------------------------------------------------------- */ + + +static gboolean pkg_select_event(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + struct pkg *pkg = data; + + switch (event->button) { + case 1: + active_pkg = pkg; + /* @@@ we could actually skip instantiation here */ + change_world(); + break; + } + return TRUE; +} + + +static GtkWidget *build_pkg_names(void) +{ + GtkWidget *hbox; + struct pkg *pkg; + GtkWidget *field; + + hbox = gtk_hbox_new(FALSE, 0); + for (pkg = pkgs; pkg; pkg = pkg->next) + if (pkg->name) { + field = add_activator(hbox, pkg == active_pkg, + pkg_select_event, pkg, + "Package name. Click to make active.", + "%s", pkg->name); + g_signal_connect(G_OBJECT(box_of_label(field)), + "scroll_event", + G_CALLBACK(pkg_scroll_event), NULL); + } + return hbox; +} + + +/* ----- frame labels ------------------------------------------------------ */ + + +static int validate_frame_name(const char *s, void *ctx) +{ + struct frame *f; + + if (!is_id(s)) + return 0; + for (f = frames->next; f; f = f->next) + if (!strcmp(f->name, s)) + return 0; + return 1; +} + + +static void unselect_frame(void *data) +{ + struct frame *frame= data; + + /* + * "unselect" means in this context that the selection has moved + * elsewhere. However, this does not necessarily change the frame. + * (And, in fact, since we rebuild the frame list anyway, the color + * change here doesn't matter if selecting a different frame.) + * So we revert from "editing" to "selected". + */ + label_in_box_bg(frame->label, COLOR_FRAME_SELECTED); +} + + +static void edit_frame(struct frame *frame) +{ + const char *tip; + + inst_select_outside(frame, unselect_frame); + label_in_box_bg(frame->label, COLOR_FRAME_EDITING); + tip = "Frame name"; + status_set_type_entry(NULL, "name ="); + status_set_name(tip, "%s", frame->name); + edit_nothing(); + edit_unique(&frame->name, validate_frame_name, frame, tip); +} + + +void select_frame(struct frame *frame) +{ + if (active_frame) + label_in_box_bg(active_frame->label, COLOR_FRAME_UNSELECTED); + active_frame = frame; + change_world(); +} + + +static gboolean frame_press_event(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + struct frame *frame = data; + + switch (event->button) { + case 3: + pop_up_frame(frame, event); + return TRUE; + } + return FALSE; +} + + +static gboolean frame_release_event(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + struct frame *frame = data; + + switch (event->button) { + case 1: + if (is_dragging(frame)) + return FALSE; + if (active_frame != frame) { + select_frame(frame); + } else { + if (active_frame->name) { + edit_nothing(); + edit_frame(frame); + } + } + return TRUE; + } + return FALSE; +} + + +static GtkWidget *build_frame_label(struct frame *frame) +{ + GtkWidget *label; + + label = label_in_box_new(frame->name ? frame->name : "(root)", + frame->name ? "Frame name. Click to select or edit." : + "Root frame. Click to select."); + gtk_misc_set_padding(GTK_MISC(label), 2, 2); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + + label_in_box_bg(label, active_frame == frame ? + COLOR_FRAME_SELECTED : COLOR_FRAME_UNSELECTED); + + g_signal_connect(G_OBJECT(box_of_label(label)), + "button_press_event", G_CALLBACK(frame_press_event), frame); + g_signal_connect(G_OBJECT(box_of_label(label)), + "button_release_event", G_CALLBACK(frame_release_event), frame); + frame->label = label; + + if (frame != frames) + setup_frame_drag(frame); + + return box_of_label(label); +} + + +/* ----- frame references -------------------------------------------------- */ + + +static gboolean frame_ref_select_event(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + struct obj *obj = data; + + switch (event->button) { + case 1: + obj->u.frame.ref->active_ref = data; + change_world(); + break; + } + return TRUE; +} + + +static GtkWidget *build_frame_refs(const struct frame *frame) +{ + GtkWidget *hbox; + struct obj *obj; + char *tooltip; + + hbox = gtk_hbox_new(FALSE, 0); + for (obj = frame->objs; obj; obj = obj->next) + if (obj->type == ot_frame && + obj->u.frame.ref == active_frame) { + tooltip = stralloc_printf( + "Frame <b>%s</b> is referenced here. " + "Click to make active.", active_frame->name); + add_activator(hbox, + obj == obj->u.frame.ref->active_ref, + frame_ref_select_event, obj, + tooltip, + "%d", obj->u.frame.lineno); + free(tooltip); + } + return hbox; +} + + +/* ----- frames ------------------------------------------------------------ */ + + +void build_frames(GtkWidget *vbox, int wrap_width) +{ + struct frame *frame; + GtkWidget *hbox, *tab, *label, *packages, *refs, *vars, *items, *meas; + int n = 0; + int max_name_width, name_width; + + destroy_all_children(GTK_CONTAINER(vbox)); + for (frame = frames; frame; frame = frame->next) + n++; + + hbox = gtk_hbox_new(FALSE, 0); + + tab = gtk_table_new(n*2+3, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(tab), 1); + gtk_table_set_col_spacings(GTK_TABLE(tab), 1); + + gtk_box_pack_start(GTK_BOX(hbox), tab, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + label = build_pkg_name(); + gtk_table_attach_defaults(GTK_TABLE(tab), label, 0, 1, 0, 1); + max_name_width = get_widget_width(label); + + packages = build_pkg_names(); + gtk_table_attach_defaults(GTK_TABLE(tab), packages, 1, 2, 0, 1); + + n = 0; + for (frame = frames; frame; frame = frame->next) { + label = build_frame_label(frame); + gtk_table_attach_defaults(GTK_TABLE(tab), label, + 0, 1, n*2+1, n*2+2); + n++; + name_width = get_widget_width(label); + if (name_width > max_name_width) + max_name_width = name_width; + } + + wrap_width -= max_name_width+FRAME_AREA_MISC_WIDTH; + n = 0; + for (frame = frames; frame; frame = frame->next) { + refs = build_frame_refs(frame); + gtk_table_attach_defaults(GTK_TABLE(tab), refs, + 1, 2, n*2+1, n*2+2); + + if (show_vars) { + vars = build_vars(frame, wrap_width); + gtk_table_attach_defaults(GTK_TABLE(tab), vars, + 1, 2, n*2+2, n*2+3); + dont_build_items(frame); + } else { + items = build_items(frame); + gtk_table_attach_defaults(GTK_TABLE(tab), items, + 1, 2, n*2+2, n*2+3); + } + + n++; + } + + if (!show_vars) { + meas = build_meas(frames); + gtk_table_attach_defaults(GTK_TABLE(tab), meas, + 1, 2, n*2+2, n*2+3); + } + + gtk_widget_show_all(hbox); +} |