Root/gui_frame.c

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

Archive Download this file

Branches:
master



interactive