Root/gui_status.c

1/*
2 * gui_status.c - GUI, status area
3 *
4 * Written 2009-2012 by Werner Almesberger
5 * Copyright 2009-2012 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 <stdarg.h>
15#include <stdlib.h>
16#include <stdio.h>
17#include <string.h>
18#include <assert.h>
19#include <gtk/gtk.h>
20#include <gdk/gdkkeysyms.h>
21
22#include "util.h"
23#include "coord.h"
24#include "error.h"
25#include "unparse.h"
26#include "obj.h"
27#include "layer.h"
28#include "gui_util.h"
29#include "gui_style.h"
30#include "gui_canvas.h"
31#include "gui_frame.h"
32#include "gui.h"
33#include "gui_status.h"
34
35
36enum edit_status {
37    es_unchanged,
38    es_good,
39    es_bad,
40};
41
42
43struct edit_ops {
44    char *(*retrieve)(void *ctx);
45    enum edit_status (*status)(const char *s, void *ctx);
46    void (*store)(const char *s, void *ctx);
47};
48
49
50enum curr_unit curr_unit = curr_unit_mm;
51
52
53static GtkWidget *open_edits = NULL;
54static GtkWidget *last_edit = NULL;
55
56
57/* ----- setter functions -------------------------------------------------- */
58
59
60static GtkWidget *status_icon;
61static GtkWidget *status_name, *status_entry;
62static GtkWidget *status_type_x, *status_type_y, *status_type_entry;
63static GtkWidget *status_box_x, *status_entry_y;
64static GtkWidget *status_x, *status_y;
65static GtkWidget *status_r, *status_angle;
66static GtkWidget *status_sys_x, *status_sys_y;
67static GtkWidget *status_user_x, *status_user_y;
68static GtkWidget *status_zoom, *status_grid, *status_unit;
69static GtkWidget *status_msg;
70
71/* The x entry area serves multiple purposes */
72
73static GtkWidget *status_entry_x;
74
75
76static void set_label(GtkWidget *label, const char *tooltip,
77    const char *fmt, va_list ap)
78{
79    char *s;
80
81    s = stralloc_vprintf(fmt, ap);
82    gtk_label_set_text(GTK_LABEL(label), s);
83    gtk_widget_set_tooltip_markup(label, tooltip);
84    free(s);
85}
86
87
88#define SETTER(name) \
89    void status_set_##name(const char *tooltip, const char *fmt, ...)\
90    { \
91        va_list ap; \
92                                    \
93        va_start(ap, fmt); \
94        set_label(status_##name, tooltip, fmt, ap); \
95        va_end(ap); \
96    }
97
98SETTER(type_x)
99SETTER(type_y)
100SETTER(type_entry)
101SETTER(name)
102SETTER(x)
103SETTER(y)
104SETTER(r)
105SETTER(angle)
106SETTER(sys_x)
107SETTER(sys_y)
108SETTER(user_x)
109SETTER(user_y)
110SETTER(zoom)
111SETTER(grid)
112SETTER(unit)
113
114
115/* ----- set things with units --------------------------------------------- */
116
117
118void set_with_units(void (*set)(const char *tooltip, const char *fmt, ...),
119    const char *prefix, unit_type u, const char *tooltip)
120{
121    double n;
122    int mm;
123
124    switch (curr_unit) {
125    case curr_unit_mm:
126        n = units_to_mm(u);
127        mm = 1;
128        break;
129    case curr_unit_mil:
130        n = units_to_mil(u);
131        mm = 0;
132        break;
133    case curr_unit_auto:
134        n = units_to_best(u, &mm);
135        break;
136    default:
137        abort();
138    }
139    if (mm) {
140        /* -NNN.NNN mm */
141        set(tooltip, "%s" MM_FORMAT_FIXED " mm", prefix, n);
142    } else {
143        /* -NNNN.N mil */
144        set(tooltip, "%s" MIL_FORMAT_FIXED " mil", prefix, n);
145    }
146}
147
148
149/* ----- complex status updates -------------------------------------------- */
150
151
152void status_set_icon(GtkWidget *image)
153{
154    vacate_widget(status_icon);
155    if (image)
156        gtk_container_add(GTK_CONTAINER(status_icon), image);
157    gtk_widget_show_all(status_icon);
158}
159
160
161void status_set_xy(struct coord coord)
162{
163    /* do dX/dY etc. stuff later */
164    status_set_type_x(NULL, "X =");
165    status_set_type_y(NULL, "Y =");
166
167    set_with_units(status_set_x, "", coord.x, "Width");
168    set_with_units(status_set_y, "", coord.y, "Height");
169}
170
171
172void status_set_angle_xy(const char *tooltip, struct coord v)
173{
174    if (!v.x && !v.y)
175        status_set_angle(tooltip, "a = 0 deg");
176    else
177        status_set_angle(tooltip, "a = %3.1f deg", theta_vec(v));
178}
179
180
181static void entry_color(GtkWidget *widget, const char *color)
182{
183    GdkColor col;
184
185    col = get_color(color);
186    gtk_widget_modify_base(widget, GTK_STATE_NORMAL, &col);
187}
188
189
190/* ----- pad type display and change --------------------------------------- */
191
192
193static enum pad_type *curr_pad_type;
194static GtkWidget *pad_type;
195
196
197static void show_pad_type(void)
198{
199    gtk_label_set_text(GTK_LABEL(pad_type), pad_type_name(*curr_pad_type));
200}
201
202
203static gboolean pad_type_button_press_event(GtkWidget *widget,
204    GdkEventButton *event, gpointer data)
205{
206    switch (event->button) {
207    case 1:
208        *curr_pad_type = (*curr_pad_type+1) % pt_n;
209        show_pad_type();
210        break;
211    }
212    /*
213     * We can't just redraw() here, because changing the pad type may also
214     * affect the visual stacking. So we change the world and hope we end
215     * up selecting the same pad afterwards.
216     */
217    change_world_reselect();
218    return TRUE;
219}
220
221
222void edit_pad_type(enum pad_type *type)
223{
224    vacate_widget(status_box_x);
225    curr_pad_type = type;
226    pad_type = label_in_box_new(NULL, "Pad type. Click to cycle.");
227    gtk_container_add(GTK_CONTAINER(status_box_x), box_of_label(pad_type));
228    label_in_box_bg(pad_type, COLOR_SELECTOR);
229    g_signal_connect(G_OBJECT(box_of_label(pad_type)),
230        "button_press_event", G_CALLBACK(pad_type_button_press_event),
231        NULL);
232    show_pad_type();
233    gtk_widget_show_all(status_box_x);
234}
235
236
237/* ----- edit helper functions --------------------------------------------- */
238
239
240static void reset_edit(GtkWidget *widget)
241{
242    struct edit_ops *ops;
243    void *ctx;
244    char *s;
245
246    ops = gtk_object_get_data(GTK_OBJECT(widget), "edit-ops");
247    ctx = gtk_object_get_data(GTK_OBJECT(widget), "edit-ctx");
248    assert(ops);
249    s = ops->retrieve(ctx);
250    gtk_object_set_data(GTK_OBJECT(widget), "edit-ops", NULL);
251    gtk_entry_set_text(GTK_ENTRY(widget), s);
252    free(s);
253    entry_color(widget, COLOR_EDIT_ASIS);
254    gtk_object_set_data(GTK_OBJECT(widget), "edit-ops", ops);
255}
256
257
258static void reset_edits(void)
259{
260    GtkWidget *edit;
261
262    for (edit = open_edits; edit;
263        edit = gtk_object_get_data(GTK_OBJECT(edit), "edit-next"))
264        reset_edit(edit);
265    gtk_widget_grab_focus(GTK_WIDGET(open_edits));
266}
267
268
269static gboolean edit_key_press_event(GtkWidget *widget, GdkEventKey *event,
270    gpointer data)
271{
272    GtkWidget *next = gtk_object_get_data(GTK_OBJECT(widget), "edit-next");
273
274    switch (event->keyval) {
275    case GDK_Tab:
276        gtk_widget_grab_focus(GTK_WIDGET(next ? next : open_edits));
277        return TRUE;
278    case GDK_Escape:
279        reset_edits();
280        return TRUE;
281    default:
282        return FALSE;
283    }
284}
285
286
287static void setup_edit(GtkWidget *widget, struct edit_ops *ops, void *ctx,
288    const char *tooltip)
289{
290    gtk_object_set_data(GTK_OBJECT(widget), "edit-ops", ops);
291    gtk_object_set_data(GTK_OBJECT(widget), "edit-ctx", ctx);
292    gtk_object_set_data(GTK_OBJECT(widget), "edit-next", NULL);
293
294    reset_edit(widget);
295
296    if (!open_edits)
297        gtk_widget_grab_focus(GTK_WIDGET(widget));
298    gtk_widget_show(widget);
299
300    g_signal_connect(G_OBJECT(widget), "key_press_event",
301        G_CALLBACK(edit_key_press_event), open_edits);
302
303    if (last_edit)
304        gtk_object_set_data(GTK_OBJECT(last_edit), "edit-next", widget);
305    else
306        open_edits = widget;
307    last_edit = widget;
308    gtk_widget_set_tooltip_markup(widget, tooltip);
309}
310
311
312/* ----- identifier fields ------------------------------------------------- */
313
314
315struct edit_unique_ctx {
316    const char **s;
317    int (*validate)(const char *s, void *ctx);
318    void *ctx;
319};
320
321
322/*
323 * Handle NULL so that we can also use it for unique_null
324 */
325
326static char *unique_retrieve(void *ctx)
327{
328    struct edit_unique_ctx *unique_ctx = ctx;
329
330    return stralloc(*unique_ctx->s ? *unique_ctx->s : "");
331}
332
333
334static enum edit_status unique_status(const char *s, void *ctx)
335{
336    const struct edit_unique_ctx *unique_ctx = ctx;
337
338    if (!strcmp(s, *unique_ctx->s))
339        return es_unchanged;
340    return !unique_ctx->validate ||
341        unique_ctx->validate(s, unique_ctx->ctx) ? es_good : es_bad;
342}
343
344
345static void unique_store(const char *s, void *ctx)
346{
347    const struct edit_unique_ctx *unique_ctx = ctx;
348
349    *unique_ctx->s = unique(s);
350}
351
352
353static struct edit_ops edit_ops_unique = {
354    .retrieve = unique_retrieve,
355    .status = unique_status,
356    .store = unique_store,
357};
358
359
360void edit_unique(const char **s, int (*validate)(const char *s, void *ctx),
361    void *ctx, const char *tooltip)
362{
363    static struct edit_unique_ctx unique_ctx;
364
365    unique_ctx.s = s;
366    unique_ctx.validate = validate;
367    unique_ctx.ctx = ctx;
368    setup_edit(status_entry, &edit_ops_unique, &unique_ctx, tooltip);
369}
370
371
372/* ----- identifier fields with NULL --------------------------------------- */
373
374
375static enum edit_status unique_null_status(const char *s, void *ctx)
376{
377    const struct edit_unique_ctx *unique_ctx = ctx;
378
379    if (!strcmp(s, *unique_ctx->s ? *unique_ctx->s : ""))
380        return es_unchanged;
381    if (!*s)
382        return es_good;
383    return !unique_ctx->validate ||
384        unique_ctx->validate(s, unique_ctx->ctx) ? es_good : es_bad;
385}
386
387
388static void unique_null_store(const char *s, void *ctx)
389{
390    const struct edit_unique_ctx *unique_ctx = ctx;
391
392    if (!*s)
393        *unique_ctx->s = NULL;
394    else
395        *unique_ctx->s = unique(s);
396}
397
398
399static struct edit_ops edit_ops_null_unique = {
400    .retrieve = unique_retrieve,
401    .status = unique_null_status,
402    .store = unique_null_store,
403};
404
405
406void edit_unique_null(const char **s,
407    int (*validate)(const char *s, void *ctx), void *ctx, const char *tooltip)
408{
409    static struct edit_unique_ctx unique_ctx;
410
411    unique_ctx.s = s;
412    unique_ctx.validate = validate;
413    unique_ctx.ctx = ctx;
414    setup_edit(status_entry, &edit_ops_null_unique, &unique_ctx, tooltip);
415}
416
417
418/* ----- unique field (variable) optionally followed by values ------------- */
419
420
421struct edit_unique_with_values_ctx {
422    const char **s;
423    int (*validate)(const char *s, void *ctx);
424    void *ctx;
425    void (*set_values)(void *user, const struct value *values,
426        int n_values);
427    void *user;
428    int max_values;
429};
430
431
432static int unique_with_values_check(const char *s,
433    const struct edit_unique_with_values_ctx *unique_ctx)
434{
435    const char *id;
436    struct value *values;
437    int n;
438
439    status_begin_reporting();
440    n = parse_var(s, &id, &values, unique_ctx->max_values);
441    if (n < 0)
442        return 0;
443    free_values(values, 0);
444    return unique_ctx->validate ?
445        unique_ctx->validate(id, unique_ctx->ctx) : 0;
446}
447
448
449static enum edit_status unique_with_values_status(const char *s, void *ctx)
450{
451    const struct edit_unique_with_values_ctx *unique_ctx = ctx;
452
453    if (!strcmp(s, *unique_ctx->s))
454        return es_unchanged;
455    return unique_with_values_check(s, unique_ctx) ? es_good : es_bad;
456}
457
458
459static void unique_with_values_store(const char *s, void *ctx)
460{
461    const struct edit_unique_with_values_ctx *unique_ctx = ctx;
462    struct value *values;
463    int n;
464
465    status_begin_reporting();
466    n = parse_var(s, unique_ctx->s, &values, unique_ctx->max_values);
467    if (!n)
468        return;
469    assert(n >= 0);
470    assert(unique_ctx->max_values == -1 || n <= unique_ctx->max_values);
471    unique_ctx->set_values(unique_ctx->user, values, n);
472    free_values(values, 1);
473}
474
475
476static struct edit_ops edit_ops_unique_with_values = {
477    .retrieve = unique_retrieve,
478    .status = unique_with_values_status,
479    .store = unique_with_values_store,
480};
481
482
483void edit_unique_with_values(const char **s,
484    int (*validate)(const char *s, void *ctx), void *ctx,
485    void (*set_values)(void *user, const struct value *values, int n_values),
486    void *user, int max_values,
487    const char *tooltip)
488{
489    static struct edit_unique_with_values_ctx unique_ctx;
490
491    unique_ctx.s = s;
492    unique_ctx.validate = validate;
493    unique_ctx.ctx = ctx;
494    unique_ctx.set_values = set_values;
495    unique_ctx.user = user;
496    unique_ctx.max_values = max_values;
497    setup_edit(status_entry, &edit_ops_unique_with_values, &unique_ctx,
498        tooltip);
499}
500
501
502/* ----- variable type display and change ---------------------------------- */
503
504
505static struct var *curr_var;
506static GtkWidget *var_type;
507
508
509static void show_var_type(void)
510{
511    gtk_label_set_text(GTK_LABEL(var_type),
512        curr_var->key ? "key" : "assign");
513}
514
515
516/*
517 * This is a bit of a layering violation. Note that we can't just try to
518 * activate the edit and see what happens, because unique_with_values_status
519 * would unconditionally accept the previous value, even if it is no longer
520 * acceptable after the type change.
521 */
522
523static int attempt_var_type_change(struct var *var)
524{
525    const struct edit_unique_with_values_ctx *ctx;
526    const char *s;
527
528    ctx = gtk_object_get_data(GTK_OBJECT(status_entry), "edit-ctx");
529    s = gtk_entry_get_text(GTK_ENTRY(status_entry));
530    var->key = !var->key;
531    if (unique_with_values_check(s, ctx))
532        return 1;
533    var->key = !var->key;
534    return 0;
535}
536
537
538static gboolean do_activate(void);
539
540
541static gboolean var_type_button_press_event(GtkWidget *widget,
542    GdkEventButton *event, gpointer data)
543{
544    switch (event->button) {
545    case 1:
546        if (!attempt_var_type_change(curr_var))
547            return TRUE;
548        /* commit edit and change_world() */
549        do_activate();
550        reselect_var(curr_var);
551        return TRUE;
552    }
553    return TRUE;
554}
555
556
557void edit_var_type(struct var *var)
558{
559    vacate_widget(status_box_x);
560    curr_var = var;
561    var_type = label_in_box_new(NULL, "Variable type. Click to cycle.");
562    gtk_container_add(GTK_CONTAINER(status_box_x), box_of_label(var_type));
563    label_in_box_bg(var_type, COLOR_SELECTOR);
564    g_signal_connect(G_OBJECT(box_of_label(var_type)),
565        "button_press_event", G_CALLBACK(var_type_button_press_event),
566        NULL);
567    show_var_type();
568    gtk_widget_show_all(status_box_x);
569}
570
571
572/* ----- string fields ----------------------------------------------------- */
573
574
575struct edit_name_ctx {
576    char **s;
577    int (*validate)(const char *s, void *ctx);
578    void *ctx;
579};
580
581
582static char *name_retrieve(void *ctx)
583{
584    struct edit_name_ctx *name_ctx = ctx;
585
586    return stralloc(*name_ctx->s ? *name_ctx->s : "");
587}
588
589
590static enum edit_status name_status(const char *s, void *ctx)
591{
592    const struct edit_name_ctx *name_ctx = ctx;
593
594    if (!strcmp(s, *name_ctx->s))
595        return es_unchanged;
596    return !name_ctx->validate || name_ctx->validate(s, name_ctx->ctx) ?
597        es_good : es_bad;
598}
599
600
601static void name_store(const char *s, void *ctx)
602{
603    const struct edit_name_ctx *name_ctx = ctx;
604
605    free(*name_ctx->s);
606    *name_ctx->s = stralloc(s);
607}
608
609
610static struct edit_ops edit_ops_name = {
611    .retrieve = name_retrieve,
612    .status = name_status,
613    .store = name_store,
614};
615
616
617void edit_name(char **s, int (*validate)(const char *s, void *ctx), void *ctx,
618    const char *tooltip)
619{
620    static struct edit_name_ctx name_ctx;
621
622    name_ctx.s = s;
623    name_ctx.validate = validate;
624    name_ctx.ctx = ctx;
625    setup_edit(status_entry, &edit_ops_name, &name_ctx, tooltip);
626}
627
628
629/* ----- expression fields ------------------------------------------------- */
630
631
632static struct expr *try_parse_expr(const char *s)
633{
634    status_begin_reporting();
635    return parse_expr(s);
636}
637
638
639static char *expr_retrieve(void *ctx)
640{
641    struct expr **expr = ctx;
642
643    return unparse(*expr);
644}
645
646
647static enum edit_status expr_status(const char *s, void *ctx)
648{
649    struct expr *expr;
650
651    expr = try_parse_expr(s);
652    if (!expr)
653        return es_bad;
654    free_expr(expr);
655    return es_good;
656}
657
658
659static void expr_store(const char *s, void *ctx)
660{
661    struct expr **anchor = ctx;
662    struct expr *expr;
663
664    expr = try_parse_expr(s);
665    assert(expr);
666    if (*anchor)
667        free_expr(*anchor);
668    *anchor = expr;
669}
670
671
672static struct edit_ops edit_ops_expr = {
673    .retrieve = expr_retrieve,
674    .status = expr_status,
675    .store = expr_store,
676};
677
678
679void edit_expr(struct expr **expr, const char *tooltip)
680{
681    setup_edit(status_entry, &edit_ops_expr, expr, tooltip);
682}
683
684
685/* ----- distance expressions ---------------------------------------------- */
686
687
688static void dist_expr_store(const char *s, void *ctx)
689{
690    struct expr **anchor = ctx;
691    struct expr *expr;
692
693    expr_store(s, ctx);
694    expr = *anchor;
695    if (expr->op == op_num && !expr->u.num.exponent && !expr->u.num.n)
696        expr->u.num.exponent = 1;
697}
698
699
700static struct edit_ops edit_ops_dist_expr = {
701    .retrieve = expr_retrieve,
702    .status = expr_status,
703    .store = dist_expr_store,
704};
705
706
707static void edit_any_dist_expr(GtkWidget *widget, struct expr **expr,
708    const char *tooltip)
709{
710    setup_edit(widget, &edit_ops_dist_expr, expr, tooltip);
711}
712
713
714void edit_dist_expr(struct expr **expr, const char *tooltip)
715{
716    edit_any_dist_expr(status_entry, expr, tooltip);
717}
718
719
720void edit_x(struct expr **expr, const char *tooltip)
721{
722    vacate_widget(status_box_x);
723    gtk_container_add(GTK_CONTAINER(status_box_x), status_entry_x);
724    gtk_widget_show(status_box_x);
725    edit_any_dist_expr(status_entry_x, expr, tooltip);
726}
727
728
729void edit_y(struct expr **expr, const char *tooltip)
730{
731    edit_any_dist_expr(status_entry_y, expr, tooltip);
732}
733
734
735/* ----- expression list --------------------------------------------------- */
736
737
738struct edit_expr_list_ctx {
739    struct expr *expr;
740    void (*set_values)(void *user, const struct value *values,
741        int n_values);
742    void *user;
743};
744
745
746static char *expr_list_retrieve(void *ctx)
747{
748    struct edit_expr_list_ctx *expr_list_ctx = ctx;
749
750    return unparse(expr_list_ctx->expr);
751}
752
753
754static enum edit_status expr_list_status(const char *s, void *ctx)
755{
756    struct value *values;
757    int n;
758
759    status_begin_reporting();
760    n = parse_values(s, &values);
761    if (n < 0)
762        return es_bad;
763    free_values(values, 0);
764    return es_good;
765}
766
767
768static void expr_list_store(const char *s, void *ctx)
769{
770    struct edit_expr_list_ctx *expr_list_ctx = ctx;
771    struct value *values;
772    int n;
773
774    status_begin_reporting();
775    n = parse_values(s, &values);
776    assert(n >= 0);
777    expr_list_ctx->set_values(expr_list_ctx->user, values, n);
778    free_values(values, 1);
779}
780
781
782static struct edit_ops edit_ops_expr_list = {
783    .retrieve = expr_list_retrieve,
784    .status = expr_list_status,
785    .store = expr_list_store,
786};
787
788
789void edit_expr_list(struct expr *expr,
790    void (*set_values)(void *user, const struct value *values, int n_values),
791    void *user, const char *tooltip)
792{
793    static struct edit_expr_list_ctx expr_list_ctx;
794
795    expr_list_ctx.expr = expr;
796    expr_list_ctx.set_values = set_values;
797    expr_list_ctx.user = user;
798    setup_edit(status_entry, &edit_ops_expr_list, &expr_list_ctx, tooltip);
799}
800
801
802/* ----- text entry -------------------------------------------------------- */
803
804
805static enum edit_status get_status(GtkWidget *widget)
806{
807    struct edit_ops *ops;
808    void *ctx;
809    const char *s;
810
811    ops = gtk_object_get_data(GTK_OBJECT(widget), "edit-ops");
812    if (!ops)
813        return es_unchanged;
814    ctx = gtk_object_get_data(GTK_OBJECT(widget), "edit-ctx");
815    s = gtk_entry_get_text(GTK_ENTRY(widget));
816    return ops->status(s, ctx);
817}
818
819
820static void set_edit(GtkWidget *widget)
821{
822    struct edit_ops *ops;
823    void *ctx;
824    const char *s;
825
826    ops = gtk_object_get_data(GTK_OBJECT(widget), "edit-ops");
827    if (!ops)
828        return;
829    ctx = gtk_object_get_data(GTK_OBJECT(widget), "edit-ctx");
830    s = gtk_entry_get_text(GTK_ENTRY(widget));
831    if (ops->store)
832        ops->store(s, ctx);
833}
834
835
836static gboolean changed(GtkWidget *widget, GdkEventMotion *event,
837    gpointer data)
838{
839    switch (get_status(widget)) {
840    case es_unchanged:
841        entry_color(widget, COLOR_EDIT_ASIS);
842        break;
843    case es_good:
844        entry_color(widget, COLOR_EDIT_GOOD);
845        break;
846    case es_bad:
847        entry_color(widget, COLOR_EDIT_BAD);
848        break;
849    default:
850        abort();
851    }
852    return TRUE;
853}
854
855
856static gboolean do_activate(void)
857{
858    GtkWidget *edit;
859    enum edit_status status;
860    int unchanged = 1;
861
862    for (edit = open_edits; edit;
863        edit = gtk_object_get_data(GTK_OBJECT(edit), "edit-next")) {
864        status = get_status(edit);
865        if (status == es_bad)
866            return TRUE;
867        if (status == es_good)
868            unchanged = 0;
869    }
870    if (unchanged) {
871        inst_deselect();
872        return TRUE;
873    }
874    for (edit = open_edits; edit;
875        edit = gtk_object_get_data(GTK_OBJECT(edit), "edit-next"))
876        if (get_status(edit) == es_good) {
877            entry_color(edit, COLOR_EDIT_ASIS);
878            set_edit(edit);
879        }
880    inst_deselect();
881    change_world();
882    return TRUE;
883}
884
885
886static gboolean activate(GtkWidget *widget, GdkEventMotion *event,
887    gpointer data)
888{
889    return do_activate();
890}
891
892
893void edit_nothing(void)
894{
895    gtk_widget_hide(status_entry);
896    gtk_widget_hide(status_box_x);
897    gtk_widget_hide(status_entry_y);
898    open_edits = NULL;
899    last_edit = NULL;
900}
901
902
903/* ----- status reports ---------------------------------------------------- */
904
905
906static gint context_id;
907static int have_msg = 0;
908
909
910static void clear_status_msg(void)
911{
912    if (have_msg) {
913        gtk_statusbar_pop(GTK_STATUSBAR(status_msg), context_id);
914        have_msg = 0;
915    }
916}
917
918
919static void report_to_gui(const char *s)
920{
921    if (!have_msg)
922        gtk_statusbar_push(GTK_STATUSBAR(status_msg), context_id, s);
923    have_msg = 1;
924}
925
926
927void status_begin_reporting(void)
928{
929    clear_status_msg();
930    reporter = report_to_gui;
931}
932
933
934/* ----- unit selection ---------------------------------------------------- */
935
936
937static void show_curr_unit(void)
938{
939    static const char *tip = "Display unit. Click to cycle.";
940
941    switch (curr_unit) {
942    case curr_unit_mm:
943        status_set_unit(tip, "mm");
944        break;
945    case curr_unit_mil:
946        status_set_unit(tip, "mil");
947        break;
948    case curr_unit_auto:
949        status_set_unit(tip, "auto");
950        break;
951    default:
952        abort();
953    }
954}
955
956
957static gboolean unit_button_press_event(GtkWidget *widget,
958    GdkEventButton *event, gpointer data)
959{
960    switch (event->button) {
961    case 1:
962        curr_unit = (curr_unit+1) % curr_unit_n;
963        show_curr_unit();
964        break;
965    }
966    refresh_pos();
967    change_world();
968    return TRUE;
969}
970
971
972/* ----- setup ------------------------------------------------------------- */
973
974
975static GtkWidget *add_vbox(GtkWidget *tab, int col, int row)
976{
977    GtkWidget *vbox;
978
979    vbox = gtk_vbox_new(FALSE, 0);
980    gtk_table_attach_defaults(GTK_TABLE(tab), vbox,
981        col, col+1, row, row+1);
982    return vbox;
983}
984
985
986static GtkWidget *add_label_basic(GtkWidget *tab, int col, int row)
987{
988    GtkWidget *label;
989
990    label = label_in_box_new(NULL, NULL);
991    gtk_table_attach(GTK_TABLE(tab), box_of_label(label),
992        col, col+1, row, row+1,
993        GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 1);
994        /* 0 , 1 - padding */
995    return label;
996}
997
998
999static GtkWidget *add_label(GtkWidget *tab, int col, int row)
1000{
1001    GtkWidget *label;
1002
1003    label = add_label_basic(tab, col, row);
1004    gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
1005    gtk_label_set_selectable(GTK_LABEL(label), TRUE);
1006    return label;
1007}
1008
1009
1010static GtkWidget *make_entry(void)
1011{
1012    GtkWidget *entry;
1013
1014    entry = gtk_entry_new();
1015    gtk_entry_set_has_frame(GTK_ENTRY(entry), FALSE);
1016
1017    g_signal_connect(G_OBJECT(entry), "changed",
1018        G_CALLBACK(changed), entry);
1019    g_signal_connect(G_OBJECT(entry), "activate",
1020        G_CALLBACK(activate), entry);
1021
1022    return entry;
1023}
1024
1025
1026static GtkWidget *add_entry(GtkWidget *tab, int col, int row)
1027{
1028    GtkWidget *entry;
1029
1030    entry = make_entry();
1031    gtk_table_attach_defaults(GTK_TABLE(tab), entry,
1032        col, col+1, row, row+1);
1033    return entry;
1034}
1035
1036
1037void make_status_area(GtkWidget *vbox)
1038{
1039    GtkWidget *tab, *sep;
1040    GtkWidget *hbox, *vbox2;
1041
1042    hbox = gtk_hbox_new(FALSE, 0);
1043    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1044
1045    vbox2 = gtk_vbox_new(FALSE, 0);
1046    gtk_box_pack_start(GTK_BOX(hbox), vbox2, FALSE, FALSE, 1);
1047
1048    status_icon = gtk_event_box_new();
1049    gtk_box_pack_start(GTK_BOX(vbox2), status_icon, FALSE, FALSE, 0);
1050
1051    tab = gtk_table_new(7, 3, FALSE);
1052    gtk_box_pack_start(GTK_BOX(hbox), tab, TRUE, TRUE, 0);
1053
1054    /* types */
1055
1056    status_type_x = add_label(tab, 0, 0);
1057    status_type_y = add_label(tab, 0, 1);
1058    status_type_entry = add_label(tab, 0, 2);
1059
1060    /* x / y */
1061
1062    status_x = add_label(tab, 1, 0);
1063    status_box_x = add_vbox(tab, 2, 0);
1064    status_y = add_label(tab, 1, 1);
1065    status_entry_y = add_entry(tab, 2, 1);
1066
1067    status_entry_x = gtk_widget_ref(make_entry());
1068
1069    /* name and input */
1070
1071    status_name = add_label(tab, 1, 2);
1072    status_entry = add_entry(tab, 2, 2);
1073
1074    /* separator */
1075
1076    sep = gtk_vseparator_new();
1077    gtk_table_attach_defaults(GTK_TABLE(tab), sep, 3, 4, 0, 3);
1078
1079    /* sys / user pos */
1080
1081    status_sys_x = add_label(tab, 4, 0);
1082    status_sys_y = add_label(tab, 4, 1);
1083    status_user_x = add_label(tab, 5, 0);
1084    status_user_y = add_label(tab, 5, 1);
1085
1086    /* r / angle */
1087
1088    status_r = add_label(tab, 4, 2);
1089    status_angle = add_label(tab, 5, 2);
1090
1091    /* zoom / grid / unit */
1092
1093    status_zoom = add_label(tab, 6, 0);
1094    status_grid = add_label(tab, 6, 1);
1095    status_unit = add_label_basic(tab, 6, 2);
1096
1097    /* unit selection */
1098
1099    label_in_box_bg(status_unit, COLOR_SELECTOR);
1100    show_curr_unit();
1101    g_signal_connect(G_OBJECT(box_of_label(status_unit)),
1102        "button_press_event", G_CALLBACK(unit_button_press_event), NULL);
1103
1104    /* message bar */
1105
1106    status_msg = gtk_statusbar_new();
1107    gtk_box_pack_start(GTK_BOX(vbox), status_msg, FALSE, FALSE, 0);
1108
1109    context_id = gtk_statusbar_get_context_id(GTK_STATUSBAR(status_msg),
1110        "messages");
1111}
1112
1113
1114void cleanup_status_area(void)
1115{
1116    gtk_widget_unref(status_entry_x);
1117}
1118

Archive Download this file

Branches:
master



interactive