1
0
mirror of git://projects.qi-hardware.com/fped.git synced 2024-09-30 05:22:53 +03:00
fped/gui_status.c
Werner Almesberger 4c3dea7111 support switching variables between assignment and key (WIP)
This is a little awkward: to change a variable used as key to an
assignment, one first had to change the name such that it doesn't
clash, hit Enter, and then edit the variable again to change its
type.

Variable type changes should pick up the edit in progress and allow
a type change to also imply acceptance of the variable.
2012-05-28 02:54:50 -03:00

1108 lines
23 KiB
C

/*
* gui_status.c - GUI, status area
*
* Written 2009-2012 by Werner Almesberger
* Copyright 2009-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 <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "util.h"
#include "coord.h"
#include "error.h"
#include "unparse.h"
#include "obj.h"
#include "gui_util.h"
#include "gui_style.h"
#include "gui_canvas.h"
#include "gui_frame.h"
#include "gui.h"
#include "gui_status.h"
enum edit_status {
es_unchanged,
es_good,
es_bad,
};
struct edit_ops {
char *(*retrieve)(void *ctx);
enum edit_status (*status)(const char *s, void *ctx);
void (*store)(const char *s, void *ctx);
};
enum curr_unit curr_unit = curr_unit_mm;
static GtkWidget *open_edits = NULL;
static GtkWidget *last_edit = NULL;
/* ----- setter functions -------------------------------------------------- */
static GtkWidget *status_icon;
static GtkWidget *status_name, *status_entry;
static GtkWidget *status_type_x, *status_type_y, *status_type_entry;
static GtkWidget *status_box_x, *status_entry_y;
static GtkWidget *status_x, *status_y;
static GtkWidget *status_r, *status_angle;
static GtkWidget *status_sys_x, *status_sys_y;
static GtkWidget *status_user_x, *status_user_y;
static GtkWidget *status_zoom, *status_grid, *status_unit;
static GtkWidget *status_msg;
/* The x entry area serves multiple purposes */
static GtkWidget *status_entry_x;
static void set_label(GtkWidget *label, const char *tooltip,
const char *fmt, va_list ap)
{
char *s;
s = stralloc_vprintf(fmt, ap);
gtk_label_set_text(GTK_LABEL(label), s);
gtk_widget_set_tooltip_markup(label, tooltip);
free(s);
}
#define SETTER(name) \
void status_set_##name(const char *tooltip, const char *fmt, ...)\
{ \
va_list ap; \
\
va_start(ap, fmt); \
set_label(status_##name, tooltip, fmt, ap); \
va_end(ap); \
}
SETTER(type_x)
SETTER(type_y)
SETTER(type_entry)
SETTER(name)
SETTER(x)
SETTER(y)
SETTER(r)
SETTER(angle)
SETTER(sys_x)
SETTER(sys_y)
SETTER(user_x)
SETTER(user_y)
SETTER(zoom)
SETTER(grid)
SETTER(unit)
/* ----- set things with units --------------------------------------------- */
void set_with_units(void (*set)(const char *tooltip, const char *fmt, ...),
const char *prefix, unit_type u, const char *tooltip)
{
double n;
int mm;
switch (curr_unit) {
case curr_unit_mm:
n = units_to_mm(u);
mm = 1;
break;
case curr_unit_mil:
n = units_to_mil(u);
mm = 0;
break;
case curr_unit_auto:
n = units_to_best(u, &mm);
break;
default:
abort();
}
if (mm) {
/* -NNN.NNN mm */
set(tooltip, "%s" MM_FORMAT_FIXED " mm", prefix, n);
} else {
/* -NNNN.N mil */
set(tooltip, "%s" MIL_FORMAT_FIXED " mil", prefix, n);
}
}
/* ----- complex status updates -------------------------------------------- */
void status_set_icon(GtkWidget *image)
{
vacate_widget(status_icon);
if (image)
gtk_container_add(GTK_CONTAINER(status_icon), image);
gtk_widget_show_all(status_icon);
}
void status_set_xy(struct coord coord)
{
/* do dX/dY etc. stuff later */
status_set_type_x(NULL, "X =");
status_set_type_y(NULL, "Y =");
set_with_units(status_set_x, "", coord.x, "Width");
set_with_units(status_set_y, "", coord.y, "Height");
}
void status_set_angle_xy(const char *tooltip, struct coord v)
{
if (!v.x && !v.y)
status_set_angle(tooltip, "a = 0 deg");
else
status_set_angle(tooltip, "a = %3.1f deg", theta_vec(v));
}
static void entry_color(GtkWidget *widget, const char *color)
{
GdkColor col;
col = get_color(color);
gtk_widget_modify_base(widget, GTK_STATE_NORMAL, &col);
}
/* ----- variable type display and change ---------------------------------- */
static struct var *curr_var;
static GtkWidget *var_type;
static void show_var_type(void)
{
gtk_label_set_text(GTK_LABEL(var_type),
curr_var->key ? "key" : "assign");
}
static gboolean var_type_button_press_event(GtkWidget *widget,
GdkEventButton *event, gpointer data)
{
switch (event->button) {
case 1:
if (curr_var->key &&
find_var_in_frame(curr_var->frame, curr_var->name,
curr_var))
return TRUE;
curr_var->key = !curr_var->key;
show_var_type();
break;
}
/*
* We can't just redraw() here, because changing the variable type may
* also affect lots of other things. So we change the world and hope
* we end up selecting the same variable afterwards.
*/
change_world();
reselect_var(curr_var);
return TRUE;
}
void edit_var_type(struct var *var)
{
vacate_widget(status_box_x);
curr_var = var;
var_type = label_in_box_new(NULL, "Variable type. Click to cycle.");
gtk_container_add(GTK_CONTAINER(status_box_x), box_of_label(var_type));
label_in_box_bg(var_type, COLOR_SELECTOR);
g_signal_connect(G_OBJECT(box_of_label(var_type)),
"button_press_event", G_CALLBACK(var_type_button_press_event),
NULL);
show_var_type();
gtk_widget_show_all(status_box_x);
}
/* ----- pad type display and change --------------------------------------- */
static enum pad_type *curr_pad_type;
static GtkWidget *pad_type;
static void show_pad_type(void)
{
const char *s;
switch (*curr_pad_type) {
case pt_normal:
s = "normal";
break;
case pt_bare:
s = "bare";
break;
case pt_trace:
s = "trace";
break;
case pt_paste:
s = "paste";
break;
case pt_mask:
s = "mask";
break;
default:
abort();
}
gtk_label_set_text(GTK_LABEL(pad_type), s);
}
static gboolean pad_type_button_press_event(GtkWidget *widget,
GdkEventButton *event, gpointer data)
{
switch (event->button) {
case 1:
*curr_pad_type = (*curr_pad_type+1) % pt_n;
show_pad_type();
break;
}
/*
* We can't just redraw() here, because changing the pad type may also
* affect the visual stacking. So we change the world and hope we end
* up selecting the same pad afterwards.
*/
change_world_reselect();
return TRUE;
}
void edit_pad_type(enum pad_type *type)
{
vacate_widget(status_box_x);
curr_pad_type = type;
pad_type = label_in_box_new(NULL, "Pad type. Click to cycle.");
gtk_container_add(GTK_CONTAINER(status_box_x), box_of_label(pad_type));
label_in_box_bg(pad_type, COLOR_SELECTOR);
g_signal_connect(G_OBJECT(box_of_label(pad_type)),
"button_press_event", G_CALLBACK(pad_type_button_press_event),
NULL);
show_pad_type();
gtk_widget_show_all(status_box_x);
}
/* ----- edit helper functions --------------------------------------------- */
static void reset_edit(GtkWidget *widget)
{
struct edit_ops *ops;
void *ctx;
char *s;
ops = gtk_object_get_data(GTK_OBJECT(widget), "edit-ops");
ctx = gtk_object_get_data(GTK_OBJECT(widget), "edit-ctx");
assert(ops);
s = ops->retrieve(ctx);
gtk_object_set_data(GTK_OBJECT(widget), "edit-ops", NULL);
gtk_entry_set_text(GTK_ENTRY(widget), s);
free(s);
entry_color(widget, COLOR_EDIT_ASIS);
gtk_object_set_data(GTK_OBJECT(widget), "edit-ops", ops);
}
static void reset_edits(void)
{
GtkWidget *edit;
for (edit = open_edits; edit;
edit = gtk_object_get_data(GTK_OBJECT(edit), "edit-next"))
reset_edit(edit);
gtk_widget_grab_focus(GTK_WIDGET(open_edits));
}
static gboolean edit_key_press_event(GtkWidget *widget, GdkEventKey *event,
gpointer data)
{
GtkWidget *next = gtk_object_get_data(GTK_OBJECT(widget), "edit-next");
switch (event->keyval) {
case GDK_Tab:
gtk_widget_grab_focus(GTK_WIDGET(next ? next : open_edits));
return TRUE;
case GDK_Escape:
reset_edits();
return TRUE;
default:
return FALSE;
}
}
static void setup_edit(GtkWidget *widget, struct edit_ops *ops, void *ctx,
const char *tooltip)
{
gtk_object_set_data(GTK_OBJECT(widget), "edit-ops", ops);
gtk_object_set_data(GTK_OBJECT(widget), "edit-ctx", ctx);
gtk_object_set_data(GTK_OBJECT(widget), "edit-next", NULL);
reset_edit(widget);
if (!open_edits)
gtk_widget_grab_focus(GTK_WIDGET(widget));
gtk_widget_show(widget);
g_signal_connect(G_OBJECT(widget), "key_press_event",
G_CALLBACK(edit_key_press_event), open_edits);
if (last_edit)
gtk_object_set_data(GTK_OBJECT(last_edit), "edit-next", widget);
else
open_edits = widget;
last_edit = widget;
gtk_widget_set_tooltip_markup(widget, tooltip);
}
/* ----- identifier fields ------------------------------------------------- */
struct edit_unique_ctx {
const char **s;
int (*validate)(const char *s, void *ctx);
void *ctx;
};
/*
* Handle NULL so that we can also use it for unique_null
*/
static char *unique_retrieve(void *ctx)
{
struct edit_unique_ctx *unique_ctx = ctx;
return stralloc(*unique_ctx->s ? *unique_ctx->s : "");
}
static enum edit_status unique_status(const char *s, void *ctx)
{
const struct edit_unique_ctx *unique_ctx = ctx;
if (!strcmp(s, *unique_ctx->s))
return es_unchanged;
return !unique_ctx->validate ||
unique_ctx->validate(s, unique_ctx->ctx) ? es_good : es_bad;
}
static void unique_store(const char *s, void *ctx)
{
const struct edit_unique_ctx *unique_ctx = ctx;
*unique_ctx->s = unique(s);
}
static struct edit_ops edit_ops_unique = {
.retrieve = unique_retrieve,
.status = unique_status,
.store = unique_store,
};
void edit_unique(const char **s, int (*validate)(const char *s, void *ctx),
void *ctx, const char *tooltip)
{
static struct edit_unique_ctx unique_ctx;
unique_ctx.s = s;
unique_ctx.validate = validate;
unique_ctx.ctx = ctx;
setup_edit(status_entry, &edit_ops_unique, &unique_ctx, tooltip);
}
/* ----- identifier fields with NULL --------------------------------------- */
static enum edit_status unique_null_status(const char *s, void *ctx)
{
const struct edit_unique_ctx *unique_ctx = ctx;
if (!strcmp(s, *unique_ctx->s ? *unique_ctx->s : ""))
return es_unchanged;
if (!*s)
return es_good;
return !unique_ctx->validate ||
unique_ctx->validate(s, unique_ctx->ctx) ? es_good : es_bad;
}
static void unique_null_store(const char *s, void *ctx)
{
const struct edit_unique_ctx *unique_ctx = ctx;
if (!*s)
*unique_ctx->s = NULL;
else
*unique_ctx->s = unique(s);
}
static struct edit_ops edit_ops_null_unique = {
.retrieve = unique_retrieve,
.status = unique_null_status,
.store = unique_null_store,
};
void edit_unique_null(const char **s,
int (*validate)(const char *s, void *ctx), void *ctx, const char *tooltip)
{
static struct edit_unique_ctx unique_ctx;
unique_ctx.s = s;
unique_ctx.validate = validate;
unique_ctx.ctx = ctx;
setup_edit(status_entry, &edit_ops_null_unique, &unique_ctx, tooltip);
}
/* ----- unique field (variable) optionally followed by values ------------- */
struct edit_unique_with_values_ctx {
const char **s;
int (*validate)(const char *s, void *ctx);
void *ctx;
void (*set_values)(void *user, const struct value *values,
int n_values);
void *user;
int max_values;
};
static enum edit_status unique_with_values_status(const char *s, void *ctx)
{
const struct edit_unique_with_values_ctx *unique_ctx = ctx;
const char *id;
struct value *values;
int n;
if (!strcmp(s, *unique_ctx->s))
return es_unchanged;
status_begin_reporting();
n = parse_var(s, &id, &values, unique_ctx->max_values);
if (n < 0)
return es_bad;
free_values(values, 0);
return !unique_ctx->validate ||
unique_ctx->validate(id, unique_ctx->ctx) ? es_good : es_bad;
}
static void unique_with_values_store(const char *s, void *ctx)
{
const struct edit_unique_with_values_ctx *unique_ctx = ctx;
struct value *values;
int n;
status_begin_reporting();
n = parse_var(s, unique_ctx->s, &values, unique_ctx->max_values);
if (!n)
return;
assert(n >= 0);
assert(unique_ctx->max_values == -1 || n <= unique_ctx->max_values);
unique_ctx->set_values(unique_ctx->user, values, n);
free_values(values, 1);
}
static struct edit_ops edit_ops_unique_with_values = {
.retrieve = unique_retrieve,
.status = unique_with_values_status,
.store = unique_with_values_store,
};
void edit_unique_with_values(const char **s,
int (*validate)(const char *s, void *ctx), void *ctx,
void (*set_values)(void *user, const struct value *values, int n_values),
void *user, int max_values,
const char *tooltip)
{
static struct edit_unique_with_values_ctx unique_ctx;
unique_ctx.s = s;
unique_ctx.validate = validate;
unique_ctx.ctx = ctx;
unique_ctx.set_values = set_values;
unique_ctx.user = user;
unique_ctx.max_values = max_values;
setup_edit(status_entry, &edit_ops_unique_with_values, &unique_ctx,
tooltip);
}
/* ----- string fields ----------------------------------------------------- */
struct edit_name_ctx {
char **s;
int (*validate)(const char *s, void *ctx);
void *ctx;
};
static char *name_retrieve(void *ctx)
{
struct edit_name_ctx *name_ctx = ctx;
return stralloc(*name_ctx->s ? *name_ctx->s : "");
}
static enum edit_status name_status(const char *s, void *ctx)
{
const struct edit_name_ctx *name_ctx = ctx;
if (!strcmp(s, *name_ctx->s))
return es_unchanged;
return !name_ctx->validate || name_ctx->validate(s, name_ctx->ctx) ?
es_good : es_bad;
}
static void name_store(const char *s, void *ctx)
{
const struct edit_name_ctx *name_ctx = ctx;
free(*name_ctx->s);
*name_ctx->s = stralloc(s);
}
static struct edit_ops edit_ops_name = {
.retrieve = name_retrieve,
.status = name_status,
.store = name_store,
};
void edit_name(char **s, int (*validate)(const char *s, void *ctx), void *ctx,
const char *tooltip)
{
static struct edit_name_ctx name_ctx;
name_ctx.s = s;
name_ctx.validate = validate;
name_ctx.ctx = ctx;
setup_edit(status_entry, &edit_ops_name, &name_ctx, tooltip);
}
/* ----- expression fields ------------------------------------------------- */
static struct expr *try_parse_expr(const char *s)
{
status_begin_reporting();
return parse_expr(s);
}
static char *expr_retrieve(void *ctx)
{
struct expr **expr = ctx;
return unparse(*expr);
}
static enum edit_status expr_status(const char *s, void *ctx)
{
struct expr *expr;
expr = try_parse_expr(s);
if (!expr)
return es_bad;
free_expr(expr);
return es_good;
}
static void expr_store(const char *s, void *ctx)
{
struct expr **anchor = ctx;
struct expr *expr;
expr = try_parse_expr(s);
assert(expr);
if (*anchor)
free_expr(*anchor);
*anchor = expr;
}
static struct edit_ops edit_ops_expr = {
.retrieve = expr_retrieve,
.status = expr_status,
.store = expr_store,
};
void edit_expr(struct expr **expr, const char *tooltip)
{
setup_edit(status_entry, &edit_ops_expr, expr, tooltip);
}
/* ----- distance expressions ---------------------------------------------- */
static void dist_expr_store(const char *s, void *ctx)
{
struct expr **anchor = ctx;
struct expr *expr;
expr_store(s, ctx);
expr = *anchor;
if (expr->op == op_num && !expr->u.num.exponent && !expr->u.num.n)
expr->u.num.exponent = 1;
}
static struct edit_ops edit_ops_dist_expr = {
.retrieve = expr_retrieve,
.status = expr_status,
.store = dist_expr_store,
};
static void edit_any_dist_expr(GtkWidget *widget, struct expr **expr,
const char *tooltip)
{
setup_edit(widget, &edit_ops_dist_expr, expr, tooltip);
}
void edit_dist_expr(struct expr **expr, const char *tooltip)
{
edit_any_dist_expr(status_entry, expr, tooltip);
}
void edit_x(struct expr **expr, const char *tooltip)
{
vacate_widget(status_box_x);
gtk_container_add(GTK_CONTAINER(status_box_x), status_entry_x);
gtk_widget_show(status_box_x);
edit_any_dist_expr(status_entry_x, expr, tooltip);
}
void edit_y(struct expr **expr, const char *tooltip)
{
edit_any_dist_expr(status_entry_y, expr, tooltip);
}
/* ----- expression list --------------------------------------------------- */
struct edit_expr_list_ctx {
struct expr *expr;
void (*set_values)(void *user, const struct value *values,
int n_values);
void *user;
};
static char *expr_list_retrieve(void *ctx)
{
struct edit_expr_list_ctx *expr_list_ctx = ctx;
return unparse(expr_list_ctx->expr);
}
static enum edit_status expr_list_status(const char *s, void *ctx)
{
struct value *values;
int n;
status_begin_reporting();
n = parse_values(s, &values);
if (n < 0)
return es_bad;
free_values(values, 0);
return es_good;
}
static void expr_list_store(const char *s, void *ctx)
{
struct edit_expr_list_ctx *expr_list_ctx = ctx;
struct value *values;
int n;
status_begin_reporting();
n = parse_values(s, &values);
assert(n >= 0);
expr_list_ctx->set_values(expr_list_ctx->user, values, n);
free_values(values, 1);
}
static struct edit_ops edit_ops_expr_list = {
.retrieve = expr_list_retrieve,
.status = expr_list_status,
.store = expr_list_store,
};
void edit_expr_list(struct expr *expr,
void (*set_values)(void *user, const struct value *values, int n_values),
void *user, const char *tooltip)
{
static struct edit_expr_list_ctx expr_list_ctx;
expr_list_ctx.expr = expr;
expr_list_ctx.set_values = set_values;
expr_list_ctx.user = user;
setup_edit(status_entry, &edit_ops_expr_list, &expr_list_ctx, tooltip);
}
/* ----- text entry -------------------------------------------------------- */
static enum edit_status get_status(GtkWidget *widget)
{
struct edit_ops *ops;
void *ctx;
const char *s;
ops = gtk_object_get_data(GTK_OBJECT(widget), "edit-ops");
if (!ops)
return es_unchanged;
ctx = gtk_object_get_data(GTK_OBJECT(widget), "edit-ctx");
s = gtk_entry_get_text(GTK_ENTRY(widget));
return ops->status(s, ctx);
}
static void set_edit(GtkWidget *widget)
{
struct edit_ops *ops;
void *ctx;
const char *s;
ops = gtk_object_get_data(GTK_OBJECT(widget), "edit-ops");
if (!ops)
return;
ctx = gtk_object_get_data(GTK_OBJECT(widget), "edit-ctx");
s = gtk_entry_get_text(GTK_ENTRY(widget));
if (ops->store)
ops->store(s, ctx);
}
static gboolean changed(GtkWidget *widget, GdkEventMotion *event,
gpointer data)
{
switch (get_status(widget)) {
case es_unchanged:
entry_color(widget, COLOR_EDIT_ASIS);
break;
case es_good:
entry_color(widget, COLOR_EDIT_GOOD);
break;
case es_bad:
entry_color(widget, COLOR_EDIT_BAD);
break;
default:
abort();
}
return TRUE;
}
static gboolean activate(GtkWidget *widget, GdkEventMotion *event,
gpointer data)
{
GtkWidget *edit;
enum edit_status status;
int unchanged = 1;
for (edit = open_edits; edit;
edit = gtk_object_get_data(GTK_OBJECT(edit), "edit-next")) {
status = get_status(edit);
if (status == es_bad)
return TRUE;
if (status == es_good)
unchanged = 0;
}
if (unchanged) {
inst_deselect();
return TRUE;
}
for (edit = open_edits; edit;
edit = gtk_object_get_data(GTK_OBJECT(edit), "edit-next"))
if (get_status(edit) == es_good) {
entry_color(edit, COLOR_EDIT_ASIS);
set_edit(edit);
}
inst_deselect();
change_world();
return TRUE;
}
void edit_nothing(void)
{
gtk_widget_hide(status_entry);
gtk_widget_hide(status_box_x);
gtk_widget_hide(status_entry_y);
open_edits = NULL;
last_edit = NULL;
}
/* ----- status reports ---------------------------------------------------- */
static gint context_id;
static int have_msg = 0;
static void clear_status_msg(void)
{
if (have_msg) {
gtk_statusbar_pop(GTK_STATUSBAR(status_msg), context_id);
have_msg = 0;
}
}
static void report_to_gui(const char *s)
{
if (!have_msg)
gtk_statusbar_push(GTK_STATUSBAR(status_msg), context_id, s);
have_msg = 1;
}
void status_begin_reporting(void)
{
clear_status_msg();
reporter = report_to_gui;
}
/* ----- unit selection ---------------------------------------------------- */
static void show_curr_unit(void)
{
static const char *tip = "Display unit. Click to cycle.";
switch (curr_unit) {
case curr_unit_mm:
status_set_unit(tip, "mm");
break;
case curr_unit_mil:
status_set_unit(tip, "mil");
break;
case curr_unit_auto:
status_set_unit(tip, "auto");
break;
default:
abort();
}
}
static gboolean unit_button_press_event(GtkWidget *widget,
GdkEventButton *event, gpointer data)
{
switch (event->button) {
case 1:
curr_unit = (curr_unit+1) % curr_unit_n;
show_curr_unit();
break;
}
refresh_pos();
change_world();
return TRUE;
}
/* ----- setup ------------------------------------------------------------- */
static GtkWidget *add_vbox(GtkWidget *tab, int col, int row)
{
GtkWidget *vbox;
vbox = gtk_vbox_new(FALSE, 0);
gtk_table_attach_defaults(GTK_TABLE(tab), vbox,
col, col+1, row, row+1);
return vbox;
}
static GtkWidget *add_label_basic(GtkWidget *tab, int col, int row)
{
GtkWidget *label;
label = label_in_box_new(NULL, NULL);
gtk_table_attach(GTK_TABLE(tab), box_of_label(label),
col, col+1, row, row+1,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 1);
/* 0 , 1 - padding */
return label;
}
static GtkWidget *add_label(GtkWidget *tab, int col, int row)
{
GtkWidget *label;
label = add_label_basic(tab, col, row);
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
gtk_label_set_selectable(GTK_LABEL(label), TRUE);
return label;
}
static GtkWidget *make_entry(void)
{
GtkWidget *entry;
entry = gtk_entry_new();
gtk_entry_set_has_frame(GTK_ENTRY(entry), FALSE);
g_signal_connect(G_OBJECT(entry), "changed",
G_CALLBACK(changed), entry);
g_signal_connect(G_OBJECT(entry), "activate",
G_CALLBACK(activate), entry);
return entry;
}
static GtkWidget *add_entry(GtkWidget *tab, int col, int row)
{
GtkWidget *entry;
entry = make_entry();
gtk_table_attach_defaults(GTK_TABLE(tab), entry,
col, col+1, row, row+1);
return entry;
}
void make_status_area(GtkWidget *vbox)
{
GtkWidget *tab, *sep;
GtkWidget *hbox, *vbox2;
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
vbox2 = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), vbox2, FALSE, FALSE, 1);
status_icon = gtk_event_box_new();
gtk_box_pack_start(GTK_BOX(vbox2), status_icon, FALSE, FALSE, 0);
tab = gtk_table_new(7, 3, FALSE);
gtk_box_pack_start(GTK_BOX(hbox), tab, TRUE, TRUE, 0);
/* types */
status_type_x = add_label(tab, 0, 0);
status_type_y = add_label(tab, 0, 1);
status_type_entry = add_label(tab, 0, 2);
/* x / y */
status_x = add_label(tab, 1, 0);
status_box_x = add_vbox(tab, 2, 0);
status_y = add_label(tab, 1, 1);
status_entry_y = add_entry(tab, 2, 1);
status_entry_x = gtk_widget_ref(make_entry());
/* name and input */
status_name = add_label(tab, 1, 2);
status_entry = add_entry(tab, 2, 2);
/* separator */
sep = gtk_vseparator_new();
gtk_table_attach_defaults(GTK_TABLE(tab), sep, 3, 4, 0, 3);
/* sys / user pos */
status_sys_x = add_label(tab, 4, 0);
status_sys_y = add_label(tab, 4, 1);
status_user_x = add_label(tab, 5, 0);
status_user_y = add_label(tab, 5, 1);
/* r / angle */
status_r = add_label(tab, 4, 2);
status_angle = add_label(tab, 5, 2);
/* zoom / grid / unit */
status_zoom = add_label(tab, 6, 0);
status_grid = add_label(tab, 6, 1);
status_unit = add_label_basic(tab, 6, 2);
/* unit selection */
label_in_box_bg(status_unit, COLOR_SELECTOR);
show_curr_unit();
g_signal_connect(G_OBJECT(box_of_label(status_unit)),
"button_press_event", G_CALLBACK(unit_button_press_event), NULL);
/* message bar */
status_msg = gtk_statusbar_new();
gtk_box_pack_start(GTK_BOX(vbox), status_msg, FALSE, FALSE, 0);
context_id = gtk_statusbar_get_context_id(GTK_STATUSBAR(status_msg),
"messages");
}
void cleanup_status_area(void)
{
gtk_widget_unref(status_entry_x);
}