Root/gui_frame.c

Source at commit 73389fba76766394f0fa11c9e48f68b5913099af created 4 years 9 months ago.
By Werner Almesberger, sanitize show var/code handling; switch to enum for future changes
1/*
2 * gui_frame.c - GUI, frame window
3 *
4 * Written 2009, 2010, 2012, 2015 by Werner Almesberger
5 * Copyright 2009, 2010, 2012, 2015 by Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13
14#include <string.h>
15#include <gtk/gtk.h>
16
17#include "util.h"
18#include "error.h"
19#include "dump.h"
20#include "inst.h"
21#include "obj.h"
22#include "delete.h"
23#include "unparse.h"
24#include "gui_util.h"
25#include "gui_style.h"
26#include "gui_status.h"
27#include "gui_tool.h"
28#include "gui_canvas.h"
29#include "gui.h"
30#include "gui_frame_drag.h"
31#include "gui_frame.h"
32
33
34enum sidebar sidebar = sidebar_var;
35
36
37/* ----- add elements, shared ---------------------------------------------- */
38
39
40/* @@@ merge with fpd.y */
41
42static void add_table(struct frame *frame, struct table **anchor)
43{
44    struct table *table, **walk;
45
46    table = zalloc_type(struct table);
47    table->vars = zalloc_type(struct var);
48    table->vars->name = unique("_");
49    table->vars->frame = frame;
50    table->vars->table = table;
51    table->rows = zalloc_type(struct row);
52    table->rows->table = table;
53    table->rows->values = zalloc_type(struct value);
54    table->rows->values->expr = parse_expr("0");
55    table->rows->values->row = table->rows;
56    table->active_row = table->rows;
57    if (anchor) {
58        table->next = *anchor;
59        *anchor = table;
60    } else {
61        for (walk = &frame->tables; *walk; walk = &(*walk)->next);
62        *walk = table;
63    }
64    change_world();
65}
66
67
68static void add_loop(struct frame *frame, struct loop **anchor)
69{
70    struct loop *loop, **walk;
71
72    loop = zalloc_type(struct loop);
73    loop->var.name = unique("_");
74    loop->var.frame = frame;
75    loop->from.expr = parse_expr("0");
76    loop->to.expr = parse_expr("0");
77    if (anchor) {
78        loop->next = *anchor;
79        *anchor = loop;
80    } else {
81        loop->next = NULL;
82        for (walk = &frame->loops; *walk; walk = &(*walk)->next);
83        *walk = loop;
84    }
85    change_world();
86}
87
88
89/* ----- popup dispatcher -------------------------------------------------- */
90
91
92static void *popup_data;
93
94
95static void pop_up(GtkWidget *menu, GdkEventButton *event, void *data)
96{
97    popup_data = data;
98    gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
99        event->button, event->time);
100}
101
102
103/* ----- popup: frame ------------------------------------------------------ */
104
105
106static GtkItemFactory *factory_frame;
107static GtkWidget *popup_frame_widget;
108
109
110static void popup_add_frame(void)
111{
112    struct frame *parent = popup_data;
113    struct frame *new;
114
115    new = zalloc_type(struct frame);
116    new->name = unique("_");
117    new->next = parent->next;
118    parent->next = new;
119    change_world();
120}
121
122
123static void popup_del_frame(void)
124{
125    struct frame *frame = popup_data;
126
127    assert(frame != frames);
128    delete_frame(frame);
129    if (active_frame == frame)
130        select_frame(frames);
131    change_world();
132}
133
134
135static void popup_add_table(void)
136{
137    add_table(popup_data, NULL);
138}
139
140
141static void popup_add_loop(void)
142{
143    add_loop(popup_data, NULL);
144}
145
146
147static GtkItemFactoryEntry popup_frame_entries[] = {
148    { "/Add frame", NULL, popup_add_frame,0, "<Item>" },
149    { "/sep0", NULL, NULL, 0, "<Separator>" },
150    { "/Add variable", NULL, popup_add_table,0, "<Item>" },
151    { "/Add loop", NULL, popup_add_loop, 0, "<Item>" },
152    { "/sep1", NULL, NULL, 0, "<Separator>" },
153    { "/Delete frame", NULL, popup_del_frame,0, "<Item>" },
154    { "/sep2", NULL, NULL, 0, "<Separator>" },
155    { "/Close", NULL, NULL, 0, "<Item>" },
156    { NULL }
157};
158
159
160static gboolean can_add_frame(void)
161{
162    const struct frame *frame;
163
164    for (frame = frames->next; frame; frame = frame->next)
165        if (!strcmp(frame->name, "_"))
166            return FALSE;
167    return TRUE;
168}
169
170
171static gboolean can_add_var(const struct frame *frame)
172{
173    const struct table *table;
174    const struct var *var;
175    const struct loop *loop;
176
177    for (table = frame->tables; table; table = table->next)
178        for (var = table->vars; var; var = var->next)
179            if (!strcmp(var->name, "_"))
180                return FALSE;
181    for (loop = frame->loops; loop; loop = loop->next)
182        if (!strcmp(loop->var.name, "_"))
183            return FALSE;
184    return TRUE;
185}
186
187
188static void enable_add_var(struct frame *frame, GtkItemFactory *factory)
189{
190    gboolean add_var;
191
192    add_var = can_add_var(frame);
193    gtk_widget_set_sensitive(
194        gtk_item_factory_get_item(factory, "/Add variable"), add_var);
195    gtk_widget_set_sensitive(
196        gtk_item_factory_get_item(factory, "/Add loop"), add_var);
197}
198
199
200static void pop_up_frame(struct frame *frame, GdkEventButton *event)
201{
202    gtk_widget_set_sensitive(
203        gtk_item_factory_get_item(factory_frame, "/Delete frame"),
204        frame != frames);
205
206    gtk_widget_set_sensitive(
207        gtk_item_factory_get_item(factory_frame, "/Add frame"),
208        can_add_frame());
209
210    enable_add_var(frame, factory_frame);
211
212    pop_up(popup_frame_widget, event, frame);
213}
214
215
216/* ----- popup: single variable -------------------------------------------- */
217
218
219static GtkItemFactory *factory_single_var;
220static GtkWidget *popup_single_var_widget;
221
222
223static void add_row_here(struct table *table, struct row **anchor)
224{
225    struct row *row;
226    const struct value *walk;
227    struct value *value;
228
229    row = zalloc_type(struct row);
230    row->table = table;
231    /* @@@ future: adjust type */
232    for (walk = table->rows->values; walk; walk = walk->next) {
233        value = zalloc_type(struct value);
234        value->expr = parse_expr("0");
235        value->row = row;
236        value->next = row->values;
237        row->values = value;
238    }
239    row->next = *anchor;
240    *anchor = row;
241    change_world();
242}
243
244
245static void add_column_here(struct table *table, struct var **anchor)
246{
247    const struct var *walk;
248    struct var *var;
249    struct row *row;
250    struct value *value;
251    struct value **value_anchor;
252    int n = 0, i;
253
254    for (walk = table->vars; walk != *anchor; walk = walk->next)
255        n++;
256    var = zalloc_type(struct var);
257    var->name = unique("_");
258    var->frame = table->vars->frame;
259    var->table = table;
260    var->next = *anchor;
261    *anchor = var;
262    for (row = table->rows; row; row = row->next) {
263        value_anchor = &row->values;
264        for (i = 0; i != n; i++)
265            value_anchor = &(*value_anchor)->next;
266        value = zalloc_type(struct value);
267        value->expr = parse_expr("0");
268        value->row = row;
269        value->next = *value_anchor;
270        *value_anchor = value;
271    }
272    change_world();
273}
274
275
276static void popup_add_row(void)
277{
278    struct var *var = popup_data;
279
280    add_row_here(var->table, &var->table->rows);
281}
282
283
284static void popup_add_column(void)
285{
286    struct var *var = popup_data;
287
288    add_column_here(var->table, &var->next);
289}
290
291
292static void popup_del_table(void)
293{
294    struct var *var = popup_data;
295
296    delete_table(var->table);
297    change_world();
298}
299
300
301static void popup_add_table_from_var(void)
302{
303    struct var *var = popup_data;
304
305    add_table(var->frame, &var->table->next);
306}
307
308
309static void popup_add_loop_from_var(void)
310{
311    struct var *var = popup_data;
312
313    add_loop(var->frame, NULL);
314}
315
316
317static GtkItemFactoryEntry popup_single_var_entries[] = {
318    { "/Add row", NULL, popup_add_row, 0, "<Item>" },
319    { "/Add column", NULL, popup_add_column, 0, "<Item>" },
320    { "/sep1", NULL, NULL, 0, "<Separator>" },
321    { "/Delete variable", NULL, popup_del_table,0, "<Item>" },
322    { "/sep2", NULL, NULL, 0, "<Separator>" },
323    { "/Add variable", NULL, popup_add_table_from_var,
324                            0, "<Item>" },
325    { "/Add loop", NULL, popup_add_loop_from_var,
326                            0, "<Item>" },
327    { "/sep3", NULL, NULL, 0, "<Separator>" },
328    { "/Close", NULL, NULL, 0, "<Item>" },
329    { NULL }
330};
331
332
333static void pop_up_single_var(struct var *var, GdkEventButton *event)
334{
335    gtk_widget_set_sensitive(
336        gtk_item_factory_get_item(factory_single_var, "/Add column"),
337        can_add_var(var->frame));
338    enable_add_var(var->frame, factory_single_var);
339    pop_up(popup_single_var_widget, event, var);
340}
341
342
343/* ----- popup: table variable --------------------------------------------- */
344
345
346static GtkItemFactory *factory_table_var;
347static GtkWidget *popup_table_var_widget;
348
349
350static void popup_del_column(void)
351{
352    struct var *var = popup_data;
353    const struct var *walk;
354    int n = 0;
355
356    for (walk = var->table->vars; walk != var; walk = walk->next)
357        n++;
358    delete_column(var->table, n);
359    change_world();
360}
361
362
363static GtkItemFactoryEntry popup_table_var_entries[] = {
364    { "/Add row", NULL, popup_add_row, 0, "<Item>" },
365    { "/Add column", NULL, popup_add_column, 0, "<Item>" },
366    { "/sep1", NULL, NULL, 0, "<Separator>" },
367    { "/Delete table", NULL, popup_del_table,0, "<Item>" },
368    { "/Delete column", NULL, popup_del_column, 0, "<Item>" },
369    { "/sep2", NULL, NULL, 0, "<Separator>" },
370    { "/Add variable", NULL, popup_add_table_from_var,
371                            0, "<Item>" },
372    { "/Add loop", NULL, popup_add_loop_from_var,
373                            0, "<Item>" },
374    { "/sep3", NULL, NULL, 0, "<Separator>" },
375    { "/Close", NULL, NULL, 0, "<Item>" },
376    { NULL }
377};
378
379
380static void pop_up_table_var(struct var *var, GdkEventButton *event)
381{
382    gtk_widget_set_sensitive(
383        gtk_item_factory_get_item(factory_table_var, "/Delete column"),
384        var->table->vars->next != NULL);
385    gtk_widget_set_sensitive(
386        gtk_item_factory_get_item(factory_table_var, "/Add column"),
387        can_add_var(var->frame));
388    enable_add_var(var->frame, factory_table_var);
389    pop_up(popup_table_var_widget, event, var);
390}
391
392
393/* ----- popup: table value ------------------------------------------------ */
394
395
396static GtkItemFactory *factory_table_value;
397static GtkWidget *popup_table_value_widget;
398
399
400static void popup_add_column_by_value(void)
401{
402    struct value *value = popup_data;
403    const struct value *walk;
404    struct table *table = value->row->table;
405    struct var *var = table->vars;
406
407    for (walk = value->row->values; walk != value; walk = walk->next)
408        var = var->next;
409    add_column_here(table, &var->next);
410}
411
412
413static void popup_add_row_by_value(void)
414{
415    struct value *value = popup_data;
416
417    add_row_here(value->row->table, &value->row->next);
418}
419
420
421static void popup_del_row(void)
422{
423    struct value *value = popup_data;
424    struct table *table = value->row->table;
425
426    delete_row(value->row);
427    if (table->active_row == value->row)
428        table->active_row = table->rows;
429    change_world();
430}
431
432
433static void popup_del_column_by_value(void)
434{
435    struct value *value = popup_data;
436    const struct value *walk;
437    int n = 0;
438
439    for (walk = value->row->values; walk != value; walk = walk->next)
440        n++;
441    delete_column(value->row->table, n);
442    change_world();
443}
444
445
446static GtkItemFactoryEntry popup_table_value_entries[] = {
447    { "/Add row", NULL, popup_add_row_by_value, 0, "<Item>" },
448    { "/Add column", NULL, popup_add_column_by_value,
449                            0, "<Item>" },
450    { "/sep1", NULL, NULL, 0, "<Separator>" },
451    { "/Delete row", NULL, popup_del_row, 0, "<Item>" },
452    { "/Delete column", NULL, popup_del_column_by_value,
453                            0, "<Item>" },
454    { "/sep2", NULL, NULL, 0, "<Separator>" },
455    { "/Close", NULL, NULL, 0, "<Item>" },
456    { NULL }
457};
458
459
460static void pop_up_table_value(struct value *value, GdkEventButton *event)
461{
462    gtk_widget_set_sensitive(
463        gtk_item_factory_get_item(factory_table_value, "/Delete row"),
464        value->row->table->rows->next != NULL);
465    gtk_widget_set_sensitive(
466        gtk_item_factory_get_item(factory_table_value, "/Delete column"),
467        value->row->table->vars->next != NULL);
468    pop_up(popup_table_value_widget, event, value);
469}
470
471
472/* ----- popup: loop ------------------------------------------------------- */
473
474
475static GtkItemFactory *factory_loop_var;
476static GtkWidget *popup_loop_var_widget;
477
478
479static void popup_del_loop(void)
480{
481    struct loop *loop = popup_data;
482
483    delete_loop(loop);
484    change_world();
485}
486
487
488static void popup_add_table_from_loop(void)
489{
490    struct loop *loop = popup_data;
491
492    add_table(loop->var.frame, NULL);
493}
494
495
496static void popup_add_loop_from_loop(void)
497{
498    struct loop *loop = popup_data;
499
500    add_loop(loop->var.frame, &loop->next);
501}
502
503
504static GtkItemFactoryEntry popup_loop_var_entries[] = {
505    { "/Delete loop", NULL, popup_del_loop, 0, "<Item>" },
506    { "/sep1", NULL, NULL, 0, "<Separator>" },
507    { "/Add variable", NULL, popup_add_table_from_loop,
508                            0, "<Item>" },
509    { "/Add loop", NULL, popup_add_loop_from_loop,
510                            0, "<Item>" },
511    { "/sep2", NULL, NULL, 0, "<Separator>" },
512    { "/Close", NULL, NULL, 0, "<Item>" },
513    { NULL }
514};
515
516
517static void pop_up_loop_var(struct loop *loop, GdkEventButton *event)
518{
519    enable_add_var(loop->var.frame, factory_loop_var);
520    pop_up(popup_loop_var_widget, event, loop);
521}
522
523
524/* ----- make popups ------------------------------------------------------- */
525
526
527static GtkWidget *make_popup(const char *name, GtkItemFactory **factory,
528    GtkItemFactoryEntry *entries)
529{
530    GtkWidget *popup;
531    int n;
532
533    n = 0;
534    for (n = 0; entries[n].path; n++);
535
536    *factory = gtk_item_factory_new(GTK_TYPE_MENU, name, NULL);
537    gtk_item_factory_create_items(*factory, n, entries, NULL);
538    popup = gtk_item_factory_get_widget(*factory, name);
539    return popup;
540}
541
542
543void make_popups(void)
544{
545    popup_frame_widget = make_popup("<FpedFramePopUp>",
546        &factory_frame, popup_frame_entries);
547    popup_single_var_widget = make_popup("<FpedSingleVarPopUp>",
548        &factory_single_var, popup_single_var_entries);
549    popup_table_var_widget = make_popup("<FpedTableVarPopUp>",
550        &factory_table_var, popup_table_var_entries);
551    popup_table_value_widget = make_popup("<FpedTableValusPopUp>",
552        &factory_table_value, popup_table_value_entries);
553    popup_loop_var_widget = make_popup("<FpedLoopVarPopUp>",
554        &factory_loop_var, popup_loop_var_entries);
555}
556
557
558/* ----- variable list ----------------------------------------------------- */
559
560
561static void add_sep(GtkWidget *box, int size)
562{
563    GtkWidget *sep;
564    GdkColor black = { 0, 0, 0, 0 };
565
566    sep = gtk_drawing_area_new();
567    gtk_box_pack_start(GTK_BOX(box), sep, FALSE, TRUE, size);
568    gtk_widget_modify_bg(sep, GTK_STATE_NORMAL, &black);
569}
570
571
572/* ----- variable name editor ---------------------------------------------- */
573
574
575static int find_var_in_frame(const struct frame *frame, const char *name,
576    const struct var *self)
577{
578    const struct table *table;
579    const struct loop *loop;
580    const struct var *var;
581
582    for (table = frame->tables; table; table = table->next)
583        for (var = table->vars; var; var = var->next)
584            if (var != self && !var->key &&
585                !strcmp(var->name, name))
586                return 1;
587    for (loop = frame->loops; loop; loop = loop->next)
588        if (&loop->var != self && !strcmp(loop->var.name, name))
589            return 1;
590    return 0;
591}
592
593
594static int validate_var_name(const char *s, void *ctx)
595{
596    struct var *var = ctx;
597
598    if (!is_id(s))
599        return 0;
600    if (var->key)
601        return 1;
602    return !find_var_in_frame(var->frame, s, var);
603}
604
605
606static void unselect_var(void *data)
607{
608    struct var *var = data;
609
610    label_in_box_bg(var->widget, COLOR_VAR_PASSIVE);
611}
612
613
614static void show_value(const struct expr *expr, const struct frame *frame)
615{
616    const char *value_string;
617    struct num value;
618
619    status_set_type_x(NULL, "value =");
620    value_string = eval_str(expr, frame);
621    if (value_string) {
622        status_set_x(NULL, "\"%s\"", value_string);
623    } else {
624        value = eval_num(expr, frame);
625        if (is_undef(value))
626            status_set_x(NULL, "undefined");
627        else
628            status_set_x(NULL, "%lg%s", value.n, str_unit(value));
629    }
630}
631
632
633static void show_var_value(const struct var *var, const struct frame *frame)
634{
635    const struct var *walk;
636    const struct value *value;
637
638    if (!var->table)
639        return;
640    value = var->table->active_row->values;
641    for (walk = var->table->vars; walk != var; walk = walk->next)
642        value = value->next;
643    show_value(value->expr, frame);
644}
645
646
647static void edit_var(struct var *var,
648    void (*set_values)(void *user, const struct value *values, int n_values),
649    void *user, int max_values)
650{
651    inst_select_outside(var, unselect_var);
652    label_in_box_bg(var->widget, COLOR_VAR_EDITING);
653    status_set_type_entry(NULL, "name =");
654    status_set_name("Variable name", "%s", var->name);
655    show_var_value(var, var->frame);
656    edit_nothing();
657    edit_var_type(var);
658    edit_unique_with_values(&var->name, validate_var_name, var,
659        set_values, user, max_values,
660        "Variable name. "
661        "Shortcut:<b><i>name</i>=<i>value</i>,<i>...</i> </b>");
662}
663
664
665static void set_col_values(void *user, const struct value *values,
666    int n_values);
667
668
669void reselect_var(struct var *var)
670{
671    edit_var(var, set_col_values, var, -1);
672}
673
674
675/* ----- value editor ------------------------------------------------------ */
676
677
678static void unselect_value(void *data)
679{
680    struct value *value = data;
681
682    /*
683     * This condition is a little cryptic. Here is what it does:
684     *
685     * IF table/assignment (not loop)
686     * AND the current row is the active (selected) row
687     * AND it's an assignment (not a table).
688     *
689     * We need the last condition because the expressions of assignments
690     * are drawn with COLOR_EXPR_PASSIVE. (See build_assignment.)
691     */
692    label_in_box_bg(value->widget,
693        value->row && value->row->table->active_row == value->row &&
694        (value->row->table->rows->next || value->row->table->vars->next) ?
695         COLOR_CHOICE_SELECTED : COLOR_EXPR_PASSIVE);
696}
697
698
699static void edit_value(struct value *value, const struct frame *frame)
700{
701    inst_select_outside(value, unselect_value);
702    label_in_box_bg(value->widget, COLOR_EXPR_EDITING);
703    show_value(value->expr, frame);
704    edit_nothing();
705    edit_expr(&value->expr, "Value");
706}
707
708
709static void edit_value_list(struct value *value, const struct frame *frame,
710    void (*set_values)(void *user, const struct value *values, int n_values),
711    void *user)
712{
713    inst_select_outside(value, unselect_value);
714    label_in_box_bg(value->widget, COLOR_EXPR_EDITING);
715    show_value(value->expr, frame);
716    edit_nothing();
717    edit_expr_list(value->expr, set_values, user, "Value(s)");
718}
719
720
721/* ----- activator --------------------------------------------------------- */
722
723
724static GtkWidget *add_activator(GtkWidget *hbox, int active,
725    gboolean (*cb)(GtkWidget *widget, GdkEventButton *event, gpointer data),
726    gpointer user, const char *tooltip, const char *fmt, ...)
727{
728    GtkWidget *label;
729    va_list ap;
730    char buf[100];
731
732    va_start(ap, fmt);
733    vsprintf(buf, fmt, ap);
734    va_end(ap);
735    label = label_in_box_new(buf, tooltip);
736    gtk_misc_set_padding(GTK_MISC(label), 2, 2);
737    gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
738    label_in_box_bg(label,
739        active ? COLOR_CHOICE_SELECTED : COLOR_CHOICE_UNSELECTED);
740    gtk_box_pack_start(GTK_BOX(hbox), box_of_label(label),
741        FALSE, FALSE, 2);
742    g_signal_connect(G_OBJECT(box_of_label(label)),
743        "button_press_event", G_CALLBACK(cb), user);
744    return label;
745}
746
747
748/* ----- assignments ------------------------------------------------------- */
749
750
751static void set_col_values(void *user, const struct value *values,
752    int n_values)
753{
754    struct var *var = user;
755    struct table *table = var->table;
756    struct value *value;
757    const struct var *walk;
758    struct row **row;
759
760    row = &table->rows;
761    while (values) {
762        if (!*row)
763            add_row_here(table, row);
764        value = (*row)->values;
765        for (walk = table->vars; walk != var; walk = walk->next)
766            value = value->next;
767        free_expr(value->expr);
768        value->expr = values->expr;
769        values = values->next;
770        row = &(*row)->next;
771    }
772}
773
774
775static gboolean assignment_var_select_event(GtkWidget *widget,
776    GdkEventButton *event, gpointer data)
777{
778    struct var *var = data;
779
780    switch (event->button) {
781    case 1:
782        edit_var(var, set_col_values, var, -1);
783        break;
784    case 3:
785        pop_up_single_var(var, event);
786        break;
787    }
788    return TRUE;
789}
790
791
792static gboolean assignment_value_select_event(GtkWidget *widget,
793    GdkEventButton *event, gpointer data)
794{
795    struct value *value = data;
796
797    switch (event->button) {
798    case 1:
799        edit_nothing();
800        edit_value(value, value->row->table->vars->frame);
801        break;
802    }
803    return TRUE;
804}
805
806
807/*
808 * In tables, expressions in the active row have a COLOR_CHOICE_SELECTED
809 * background. While expressions in assignments are technically on the active
810 * (and only) row, we use COLOR_VAR_PASSIVE for better readability.
811 */
812
813static void build_assignment(GtkWidget *vbox, struct frame *frame,
814    struct table *table)
815{
816    GtkWidget *hbox, *field;
817    char *name, *expr;
818
819    if (!table->vars || table->vars->next)
820        return;
821    if (!table->rows || table->rows->next)
822        return;
823
824    hbox = gtk_hbox_new(FALSE, 0);
825    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
826
827    name = stralloc_printf("%s%s", table->vars->key ? "?" : "",
828        table->vars->name);
829    field = label_in_box_new(name, "Variable name. Click to edit.");
830    free(name);
831
832    gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
833    label_in_box_bg(field, COLOR_VAR_PASSIVE);
834    table->vars->widget = field;
835    g_signal_connect(G_OBJECT(box_of_label(field)),
836        "button_press_event",
837        G_CALLBACK(assignment_var_select_event), table->vars);
838
839    gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" = "),
840        FALSE, FALSE, 0);
841
842    expr = unparse(table->rows->values->expr);
843    field = label_in_box_new(expr, "Variable value. Click to edit.");
844    free(expr);
845    gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
846    label_in_box_bg(field, COLOR_EXPR_PASSIVE);
847    table->rows->values->widget = field;
848    g_signal_connect(G_OBJECT(box_of_label(field)),
849        "button_press_event",
850        G_CALLBACK(assignment_value_select_event), table->rows->values);
851}
852
853
854/* ----- tables ------------------------------------------------------------ */
855
856
857static void select_row(struct row *row)
858{
859    struct table *table = row->table;
860    struct value *value;
861
862    for (value = table->active_row->values; value; value = value->next)
863        label_in_box_bg(value->widget, COLOR_ROW_UNSELECTED);
864    table->active_row = row;
865    for (value = table->active_row->values; value; value = value->next)
866        label_in_box_bg(value->widget, COLOR_ROW_SELECTED);
867}
868
869
870static void set_row_values(void *user, const struct value *values,
871    int n_values)
872{
873    struct value *value = user;
874    struct row *row = value->row;
875    struct table *table = row->table;
876    struct var **var;
877    const struct value *walk;
878    int first = 1;
879
880    var = &table->vars;
881    for (walk = row->values; walk != value; walk = walk->next)
882        var = &(*var)->next;
883
884    while (values) {
885        if (!*var)
886            add_column_here(table, var);
887        if (first)
888            first = 0;
889        else
890            value = value->next;
891        free_expr(value->expr);
892        value->expr = values->expr;
893        values = values->next;
894        var = &(*var)->next;
895    }
896}
897
898
899static gboolean table_var_press_event(GtkWidget *widget,
900    GdkEventButton *event, gpointer data)
901{
902    struct var *var = data;
903
904    switch (event->button) {
905    case 3:
906        pop_up_table_var(var, event);
907        return TRUE;
908    }
909    return FALSE;
910}
911
912
913static gboolean table_var_release_event(GtkWidget *widget,
914    GdkEventButton *event, gpointer data)
915{
916    struct var *var = data;
917
918    switch (event->button) {
919    case 1:
920        if (is_dragging(var))
921            return FALSE;
922        edit_var(var, set_col_values, var, -1);
923        return TRUE;
924    }
925    return FALSE;
926}
927
928
929static gboolean table_value_press_event(GtkWidget *widget,
930    GdkEventButton *event, gpointer data)
931{
932    struct value *value = data;
933
934    switch (event->button) {
935    case 3:
936        pop_up_table_value(value, event);
937        return TRUE;
938    }
939    return FALSE;
940}
941
942
943static gboolean table_value_release_event(GtkWidget *widget,
944    GdkEventButton *event, gpointer data)
945{
946    struct value *value = data;
947
948    switch (event->button) {
949    case 1:
950        if (is_dragging(value))
951            return FALSE;
952        if (!value->row ||
953            value->row->table->active_row == value->row) {
954            edit_nothing();
955            edit_value_list(value, value->row->table->vars->frame,
956                set_row_values, value);
957        } else {
958            select_row(value->row);
959            change_world();
960        }
961        return TRUE;
962    }
963    return FALSE;
964}
965
966
967static gboolean table_scroll_event(GtkWidget *widget, GdkEventScroll *event,
968    gpointer data)
969{
970    struct table *table = data;
971    struct row *row, *last;
972
973    switch (event->direction) {
974    case GDK_SCROLL_UP:
975        last = NULL;
976        for (row = table->rows;
977            row && (!last || row != table->active_row); row = row->next)
978            last = row;
979        table->active_row = last;
980        change_world();
981        break;
982    case GDK_SCROLL_DOWN:
983        table->active_row = table->active_row->next;
984        if (!table->active_row)
985            table->active_row = table->rows;
986        change_world();
987        break;
988    default:
989        /* ignore */;
990    }
991    return TRUE;
992}
993
994
995/* @@@ this function is too long */
996
997static void build_table(GtkWidget *vbox, struct frame *frame,
998    struct table *table, int wrap_width)
999{
1000    GtkWidget *tab, *field;
1001    GtkWidget *evbox, *align, *sep;
1002    struct var *var;
1003    struct row *row;
1004    struct value *value;
1005    int n_vars = 0, n_rows = 0;
1006    int n_var, n_row, pos;
1007    char *name, *expr;
1008    GdkColor color;
1009
1010    for (var = table->vars; var; var = var->next)
1011        n_vars++;
1012    for (row = table->rows; row; row = row->next)
1013        n_rows++;
1014
1015    if (n_vars == 1 && n_rows == 1)
1016        return;
1017
1018    var = table->vars;
1019    n_var = 0;
1020    n_vars = 0;
1021    while (var) {
1022        if (n_vars) {
1023            gtk_table_resize(GTK_TABLE(tab), n_rows, n_vars+1);
1024        } else {
1025            evbox = gtk_event_box_new();
1026            align = gtk_alignment_new(0, 0, 0, 0);
1027            gtk_container_add(GTK_CONTAINER(align), evbox);
1028            gtk_box_pack_start(GTK_BOX(vbox), align,
1029                FALSE, FALSE, 0);
1030
1031            tab = gtk_table_new(n_rows+1, n_vars, FALSE);
1032            gtk_container_add(GTK_CONTAINER(evbox), tab);
1033            color = get_color(COLOR_VAR_TABLE_SEP);
1034            gtk_widget_modify_bg(GTK_WIDGET(evbox),
1035                GTK_STATE_NORMAL, &color);
1036
1037            gtk_table_set_row_spacings(GTK_TABLE(tab), 1);
1038            gtk_table_set_col_spacings(GTK_TABLE(tab), 1);
1039        }
1040
1041        name = stralloc_printf("%s%s", var->key ? "?" : "", var->name);
1042        field = label_in_box_new(name,
1043            "Variable (column) name. Click to edit.");
1044        free(name);
1045
1046        gtk_table_attach_defaults(GTK_TABLE(tab), box_of_label(field),
1047            n_vars, n_vars+1, 0, 1);
1048        label_in_box_bg(field, COLOR_VAR_PASSIVE);
1049        g_signal_connect(G_OBJECT(box_of_label(field)),
1050            "button_press_event",
1051            G_CALLBACK(table_var_press_event), var);
1052        g_signal_connect(G_OBJECT(box_of_label(field)),
1053            "button_release_event",
1054            G_CALLBACK(table_var_release_event), var);
1055        g_signal_connect(G_OBJECT(box_of_label(field)),
1056            "scroll_event",
1057            G_CALLBACK(table_scroll_event), table);
1058        var->widget = field;
1059
1060        setup_var_drag(var);
1061
1062        n_row = 0;
1063        for (row = table->rows; row; row = row->next) {
1064            value = row->values;
1065            for (pos = 0; pos != n_var; pos++)
1066                value = value->next;
1067            expr = unparse(value->expr);
1068            field = label_in_box_new(expr,
1069                "Variable value. Click to select row or to edit.");
1070            free(expr);
1071            gtk_table_attach_defaults(GTK_TABLE(tab),
1072                box_of_label(field),
1073                n_vars, n_vars+1,
1074                n_row+1, n_row+2);
1075            label_in_box_bg(field, table->active_row == row ?
1076                COLOR_ROW_SELECTED : COLOR_ROW_UNSELECTED);
1077            g_signal_connect(G_OBJECT(box_of_label(field)),
1078                "button_press_event",
1079                G_CALLBACK(table_value_press_event), value);
1080            g_signal_connect(G_OBJECT(box_of_label(field)),
1081                "button_release_event",
1082                G_CALLBACK(table_value_release_event), value);
1083            g_signal_connect(G_OBJECT(box_of_label(field)),
1084                "scroll_event",
1085                G_CALLBACK(table_scroll_event), table);
1086            value->widget = field;
1087            setup_value_drag(value);
1088            n_row++;
1089        }
1090
1091        /*
1092         * Wrap tables wider than the screen area available for
1093         * variables and tables. Don't wrap before having output at
1094         * least one column.
1095         */
1096        if (n_vars && get_widget_width(tab) > wrap_width) {
1097            /*
1098             * Resizing alone doesn't hide extra columns. We have
1099             * to explicitly remove their content as well.
1100             */
1101            gtk_container_remove(GTK_CONTAINER(tab),
1102                box_of_label(var->widget));
1103            for (row = table->rows; row; row = row->next) {
1104                value = row->values;
1105                for (pos = 0; pos != n_var; pos++)
1106                    value = value->next;
1107                gtk_container_remove(GTK_CONTAINER(tab),
1108                    box_of_label(value->widget));
1109            }
1110            gtk_table_resize(GTK_TABLE(tab), n_rows, n_vars);
1111
1112            sep = gtk_vbox_new(FALSE, 0);
1113            gtk_box_pack_start(GTK_BOX(vbox), sep,
1114                FALSE, FALSE, 1);
1115
1116            n_vars = 0;
1117            continue;
1118        }
1119
1120        var = var->next;
1121        n_var++;
1122        n_vars++;
1123    }
1124}
1125
1126
1127/* ----- loops ------------------------------------------------------------- */
1128
1129
1130static void set_loop_values(void *user, const struct value *values,
1131    int n_values)
1132{
1133    struct loop *loop = user;
1134
1135    switch (n_values) {
1136    case 2:
1137        if (loop->to.expr)
1138            free_expr(loop->to.expr);
1139        loop->to.expr = values->next->expr;
1140        /* fall through */
1141    case 1:
1142        if (loop->from.expr)
1143            free_expr(loop->from.expr);
1144        loop->from.expr = values->expr;
1145        break;
1146    case 0:
1147        break;
1148    default:
1149        abort();
1150    }
1151}
1152
1153
1154static gboolean loop_var_select_event(GtkWidget *widget,
1155    GdkEventButton *event, gpointer data)
1156{
1157    struct loop *loop = data;
1158
1159    switch (event->button) {
1160    case 1:
1161        edit_var(&loop->var, set_loop_values, loop, 2);
1162        break;
1163    case 3:
1164        pop_up_loop_var(loop, event);
1165        break;
1166    }
1167    return TRUE;
1168}
1169
1170
1171static gboolean loop_from_select_event(GtkWidget *widget,
1172    GdkEventButton *event, gpointer data)
1173{
1174    struct loop *loop = data;
1175
1176    switch (event->button) {
1177    case 1:
1178        edit_nothing();
1179        edit_value(&loop->from, loop->var.frame);
1180        break;
1181    }
1182    return TRUE;
1183}
1184
1185
1186static gboolean loop_to_select_event(GtkWidget *widget,
1187    GdkEventButton *event, gpointer data)
1188{
1189    struct loop *loop = data;
1190
1191    switch (event->button) {
1192    case 1:
1193        edit_nothing();
1194        edit_value(&loop->to, loop->var.frame);
1195        break;
1196    }
1197    return TRUE;
1198}
1199
1200
1201static gboolean loop_select_event(GtkWidget *widget, GdkEventButton *event,
1202    gpointer data)
1203{
1204    struct loop *loop = data;
1205
1206    switch (event->button) {
1207    case 1:
1208        loop->active =
1209            (long) gtk_object_get_data(GTK_OBJECT(widget), "value");
1210        change_world();
1211        break;
1212    }
1213    return TRUE;
1214}
1215
1216
1217static gboolean loop_scroll_event(GtkWidget *widget, GdkEventScroll *event,
1218    gpointer data)
1219{
1220    struct loop *loop = data;
1221
1222    switch (event->direction) {
1223    case GDK_SCROLL_UP:
1224        if (loop->active < loop->iterations-1) {
1225            loop->active++;
1226            change_world();
1227        }
1228        break;
1229    case GDK_SCROLL_DOWN:
1230        if (loop->active) {
1231            loop->active--;
1232            change_world();
1233        }
1234        break;
1235    default:
1236        /* ignore */;
1237    }
1238    return TRUE;
1239}
1240
1241
1242static void build_loop(GtkWidget *vbox, struct frame *frame,
1243    struct loop *loop)
1244{
1245    GtkWidget *hbox, *field, *label;
1246    char *expr;
1247    int i;
1248
1249    hbox = gtk_hbox_new(FALSE, 0);
1250    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1251
1252    field = label_in_box_new(loop->var.name,
1253        "Variable name. Click to edit.");
1254    gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
1255    label_in_box_bg(field, COLOR_VAR_PASSIVE);
1256    if (instantiation_error == loop)
1257        label_in_box_fg(field, COLOR_ITEM_ERROR);
1258    g_signal_connect(G_OBJECT(box_of_label(field)),
1259        "button_press_event",
1260        G_CALLBACK(loop_var_select_event), loop);
1261    loop->var.widget = field;
1262
1263    gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" = "),
1264        FALSE, FALSE, 0);
1265
1266    expr = unparse(loop->from.expr);
1267    field = label_in_box_new(expr,
1268        "Start value of loop. Click to edit.");
1269    free(expr);
1270    gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
1271    label_in_box_bg(field, COLOR_EXPR_PASSIVE);
1272    g_signal_connect(G_OBJECT(box_of_label(field)),
1273        "button_press_event",
1274        G_CALLBACK(loop_from_select_event), loop);
1275    loop->from.widget = field;
1276
1277    gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" ... "),
1278        FALSE, FALSE, 0);
1279
1280    expr = unparse(loop->to.expr);
1281    field = label_in_box_new(expr, "End value of loop. Click to edit.");
1282    free(expr);
1283    gtk_box_pack_start(GTK_BOX(hbox), box_of_label(field), FALSE, FALSE, 0);
1284    label_in_box_bg(field, COLOR_EXPR_PASSIVE);
1285    g_signal_connect(G_OBJECT(box_of_label(field)),
1286        "button_press_event",
1287        G_CALLBACK(loop_to_select_event), loop);
1288    loop->to.widget = field;
1289
1290    gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" ("),
1291        FALSE, FALSE, 0);
1292
1293    for (i = 0; i != loop->iterations; i++) {
1294        label = add_activator(hbox, loop->active == i,
1295            loop_select_event, loop,
1296            "Loop value. Click to make active.",
1297            "%g", loop->n+i);
1298        gtk_object_set_data(GTK_OBJECT(box_of_label(label)), "value",
1299            (gpointer) (long) i);
1300
1301        g_signal_connect(G_OBJECT(box_of_label(label)),
1302            "scroll_event",
1303            G_CALLBACK(loop_scroll_event), loop);
1304    }
1305
1306    gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(")"),
1307        FALSE, FALSE, 0);
1308}
1309
1310
1311/* ----- the list of variables, tables, and loops -------------------------- */
1312
1313
1314static GtkWidget *build_vars(struct frame *frame, int wrap_width)
1315{
1316    GtkWidget *vbox;
1317    struct table *table;
1318    struct loop *loop;
1319
1320    vbox = gtk_vbox_new(FALSE, 0);
1321    for (table = frame->tables; table; table = table->next) {
1322        add_sep(vbox, 3);
1323        build_assignment(vbox, frame, table);
1324        build_table(vbox, frame, table, wrap_width);
1325    }
1326    for (loop = frame->loops; loop; loop = loop->next) {
1327        add_sep(vbox, 3);
1328        build_loop(vbox, frame, loop);
1329    }
1330    return vbox;
1331}
1332
1333
1334/* ----- items ------------------------------------------------------------- */
1335
1336
1337static void set_item_color(struct inst *inst, const char *color)
1338{
1339    GtkWidget *label;
1340
1341    if (inst->vec)
1342        label = inst->vec->list_widget;
1343    else
1344        label = inst->obj->list_widget;
1345    if (label)
1346        label_in_box_bg(box_of_label(label), color);
1347}
1348
1349
1350void gui_frame_select_inst(struct inst *inst)
1351{
1352    set_item_color(inst, COLOR_ITEM_SELECTED);
1353}
1354
1355
1356void gui_frame_deselect_inst(struct inst *inst)
1357{
1358    set_item_color(inst, COLOR_ITEM_NORMAL);
1359}
1360
1361
1362static gboolean item_select_vec(GtkWidget *widget, GdkEventButton *event,
1363     gpointer data)
1364{
1365    struct vec *vec = data;
1366
1367    switch (event->button) {
1368    case 1:
1369        inst_select_vec(vec);
1370        redraw();
1371        break;
1372    }
1373    return TRUE;
1374}
1375
1376
1377static gboolean item_select_obj(GtkWidget *widget, GdkEventButton *event,
1378     gpointer data)
1379{
1380    struct obj *obj = data;
1381
1382    switch (event->button) {
1383    case 1:
1384        inst_select_obj(obj);
1385        redraw();
1386        break;
1387    }
1388    return TRUE;
1389}
1390
1391
1392static GtkWidget *item_label(GtkWidget *tab, char *s, int col, int row,
1393    gboolean (*cb)(GtkWidget *widget, GdkEventButton *event, gpointer data),
1394    gpointer data)
1395{
1396    GtkWidget *label;
1397
1398    label = label_in_box_new(s, "Click to select.");
1399    gtk_misc_set_padding(GTK_MISC(label), 0, 0);
1400    gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
1401    gtk_widget_modify_font(label, item_list_font);
1402    gtk_table_attach_defaults(GTK_TABLE(tab), box_of_label(label),
1403        col, col+1, row, row+1);
1404    label_in_box_bg(box_of_label(label), COLOR_ITEM_NORMAL);
1405
1406    if (cb)
1407        g_signal_connect(G_OBJECT(box_of_label(label)),
1408            "button_press_event", G_CALLBACK(cb), data);
1409
1410    free(s);
1411    return label;
1412}
1413
1414
1415static GtkWidget *build_items(struct frame *frame)
1416{
1417    GtkWidget *vbox, *hbox, *tab;
1418    struct order *order, *item;
1419    struct vec *vec;
1420    struct obj *obj;
1421    int n;
1422    char *s, *t;
1423
1424    n = 0;
1425    for (vec = frame->vecs; vec; vec = vec->next)
1426        n++;
1427    for (obj = frame->objs; obj; obj = obj->next)
1428        if (obj->type != ot_meas)
1429            n++;
1430
1431    vbox = gtk_vbox_new(FALSE, 0);
1432    add_sep(vbox, 3);
1433
1434    hbox = gtk_hbox_new(FALSE, 0);
1435    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1436
1437    tab = gtk_table_new(n, 2, FALSE);
1438    gtk_box_pack_start(GTK_BOX(hbox), tab, FALSE, FALSE, 0);
1439
1440    order = order_frame(frame);
1441    n = 0;
1442    for (item = order; item->vec || item->obj; item++) {
1443        if (item->obj) {
1444            s = print_obj(item->obj, item->vec);
1445            item->obj->list_widget = item_label(tab, s, 1, n,
1446                item_select_obj, item->obj);
1447            if (item->obj == instantiation_error)
1448                label_in_box_fg(item->obj->list_widget,
1449                    COLOR_ITEM_ERROR);
1450        } else {
1451            t = stralloc_printf("%s: ", print_label(item->vec));
1452            item_label(tab, t, 0, n, NULL, NULL);
1453
1454            s = print_vec(item->vec);
1455            item->vec->list_widget = item_label(tab, s, 1, n,
1456                item_select_vec, item->vec);
1457            if (item->vec == instantiation_error)
1458                label_in_box_fg(item->vec->list_widget,
1459                    COLOR_ITEM_ERROR);
1460        }
1461        n++;
1462        }
1463        free(order);
1464
1465    return vbox;
1466}
1467
1468
1469static GtkWidget *build_meas(struct frame *frame)
1470{
1471    GtkWidget *vbox, *hbox, *tab;
1472    struct obj *obj;
1473    int n;
1474    char *s;
1475
1476    n = 0;
1477    for (obj = frame->objs; obj; obj = obj->next)
1478        if (obj->type == ot_meas)
1479            n++;
1480
1481    vbox = gtk_vbox_new(FALSE, 0);
1482    add_sep(vbox, 3);
1483
1484    hbox = gtk_hbox_new(FALSE, 0);
1485    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1486
1487    tab = gtk_table_new(n, 2, FALSE);
1488    gtk_box_pack_start(GTK_BOX(hbox), tab, FALSE, FALSE, 0);
1489
1490    n = 0;
1491    for (obj = frame->objs; obj; obj = obj->next) {
1492        if (obj->type != ot_meas)
1493            continue;
1494        s = print_meas(obj);
1495        obj->list_widget = item_label(tab, s, 0, n,
1496            item_select_obj, obj);
1497        if (obj == instantiation_error)
1498            label_in_box_fg(obj->list_widget, COLOR_ITEM_ERROR);
1499        n++;
1500        }
1501
1502    return vbox;
1503}
1504
1505
1506static void dont_build_items(struct frame *frame)
1507{
1508    struct vec *vec;
1509    struct obj *obj;
1510
1511    for (vec = frame->vecs; vec; vec = vec->next)
1512        vec->list_widget = NULL;
1513    for (obj = frame->objs; obj; obj = obj->next)
1514        obj->list_widget = NULL;
1515}
1516
1517
1518/* ----- package name ------------------------------------------------------ */
1519
1520
1521static int validate_pkg_name(const char *s, void *ctx)
1522{
1523    if (!*s)
1524        return 0;
1525    while (*s) {
1526        if (*s < 32 || *s > 126)
1527            return 0;
1528        s++;
1529    }
1530    return 1;
1531}
1532
1533static void unselect_pkg_name(void *data)
1534{
1535    GtkWidget *widget = data;
1536
1537    label_in_box_bg(widget, COLOR_PART_NAME);
1538}
1539
1540
1541static gboolean pkg_scroll_event(GtkWidget *widget, GdkEventScroll *event,
1542    gpointer data)
1543{
1544    struct pkg *pkg, *last;
1545
1546    switch (event->direction) {
1547    case GDK_SCROLL_UP:
1548        if (active_pkg->next)
1549            active_pkg = active_pkg->next;
1550        else
1551            active_pkg = pkgs->next;
1552        change_world();
1553        break;
1554    case GDK_SCROLL_DOWN:
1555        last = NULL;
1556        for (pkg = pkgs->next; pkg && (!last || pkg != active_pkg);
1557            pkg = pkg->next)
1558            last = pkg;
1559        active_pkg = last;
1560        change_world();
1561        break;
1562    default:
1563        /* ignore */;
1564    }
1565    return TRUE;
1566}
1567
1568
1569static gboolean pkg_name_edit_event(GtkWidget *widget, GdkEventButton *event,
1570    gpointer data)
1571{
1572    switch (event->button) {
1573    case 1:
1574        inst_select_outside(widget, unselect_pkg_name);
1575        label_in_box_bg(widget, COLOR_PART_NAME_EDITING);
1576        status_set_type_entry(NULL, "package =");
1577        status_set_name("Package name (actual)", "%s", pkg_name);
1578        edit_nothing();
1579        edit_name(&pkg_name, validate_pkg_name, NULL,
1580            "Package name (template)");
1581        break;
1582    }
1583    return TRUE;
1584}
1585
1586
1587static GtkWidget *build_pkg_name(void)
1588{
1589    GtkWidget *label;
1590
1591    label = label_in_box_new(pkg_name,
1592        "Package name. (Template) Click to edit.");
1593    gtk_misc_set_padding(GTK_MISC(label), 2, 2);
1594    gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
1595
1596    label_in_box_bg(label, COLOR_PART_NAME);
1597
1598    g_signal_connect(G_OBJECT(box_of_label(label)),
1599        "button_press_event", G_CALLBACK(pkg_name_edit_event), NULL);
1600    g_signal_connect(G_OBJECT(box_of_label(label)),
1601        "scroll_event", G_CALLBACK(pkg_scroll_event), NULL);
1602
1603    return box_of_label(label);
1604}
1605
1606
1607/* ----- packages ---------------------------------------------------------- */
1608
1609
1610static gboolean pkg_select_event(GtkWidget *widget, GdkEventButton *event,
1611    gpointer data)
1612{
1613    struct pkg *pkg = data;
1614
1615    switch (event->button) {
1616    case 1:
1617        active_pkg = pkg;
1618        /* @@@ we could actually skip instantiation here */
1619        change_world();
1620        break;
1621    }
1622    return TRUE;
1623}
1624
1625
1626static GtkWidget *build_pkg_names(void)
1627{
1628    GtkWidget *hbox;
1629    struct pkg *pkg;
1630    GtkWidget *field;
1631
1632    hbox = gtk_hbox_new(FALSE, 0);
1633    for (pkg = pkgs; pkg; pkg = pkg->next)
1634        if (pkg->name) {
1635            field = add_activator(hbox, pkg == active_pkg,
1636                pkg_select_event, pkg,
1637                "Package name. Click to make active.",
1638                "%s", pkg->name);
1639            g_signal_connect(G_OBJECT(box_of_label(field)),
1640                "scroll_event",
1641                G_CALLBACK(pkg_scroll_event), NULL);
1642        }
1643    return hbox;
1644}
1645
1646
1647/* ----- frame labels ------------------------------------------------------ */
1648
1649
1650static int validate_frame_name(const char *s, void *ctx)
1651{
1652    struct frame *f;
1653
1654    if (!is_id(s))
1655        return 0;
1656    for (f = frames->next; f; f = f->next)
1657        if (!strcmp(f->name, s))
1658            return 0;
1659    return 1;
1660}
1661
1662
1663static void unselect_frame(void *data)
1664{
1665    struct frame *frame= data;
1666
1667    /*
1668     * "unselect" means in this context that the selection has moved
1669     * elsewhere. However, this does not necessarily change the frame.
1670     * (And, in fact, since we rebuild the frame list anyway, the color
1671     * change here doesn't matter if selecting a different frame.)
1672     * So we revert from "editing" to "selected".
1673     */
1674    label_in_box_bg(frame->label, COLOR_FRAME_SELECTED);
1675}
1676
1677
1678static void edit_frame(struct frame *frame)
1679{
1680    const char *tip;
1681
1682    inst_select_outside(frame, unselect_frame);
1683    label_in_box_bg(frame->label, COLOR_FRAME_EDITING);
1684    tip = "Frame name";
1685    status_set_type_entry(NULL, "name =");
1686    status_set_name(tip, "%s", frame->name);
1687    edit_nothing();
1688    edit_unique(&frame->name, validate_frame_name, frame, tip);
1689}
1690
1691
1692void select_frame(struct frame *frame)
1693{
1694    if (active_frame)
1695        label_in_box_bg(active_frame->label, COLOR_FRAME_UNSELECTED);
1696    active_frame = frame;
1697    change_world();
1698}
1699
1700
1701static gboolean frame_press_event(GtkWidget *widget, GdkEventButton *event,
1702    gpointer data)
1703{
1704    struct frame *frame = data;
1705
1706    switch (event->button) {
1707    case 3:
1708        pop_up_frame(frame, event);
1709        return TRUE;
1710    }
1711    return FALSE;
1712}
1713
1714
1715static gboolean frame_release_event(GtkWidget *widget, GdkEventButton *event,
1716    gpointer data)
1717{
1718    struct frame *frame = data;
1719
1720    switch (event->button) {
1721    case 1:
1722        if (is_dragging(frame))
1723            return FALSE;
1724        if (active_frame != frame) {
1725            select_frame(frame);
1726        } else {
1727            if (active_frame->name) {
1728                edit_nothing();
1729                edit_frame(frame);
1730            }
1731        }
1732        return TRUE;
1733    }
1734    return FALSE;
1735}
1736
1737
1738static GtkWidget *build_frame_label(struct frame *frame)
1739{
1740    GtkWidget *label;
1741
1742    label = label_in_box_new(frame->name ? frame->name : "(root)",
1743        frame->name ? "Frame name. Click to select or edit." :
1744        "Root frame. Click to select.");
1745    gtk_misc_set_padding(GTK_MISC(label), 2, 2);
1746    gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
1747
1748    label_in_box_bg(label, active_frame == frame ?
1749        COLOR_FRAME_SELECTED : COLOR_FRAME_UNSELECTED);
1750
1751    g_signal_connect(G_OBJECT(box_of_label(label)),
1752        "button_press_event", G_CALLBACK(frame_press_event), frame);
1753    g_signal_connect(G_OBJECT(box_of_label(label)),
1754        "button_release_event", G_CALLBACK(frame_release_event), frame);
1755    frame->label = label;
1756
1757    if (frame != frames)
1758        setup_frame_drag(frame);
1759
1760    return box_of_label(label);
1761}
1762
1763
1764/* ----- frame references -------------------------------------------------- */
1765
1766
1767static gboolean frame_ref_select_event(GtkWidget *widget, GdkEventButton *event,
1768    gpointer data)
1769{
1770    struct obj *obj = data;
1771
1772    switch (event->button) {
1773    case 1:
1774        obj->u.frame.ref->active_ref = data;
1775        change_world();
1776        break;
1777    }
1778    return TRUE;
1779}
1780
1781
1782static GtkWidget *build_frame_refs(const struct frame *frame)
1783{
1784    GtkWidget *hbox;
1785    struct obj *obj;
1786    char *tooltip;
1787
1788    hbox = gtk_hbox_new(FALSE, 0);
1789    for (obj = frame->objs; obj; obj = obj->next)
1790        if (obj->type == ot_frame &&
1791            obj->u.frame.ref == active_frame) {
1792            tooltip = stralloc_printf(
1793                "Frame <b>%s</b> is referenced here. "
1794                "Click to make active.", active_frame->name);
1795            add_activator(hbox,
1796                obj == obj->u.frame.ref->active_ref,
1797                frame_ref_select_event, obj,
1798                tooltip,
1799                "%d", obj->u.frame.lineno);
1800            free(tooltip);
1801        }
1802    return hbox;
1803}
1804
1805
1806/* ----- frames ------------------------------------------------------------ */
1807
1808
1809void build_frames(GtkWidget *vbox, int wrap_width)
1810{
1811    struct frame *frame;
1812    GtkWidget *hbox, *tab, *label, *packages, *refs, *vars, *items, *meas;
1813    int n = 0;
1814    int max_name_width, name_width;
1815
1816    destroy_all_children(GTK_CONTAINER(vbox));
1817    for (frame = frames; frame; frame = frame->next)
1818        n++;
1819
1820    hbox = gtk_hbox_new(FALSE, 0);
1821
1822    tab = gtk_table_new(n*2+3, 2, FALSE);
1823    gtk_table_set_row_spacings(GTK_TABLE(tab), 1);
1824    gtk_table_set_col_spacings(GTK_TABLE(tab), 1);
1825
1826    gtk_box_pack_start(GTK_BOX(hbox), tab, FALSE, FALSE, 0);
1827    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1828
1829    label = build_pkg_name();
1830    gtk_table_attach_defaults(GTK_TABLE(tab), label, 0, 1, 0, 1);
1831    max_name_width = get_widget_width(label);
1832
1833    packages = build_pkg_names();
1834    gtk_table_attach_defaults(GTK_TABLE(tab), packages, 1, 2, 0, 1);
1835
1836    n = 0;
1837    for (frame = frames; frame; frame = frame->next) {
1838        label = build_frame_label(frame);
1839        gtk_table_attach_defaults(GTK_TABLE(tab), label,
1840            0, 1, n*2+1, n*2+2);
1841        n++;
1842        name_width = get_widget_width(label);
1843        if (name_width > max_name_width)
1844            max_name_width = name_width;
1845    }
1846
1847    wrap_width -= max_name_width+FRAME_AREA_MISC_WIDTH;
1848    n = 0;
1849    for (frame = frames; frame; frame = frame->next) {
1850        refs = build_frame_refs(frame);
1851        gtk_table_attach_defaults(GTK_TABLE(tab), refs,
1852            1, 2, n*2+1, n*2+2);
1853
1854        switch (sidebar) {
1855        case sidebar_var:
1856            vars = build_vars(frame, wrap_width);
1857            gtk_table_attach_defaults(GTK_TABLE(tab), vars,
1858                1, 2, n*2+2, n*2+3);
1859            dont_build_items(frame);
1860            break;
1861        case sidebar_code:
1862            items = build_items(frame);
1863            gtk_table_attach_defaults(GTK_TABLE(tab), items,
1864                1, 2, n*2+2, n*2+3);
1865            break;
1866        default:
1867            abort();
1868        }
1869
1870        n++;
1871    }
1872
1873    if (sidebar == sidebar_code) {
1874        meas = build_meas(frames);
1875        gtk_table_attach_defaults(GTK_TABLE(tab), meas,
1876            1, 2, n*2+2, n*2+3);
1877    }
1878
1879    gtk_widget_show_all(hbox);
1880}
1881

Archive Download this file

Branches:
master



interactive