Root/gui_tool.c

Source at commit 377b7f81193685f23c93546767a059e194419446 created 3 years 8 months ago.
By Werner Almesberger, fped.c: new option -m (for -p and -P) to suppress showing measurements
1/*
2 * gui_tool.c - GUI, tool bar
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 <stdlib.h>
15#include <math.h>
16#include <assert.h>
17#include <gtk/gtk.h>
18
19#include "util.h"
20#include "inst.h"
21#include "meas.h"
22#include "obj.h"
23#include "gui_util.h"
24#include "gui_style.h"
25#include "gui_inst.h"
26#include "gui_over.h"
27#include "gui_canvas.h"
28#include "gui_status.h"
29#include "gui.h"
30#include "gui_meas.h"
31#include "gui_tool.h"
32
33
34#include "icons/arc.xpm"
35#include "icons/circ.xpm"
36#include "icons/frame.xpm"
37#include "icons/line.xpm"
38#include "icons/meas.xpm"
39#include "icons/meas_x.xpm"
40#include "icons/meas_y.xpm"
41#include "icons/pad.xpm"
42#include "icons/rpad.xpm"
43#include "icons/hole.xpm"
44#include "icons/point.xpm"
45#include "icons/delete.xpm"
46#include "icons/delete_off.xpm"
47#include "icons/rect.xpm"
48#include "icons/vec.xpm"
49
50
51static GtkWidget *ev_point, *ev_delete;
52static GtkWidget *active_tool;
53static struct tool_ops *active_ops = NULL;
54static struct inst *hover_inst = NULL;
55static GtkWidget *delete_image[2];
56
57static struct drag_state {
58    struct inst *inst; /* non-NULL if dragging an existing object */
59    struct inst *new; /* non-NULL if dragging a new object */
60    int anchors_n; /* number of anchors, 0 if no moving */
61    int anchor_i; /* current anchor */
62    struct vec **anchors[3];
63} drag = {
64    .new = NULL,
65    .anchors_n = 0,
66};
67
68static struct coord last_canvas_pos;
69
70
71static struct vec *new_vec(struct inst *base)
72{
73    struct vec *vec, **walk;
74
75    vec = alloc_type(struct vec);
76    vec->nul_tag = 0;
77    vec->name = NULL;
78    vec->base = inst_get_vec(base);
79    vec->next = NULL;
80    vec->frame = active_frame;
81    for (walk = &active_frame->vecs; *walk; walk = &(*walk)->next);
82    *walk = vec;
83    return vec;
84}
85
86
87struct obj *new_obj_unconnected(enum obj_type type, struct inst *base)
88{
89    struct obj *obj;
90
91    obj = alloc_type(struct obj);
92    obj->type = type;
93    obj->name = NULL;
94    obj->frame = active_frame;
95    obj->base = inst_get_vec(base);
96    obj->next = NULL;
97    obj->lineno = 0;
98    return obj;
99}
100
101
102void connect_obj(struct frame *frame, struct obj *obj)
103{
104    struct obj **walk;
105
106    obj->frame = frame;
107    for (walk = &frame->objs; *walk; walk = &(*walk)->next);
108    *walk = obj;
109}
110
111
112static struct obj *new_obj(enum obj_type type, struct inst *base)
113{
114    struct obj *obj;
115
116    obj = new_obj_unconnected(type, base);
117    connect_obj(active_frame, obj);
118    return obj;
119}
120
121
122/* ----- shared functions -------------------------------------------------- */
123
124
125struct pix_buf *draw_move_line_common(struct inst *inst,
126    struct coord end, struct coord pos, int i)
127{
128    struct coord from, to;
129    struct pix_buf *buf;
130
131    from = translate(inst->base);
132    to = translate(end);
133    pos = translate(pos);
134    switch (i) {
135    case 0:
136        from = pos;
137        break;
138    case 1:
139        to = pos;
140        break;
141    default:
142        abort();
143    }
144    buf = save_pix_buf(DA, from.x, from.y, to.x, to.y, 1);
145    gdk_draw_line(DA, gc_drag, from.x, from.y, to.x, to.y);
146    return buf;
147}
148
149
150static struct pix_buf *draw_move_rect_common(struct inst *inst,
151    struct coord other, struct coord pos, int i)
152{
153    struct coord min, max;
154    struct pix_buf *buf;
155
156    min = translate(inst->base);
157    max = translate(other);
158    pos = translate(pos);
159    switch (i) {
160    case 0:
161        min = pos;
162        break;
163    case 1:
164        max = pos;
165        break;
166    default:
167        abort();
168    }
169    sort_coord(&min, &max);
170    buf = save_pix_buf(DA, min.x, min.y, max.x, max.y, 1);
171    gdk_draw_rectangle(DA, gc_drag, FALSE,
172        min.x, min.y, max.x-min.x, max.y-min.y);
173    return buf;
174}
175
176
177static struct pix_buf *hover_common(GdkGC *gc, struct coord center, unit_type r)
178{
179    struct pix_buf *buf;
180
181    center = translate(center);
182    buf = save_pix_buf(DA,
183        center.x-r, center.y-r, center.x+r, center.y+r, 2);
184    draw_circle(DA, gc, FALSE, center.x, center.y, VEC_EYE_R);
185    return buf;
186}
187
188
189/* ----- delete ------------------------------------------------------------ */
190
191
192static void tool_selected_delete(void)
193{
194    if (selected_inst) {
195        tool_dehover();
196        inst_delete(selected_inst);
197        change_world();
198    }
199    tool_reset();
200}
201
202
203static struct tool_ops delete_ops = {
204    .tool_selected = tool_selected_delete,
205};
206
207
208void tool_selected_inst(struct inst *inst)
209{
210    set_image(ev_delete, delete_image[inst != NULL]);
211}
212
213
214/* ----- vec --------------------------------------------------------------- */
215
216
217static struct coord gridify(struct coord base, struct coord pos)
218{
219    struct coord new;
220    unit_type unit;
221
222    switch (curr_unit) {
223    case curr_unit_mm:
224    case curr_unit_auto:
225        unit = mm_to_units(0.1);
226        break;
227    case curr_unit_mil:
228        unit = mil_to_units(10);
229        break;
230    default:
231        abort();
232    }
233    new.x = pos.x-((pos.x-base.x) % unit);
234    new.y = pos.y-((pos.y-base.y) % unit);
235    if (new.x != base.x || new.y != base.y)
236        return new;
237    if (fabs(pos.x-base.x) > fabs(pos.y-base.y))
238        new.x += pos.x > base.x ? unit : -unit;
239    else
240        new.y += pos.y > base.y ? unit : -unit;
241    return new;
242}
243
244
245static struct pix_buf *drag_new_vec(struct inst *from, struct coord to)
246{
247    struct coord pos;
248    struct pix_buf *buf;
249
250    pos = inst_get_point(from);
251    to = gridify(pos, to);
252    status_set_type_x(NULL, "dX =");
253    status_set_type_y(NULL, "dX =");
254    /* @@@ use status_set_xy */
255    switch (curr_unit) {
256    case curr_unit_mm:
257    case curr_unit_auto:
258        status_set_x(NULL, "%lg mm", units_to_mm(to.x-pos.x));
259        status_set_y(NULL, "%lg mm", units_to_mm(to.y-pos.y));
260        break;
261    case curr_unit_mil:
262        status_set_x(NULL, "%lg mil", units_to_mil(to.x-pos.x));
263        status_set_y(NULL, "%lg mil", units_to_mil(to.y-pos.y));
264        break;
265    default:
266        abort();
267    }
268    pos = translate(pos);
269    to = translate(to);
270    buf = save_pix_buf(DA, pos.x, pos.y, to.x, to.y, 1);
271    gdk_draw_line(DA, gc_drag, pos.x, pos.y, to.x, to.y);
272    return buf;
273}
274
275
276struct pix_buf *draw_move_vec(struct inst *inst, struct coord pos, int i)
277{
278    return draw_move_line_common(inst,
279        add_vec(sub_vec(inst->u.vec.end, inst->base), pos), pos, i);
280}
281
282
283struct pix_buf *gui_hover_vec(struct inst *self)
284{
285    return hover_common(gc_vec[mode_hover],
286        self->u.vec.end, VEC_EYE_R);
287}
288
289
290static int end_new_raw_vec(struct inst *from, struct coord to)
291{
292    struct vec *vec;
293    struct coord pos;
294
295    vec = new_vec(from);
296    pos = inst_get_point(from);
297    to = gridify(pos, to);
298    switch (curr_unit) {
299    case curr_unit_mm:
300    case curr_unit_auto:
301        vec->x = new_num(make_mm(units_to_mm(to.x-pos.x)));
302        vec->y = new_num(make_mm(units_to_mm(to.y-pos.y)));
303        break;
304    case curr_unit_mil:
305        vec->x = new_num(make_mil(units_to_mil(to.x-pos.x)));
306        vec->y = new_num(make_mil(units_to_mil(to.y-pos.y)));
307        break;
308    default:
309        abort();
310    }
311    return 1;
312}
313
314
315static struct tool_ops vec_ops = {
316    .drag_new = drag_new_vec,
317    .end_new_raw = end_new_raw_vec,
318};
319
320
321/* ----- line -------------------------------------------------------------- */
322
323
324struct pix_buf *drag_new_line(struct inst *from, struct coord to)
325{
326    struct coord pos;
327    struct pix_buf *buf;
328
329    pos = translate(inst_get_point(from));
330    to = translate(to);
331    buf = save_pix_buf(DA, pos.x, pos.y, to.x, to.y, 1);
332    gdk_draw_line(DA, gc_drag, pos.x, pos.y, to.x, to.y);
333    return buf;
334}
335
336
337struct pix_buf *draw_move_line(struct inst *inst, struct coord pos, int i)
338{
339    return draw_move_line_common(inst, inst->u.rect.end, pos, i);
340}
341
342
343static int end_new_line(struct inst *from, struct inst *to)
344{
345    struct obj *obj;
346
347    if (from == to)
348        return 0;
349    obj = new_obj(ot_line, from);
350    obj->u.line.other = inst_get_vec(to);
351    obj->u.line.width = NULL;
352    return 1;
353}
354
355
356static struct tool_ops line_ops = {
357    .drag_new = drag_new_line,
358    .end_new = end_new_line,
359};
360
361
362/* ----- rect -------------------------------------------------------------- */
363
364
365static struct pix_buf *drag_new_rect(struct inst *from, struct coord to)
366{
367    struct coord pos;
368    struct pix_buf *buf;
369
370    pos = translate(inst_get_point(from));
371    to = translate(to);
372    sort_coord(&pos, &to);
373    buf = save_pix_buf(DA, pos.x, pos.y, to.x, to.y, 1);
374    gdk_draw_rectangle(DA, gc_drag, FALSE,
375        pos.x, pos.y, to.x-pos.x, to.y-pos.y);
376    return buf;
377}
378
379
380struct pix_buf *draw_move_rect(struct inst *inst, struct coord pos, int i)
381{
382    return draw_move_rect_common(inst, inst->u.rect.end, pos, i);
383}
384
385
386static int end_new_rect(struct inst *from, struct inst *to)
387{
388    struct obj *obj;
389
390    if (from == to)
391        return 0;
392    obj = new_obj(ot_rect, from);
393    obj->u.rect.other = inst_get_vec(to);
394    obj->u.rect.width = NULL;
395    return 1;
396}
397
398
399static struct tool_ops rect_ops = {
400    .drag_new = drag_new_rect,
401    .end_new = end_new_rect,
402};
403
404
405/* ----- pad --------------------------------------------------------------- */
406
407
408static int end_new_pad(struct inst *from, struct inst *to)
409{
410    struct obj *obj;
411
412    if (from == to)
413        return 0;
414    obj = new_obj(ot_pad, from);
415    obj->u.pad.other = inst_get_vec(to);
416    obj->u.pad.name = stralloc("?");
417    obj->u.pad.rounded = 0;
418    obj->u.pad.type = pt_normal;
419    return 1;
420}
421
422
423struct pix_buf *draw_move_pad(struct inst *inst, struct coord pos, int i)
424{
425    return draw_move_rect_common(inst, inst->u.pad.other, pos, i);
426}
427
428
429static struct tool_ops pad_ops = {
430    .drag_new = drag_new_rect,
431    .end_new = end_new_pad,
432};
433
434
435/* ----- rounded pad ------------------------------------------------------- */
436
437
438static int end_new_rpad(struct inst *from, struct inst *to)
439{
440    struct obj *obj;
441
442    if (from == to)
443        return 0;
444    obj = new_obj(ot_pad, from);
445    obj->u.pad.other = inst_get_vec(to);
446    obj->u.pad.name = stralloc("?");
447    obj->u.pad.rounded = 1;
448    obj->u.pad.type = pt_normal;
449    return 1;
450}
451
452
453struct pix_buf *draw_move_rpad(struct inst *inst, struct coord pos, int i)
454{
455    return draw_move_rect_common(inst, inst->u.pad.other, pos, i);
456}
457
458
459static struct tool_ops rpad_ops = {
460    .drag_new = drag_new_rect,
461    .end_new = end_new_rpad,
462};
463
464
465/* ----- hole -------------------------------------------------------------- */
466
467
468static int end_new_hole(struct inst *from, struct inst *to)
469{
470    struct obj *obj;
471
472    if (from == to)
473        return 0;
474    obj = new_obj(ot_hole, from);
475    obj->u.hole.other = inst_get_vec(to);
476    return 1;
477}
478
479
480struct pix_buf *draw_move_hole(struct inst *inst, struct coord pos, int i)
481{
482    return draw_move_rect_common(inst, inst->u.hole.other, pos, i);
483}
484
485
486static struct tool_ops hole_ops = {
487    .drag_new = drag_new_rect,
488    .end_new = end_new_hole,
489};
490
491
492/* ----- circ -------------------------------------------------------------- */
493
494
495static struct pix_buf *drag_new_circ(struct inst *from, struct coord to)
496{
497    struct coord pos;
498    struct pix_buf *buf;
499    double r;
500
501    pos = translate(inst_get_point(from));
502    to = translate(to);
503    r = hypot(to.x-pos.x, to.y-pos.y);
504    buf = save_pix_buf(DA, pos.x-r, pos.y-r, pos.x+r, pos.y+r, 1);
505    draw_circle(DA, gc_drag, FALSE, pos.x, pos.y, r);
506    return buf;
507}
508
509
510static int end_new_circ(struct inst *from, struct inst *to)
511{
512    struct obj *obj;
513
514    if (from == to)
515        return 0;
516    obj = new_obj(ot_arc, from);
517    obj->u.arc.start = inst_get_vec(to);
518    obj->u.arc.end = inst_get_vec(to);
519    obj->u.arc.width = NULL;
520    return 1;
521}
522
523
524struct pix_buf *draw_move_arc(struct inst *inst, struct coord pos, int i)
525{
526    struct coord c, from, to, end;
527    double r, r_save, a1, a2;
528    struct pix_buf *buf;
529
530    c = translate(inst->base);
531    from =
532        translate(rotate_r(inst->base, inst->u.arc.r, inst->u.arc.a1));
533    to =
534        translate(rotate_r(inst->base, inst->u.arc.r, inst->u.arc.a2));
535    pos = translate(pos);
536    switch (i) {
537    case 0:
538        c = pos;
539        break;
540    case 2:
541        from = pos;
542        break;
543    case 1:
544        to = pos;
545        break;
546    default:
547        abort();
548    }
549    r = hypot(from.x-c.x, from.y-c.y);
550    /*
551     * the screen coordinate system is reversed with y growing downward,
552     * so we have to negate the angles.
553     */
554    a1 = -theta(c, from);
555    a2 = -theta(c, to);
556    if (a2 < a1)
557        a2 += 360.0;
558
559    if (i != 2) {
560        r_save = r;
561    } else {
562        r_save = hypot(to.x-c.x, to.y-c.y);
563        if (r > r_save)
564            r_save = r;
565    }
566    buf = save_pix_buf(DA,
567        c.x-r_save, c.y-r_save, c.x+r_save, c.y+r_save, 1);
568    draw_arc(DA, gc_drag, FALSE, c.x, c.y, r, a1, a2);
569    if (i == 1) {
570        end = rotate_r(c, r_save, -a2);
571        gdk_draw_line(DA, gc_drag, c.x, c.y, end.x, end.y);
572    }
573    return buf;
574}
575
576
577void do_move_to_arc(struct inst *inst, struct inst *to, int i)
578{
579    struct vec *vec = inst_get_vec(to);
580    struct obj *obj = inst->obj;
581
582    switch (i) {
583    case 0:
584        obj->base = vec;
585        break;
586    case 2:
587        obj->u.arc.start = vec;
588        break;
589    case 1:
590        obj->u.arc.end = vec;
591        break;
592    default:
593        abort();
594    }
595}
596
597
598static struct tool_ops circ_ops = {
599    .drag_new = drag_new_circ,
600    .end_new = end_new_circ,
601};
602
603
604/* ----- frame helper ------------------------------------------------------ */
605
606
607int is_parent_of(const struct frame *p, const struct frame *c)
608{
609    const struct obj *obj;
610
611    if (p == c)
612        return 1;
613    for (obj = p->objs; obj; obj = obj->next)
614        if (obj->type == ot_frame)
615            if (is_parent_of(obj->u.frame.ref, c))
616                return 1;
617    return 0;
618}
619
620
621/* ----- frame ------------------------------------------------------------- */
622
623
624static struct frame *locked_frame = NULL;
625
626
627struct pix_buf *draw_move_frame(struct inst *inst, struct coord pos, int i)
628{
629    struct pix_buf *buf;
630    int r = FRAME_EYE_R2;
631
632    pos = translate(pos);
633    buf = save_pix_buf(DA, pos.x-r, pos.y-r, pos.x+r, pos.y+r, 1);
634    draw_arc(DA, gc_drag, FALSE, pos.x, pos.y, r, 0, 360);
635    return buf;
636}
637
638
639struct pix_buf *gui_hover_frame(struct inst *self)
640{
641    return hover_common(gc_frame[mode_hover],
642        self->base, FRAME_EYE_R2);
643}
644
645
646static int end_new_frame(struct inst *from, struct inst *to)
647{
648    struct obj *obj;
649
650    if (!locked_frame || is_parent_of(locked_frame, active_frame))
651        return 0;
652    obj = new_obj(ot_frame, from);
653    obj->u.frame.ref = locked_frame;
654    obj->u.frame.lineno = 0;
655    if (!locked_frame->active_ref)
656        locked_frame->active_ref = obj;
657    locked_frame = NULL;
658    tool_reset();
659    return 1;
660}
661
662
663static struct tool_ops frame_ops = {
664    .tool_selected = NULL,
665    .drag_new = NULL,
666    .end_new = end_new_frame,
667};
668
669
670/* ----- moving references ------------------------------------------------- */
671
672
673#if 0
674static int may_move(struct inst *curr)
675{
676    if (!selected_inst)
677        return 0;
678    if (drag.anchors_n)
679        return 0; /* already moving something else */
680    drag.anchors_n = inst_anchors(selected_inst, drag.anchors);
681    for (drag.anchor_i = 0; drag.anchor_i != drag.anchors_n;
682        drag.anchor_i++)
683        if (*drag.anchors[drag.anchor_i] == inst_get_vec(curr))
684            return 1;
685    drag.anchors_n = 0;
686    return 0;
687}
688#endif
689
690
691static int would_be_equal(const struct drag_state *state,
692    int a, int b, struct inst *curr)
693{
694    const struct vec *va;
695    const struct vec *vb;
696
697    va = a == state->anchor_i ? inst_get_vec(curr) : *state->anchors[a];
698    vb = b == state->anchor_i ? inst_get_vec(curr) : *state->anchors[b];
699    return va == vb;
700}
701
702
703static int may_move_to(const struct drag_state *state, struct inst *curr)
704{
705    assert(drag.inst);
706    assert(state->anchors_n);
707    switch (state->anchors_n) {
708    case 3:
709        if (would_be_equal(state, 0, 2, curr))
710            return 0;
711        /* fall through */
712    case 2:
713        if (would_be_equal(state, 0, 1, curr))
714            return 0;
715        /* fall through */
716    case 1:
717        return 1;
718    default:
719        abort();
720    }
721}
722
723
724static void do_move_to(struct drag_state *state, struct inst *curr)
725{
726    assert(state->inst->ops->find_point || may_move_to(state, curr));
727    *state->anchors[state->anchor_i] = inst_get_vec(curr);
728}
729
730
731/* ----- hover ------------------------------------------------------------- */
732
733
734static struct pix_buf *hover_save_and_draw(void *user)
735{
736    return inst_hover(hover_inst);
737}
738
739
740void tool_dehover(void)
741{
742    if (!hover_inst)
743        return;
744    over_leave();
745    hover_inst = NULL;
746}
747
748
749/*
750 * When hovering, we have to consider the following states:
751 *
752 * selected (selected_inst != NULL)
753 * | dragging new (drag.new != NULL)
754 * | | dragging existing (drag.anchors_n != 0)
755 * | | | tool selected (active_ops)
756 * | | | |
757 * Y N N N highlight if inst_find_point_selected else don't
758 * Y N N Y highlight if inst_find_point_selected else fall over to tool
759 * - Y N - highlight if inst_find_point / active_ops->find_point else don't
760 * - N Y - highlight if may_move_to else don't
761 * - Y Y - invalid state
762 * N N N Y highlight if inst_find_point / active_ops->find_point else don't
763 * N N N N don't highlight
764 */
765
766static struct inst *get_hover_inst(struct coord pos)
767{
768    struct inst *inst;
769    int i;
770
771    if (drag.new) {
772        if (active_ops->find_point)
773            return active_ops->find_point(pos);
774        return inst_find_point(pos);
775    }
776    if (drag.anchors_n) {
777        if (drag.inst->ops->find_point)
778            return drag.inst->ops->find_point(drag.inst, pos);
779        inst = inst_find_point(pos);
780        if (!inst)
781            return NULL;
782        return may_move_to(&drag, inst) ? inst : NULL;
783    }
784    if (selected_inst) {
785        i = inst_find_point_selected(pos, &inst);
786        if (i != -1)
787            return inst;
788    }
789    if (!active_ops)
790        return NULL;
791    if (active_ops->find_point)
792        return active_ops->find_point(pos);
793    return inst_find_point(pos);
794}
795
796
797int tool_hover(struct coord pos)
798{
799    struct inst *curr;
800
801    curr = get_hover_inst(pos);
802#if 0
803    if ((drag.new && curr == drag.new) || (drag.inst && curr == drag.inst))
804        return;
805    if (curr && !active_ops) {
806        if (drag.anchors_n) {
807            if (!may_move_to(&drag, curr))
808                curr = NULL;
809        } else {
810            if (!may_move(curr))
811                curr = NULL;
812            drag.anchors_n = 0;
813        }
814    }
815got:
816#endif
817
818    if (curr == hover_inst)
819        return !!curr;
820    if (hover_inst) {
821        over_leave();
822        hover_inst = NULL;
823    }
824    if (!curr)
825        return 0;
826    hover_inst = curr;
827    over_enter(hover_save_and_draw, NULL);
828    return 1;
829}
830
831
832/* ----- frame drag and drop ----------------------------------------------- */
833
834
835/*
836 * When dragging a frame, we temporarily replace the selected tool (if any)
837 * with the frame tool.
838 */
839
840
841static struct tool_ops *pushed_ops;
842
843
844void tool_push_frame(struct frame *frame)
845{
846    pushed_ops = active_ops;
847    locked_frame = frame;
848    active_ops = &frame_ops;
849    /*
850     * We don't need to call tool_selected since, with drag and drop, the
851     * frame tools doesn't need activation anymore.
852     */
853}
854
855
856static int do_place_frame(struct frame *frame, struct coord pos)
857{
858    if (!get_hover_inst(pos))
859        return 0;
860    tool_consider_drag(pos);
861    return 1;
862}
863
864
865/*
866 * Gtk calls drag-leave, drag-end, and only then drag-drop. So we'll already
867 * have cleaned up in tool_pop_frame before we get here. In order to place the
868 * frame, we need to activate the frame tool again.
869 *
870 * @@@ bug: there's a tool_reset in this path, so we'll lose the widget of the
871 * tool that's really active. This problem will vanish when scrapping the
872 * old-style frame referenes.
873 */
874
875int tool_place_frame(struct frame *frame, struct coord pos)
876{
877    int ok;
878
879    active_ops = &frame_ops;
880    ok = do_place_frame(frame, pos);
881    active_ops = pushed_ops;
882    return ok;
883}
884
885
886void tool_pop_frame(void)
887{
888    if (!active_tool)
889        return;
890    active_ops = pushed_ops;
891    /*
892     * We don't need to call tool_selected since the only tool that could
893     * use this would be the delete tool, and there the semantics would be
894     * undesirable. Also, the delete tool never stays active, so it can't
895     * appear together with drag and drop anyway.
896     */
897}
898
899
900/* ----- tooltip ----------------------------------------------------------- */
901
902
903const char *tool_tip(struct coord pos)
904{
905    struct inst *inst;
906
907    inst = get_hover_inst(pos);
908    if (!inst)
909        return NULL;
910
911    /*
912     * The logic below follows exactly what happens in get_hover_inst.
913     */
914
915    if (drag.new)
916        return "End here";
917    if (drag.anchors_n)
918        return "Move here";
919    if (selected_inst)
920        return "Move this point";
921    return "Start here";
922}
923
924
925/* ----- mouse actions ----------------------------------------------------- */
926
927
928static struct pix_buf *drag_save_and_draw(void *user, struct coord to)
929{
930    if (drag.new) {
931        assert(active_ops);
932        return active_ops->drag_new(drag.new, to);
933    } else {
934        return inst_draw_move(drag.inst, to, drag.anchor_i);
935    }
936}
937
938
939/*
940 * When considering dragging, we have the following states:
941 *
942 * selected (selected_inst != NULL)
943 * | tool selected (active_ops)
944 * | |
945 * N N don't
946 * Y - if we could drag, drag_new/end_new, else fall over to tool
947 * N Y else single-click creation, else drag_new/end_new
948 */
949
950int tool_consider_drag(struct coord pos)
951{
952    struct inst *curr;
953
954    assert(!drag.new);
955    assert(!drag.anchors_n);
956    last_canvas_pos = translate(pos);
957
958    if (!selected_inst && !active_ops)
959        return 0;
960    if (selected_inst) {
961        drag.anchor_i = inst_find_point_selected(pos, NULL);
962        if (drag.anchor_i != -1) {
963            tool_dehover();
964            drag.inst = selected_inst;
965            drag.new = NULL;
966            drag.anchors_n =
967                inst_anchors(selected_inst, drag.anchors);
968            over_begin(drag_save_and_draw, NULL, pos);
969            inst_begin_drag_move(selected_inst, drag.anchor_i);
970            return 1;
971        }
972    }
973    if (!active_ops)
974        return 0;
975
976    curr = get_hover_inst(pos);
977    if (!curr)
978        return 0;
979
980    tool_dehover();
981
982    if (active_ops->drag_new) {
983        if (active_ops->begin_drag_new)
984            active_ops->begin_drag_new(curr);
985        drag.inst = NULL;
986        drag.new = curr;
987        over_begin(drag_save_and_draw, NULL, pos);
988        return 1;
989    }
990
991    /* object is created without dragging */
992    if (active_ops->end_new(curr, NULL))
993        return -1;
994    return 0;
995
996}
997
998
999void tool_drag(struct coord to)
1000{
1001    last_canvas_pos = translate(to);
1002    over_move(to);
1003}
1004
1005
1006void tool_cancel_drag(void)
1007{
1008    if (drag.anchors_n && drag.inst->ops->end_drag_move)
1009        drag.inst->ops->end_drag_move();
1010    drag.new = NULL;
1011    active_ops = NULL;
1012    drag.anchors_n = 0;
1013    over_end();
1014    tool_dehover();
1015    tool_reset();
1016}
1017
1018
1019int tool_end_drag(struct coord to)
1020{
1021    struct drag_state state = drag;
1022    struct inst *end;
1023    struct tool_ops *ops = active_ops;
1024
1025    tool_cancel_drag();
1026    if (state.new && ops->end_new_raw)
1027        return ops->end_new_raw(state.new, to);
1028    if (state.new && ops->find_point) {
1029        end = ops->find_point(to);
1030    } else {
1031        if (state.inst && state.inst->ops->find_point)
1032            end = state.inst->ops->find_point(state.inst, to);
1033        else
1034            end = inst_find_point(to);
1035    }
1036    if (!end) {
1037        if (state.new && ops->cancel_drag_new)
1038            ops->cancel_drag_new();
1039        return 0;
1040    }
1041    if (state.new)
1042        return ops->end_new(state.new, end);
1043
1044    /* if we got the point from find_point, it's authoritative */
1045    if (!state.inst->ops->find_point && !may_move_to(&state, end))
1046        return 0;
1047    if (!inst_do_move_to(state.inst, end, state.anchor_i))
1048        do_move_to(&state, end);
1049    return 1;
1050}
1051
1052
1053void tool_redraw(void)
1054{
1055    struct coord pos;
1056
1057    over_reset();
1058    hover_inst = NULL;
1059    if (!drag.new && !drag.anchors_n)
1060        return;
1061    pos = canvas_to_coord(last_canvas_pos.x, last_canvas_pos.y);
1062    tool_hover(pos);
1063    over_begin(drag_save_and_draw, NULL, pos);
1064}
1065
1066
1067/* ----- Retrieve icons by instance characteristics ------------------------ */
1068
1069
1070GtkWidget *get_icon_by_inst(const struct inst *inst)
1071{
1072    char **image;
1073
1074    switch (inst->prio) {
1075    case ip_frame:
1076        image = xpm_frame;
1077        break;
1078    case ip_pad_copper:
1079    case ip_pad_special:
1080        image = inst->obj->u.pad.rounded ? xpm_rpad : xpm_pad;
1081        break;
1082    case ip_hole:
1083        image = xpm_hole;
1084        break;
1085    case ip_circ:
1086        image = xpm_circ;
1087        break;
1088    case ip_arc:
1089        image = xpm_arc;
1090        break;
1091    case ip_rect:
1092        image = xpm_rect;
1093        break;
1094    case ip_meas:
1095        switch (inst->obj->u.meas.type) {
1096        case mt_xy_next:
1097        case mt_xy_max:
1098            image = xpm_meas;
1099            break;
1100        case mt_x_next:
1101        case mt_x_max:
1102            image = xpm_meas_x;
1103            break;
1104        case mt_y_next:
1105        case mt_y_max:
1106            image = xpm_meas_y;
1107            break;
1108        default:
1109            abort();
1110        }
1111        break;
1112    case ip_line:
1113        image = xpm_line;
1114        break;
1115    case ip_vec:
1116        image = xpm_vec;
1117        break;
1118    default:
1119        abort();
1120    }
1121    return make_transparent_image(DA, image, NULL);
1122}
1123
1124
1125/* ----- tool bar creation ------------------------------------------------- */
1126
1127
1128static void tool_select(GtkWidget *evbox, struct tool_ops *ops)
1129{
1130    GdkColor col;
1131
1132    if (active_tool) {
1133        if (active_ops && active_ops->tool_deselected)
1134            active_ops->tool_deselected();
1135        col = get_color(COLOR_TOOL_UNSELECTED);
1136        gtk_widget_modify_bg(active_tool, GTK_STATE_NORMAL, &col);
1137        active_tool = NULL;
1138    }
1139    col = get_color(COLOR_TOOL_SELECTED);
1140    gtk_widget_modify_bg(evbox, GTK_STATE_NORMAL, &col);
1141    active_tool = evbox;
1142    active_ops = ops;
1143    if (ops && ops->tool_selected)
1144        ops->tool_selected();
1145}
1146
1147
1148void tool_reset(void)
1149{
1150    over_reset();
1151    hover_inst = NULL;
1152    tool_select(ev_point, NULL);
1153}
1154
1155
1156static gboolean tool_button_press_event(GtkWidget *widget,
1157    GdkEventButton *event, gpointer data)
1158{
1159    switch (event->button) {
1160    case 1:
1161        tool_select(widget, data);
1162        break;
1163    }
1164    return TRUE;
1165}
1166
1167
1168static void tool_separator(GtkWidget *bar)
1169{
1170    GtkToolItem *item;
1171
1172    item = gtk_separator_tool_item_new();
1173    gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(item), FALSE);
1174    gtk_toolbar_insert(GTK_TOOLBAR(bar), item, -1);
1175}
1176
1177
1178GtkWidget *gui_setup_tools(GdkDrawable *drawable)
1179{
1180    GtkWidget *bar;
1181
1182    bar = gtk_toolbar_new();
1183    gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_ICONS);
1184    gtk_toolbar_set_orientation(GTK_TOOLBAR(bar),
1185        GTK_ORIENTATION_VERTICAL);
1186
1187    ev_point = tool_button(bar, drawable, xpm_point,
1188        "Select and move items",
1189        tool_button_press_event, NULL);
1190    ev_delete = tool_button(bar, drawable, NULL, NULL,
1191         tool_button_press_event, &delete_ops);
1192    tool_separator(bar);
1193    tool_button(bar, drawable, xpm_vec,
1194        "Add a vector",
1195        tool_button_press_event, &vec_ops);
1196    tool_button(bar, drawable, xpm_pad,
1197        "Add a rectangular pad",
1198        tool_button_press_event, &pad_ops);
1199    tool_button(bar, drawable, xpm_rpad,
1200        "Add a rounded pad",
1201        tool_button_press_event, &rpad_ops);
1202    tool_button(bar, drawable, xpm_hole,
1203        "Add a hole",
1204        tool_button_press_event, &hole_ops);
1205    tool_button(bar, drawable, xpm_line,
1206        "Add a silk screen line",
1207        tool_button_press_event, &line_ops);
1208    tool_button(bar, drawable, xpm_rect,
1209        "Add a silk screen rectangle",
1210        tool_button_press_event, &rect_ops);
1211    tool_button(bar, drawable, xpm_circ,
1212        "Add a silk screen circle or arc",
1213        tool_button_press_event, &circ_ops);
1214    tool_separator(bar);
1215    tool_button(bar, drawable, xpm_meas,
1216        "Add a measurement",
1217        tool_button_press_event, &tool_meas_ops);
1218    tool_button(bar, drawable, xpm_meas_x,
1219        "Add a horizontal measurement",
1220        tool_button_press_event, &tool_meas_ops_x);
1221    tool_button(bar, drawable, xpm_meas_y,
1222        "Add a vertical measurement",
1223        tool_button_press_event, &tool_meas_ops_y);
1224
1225    delete_image[0] = gtk_widget_ref(make_image(drawable, xpm_delete_off,
1226        NULL));
1227    delete_image[1] = gtk_widget_ref(make_image(drawable, xpm_delete,
1228        "Delete the selected item"));
1229    set_image(ev_delete, delete_image[0]);
1230
1231    tool_reset();
1232
1233    return bar;
1234}
1235
1236
1237void gui_cleanup_tools(void)
1238{
1239    g_object_unref(delete_image[0]);
1240    g_object_unref(delete_image[1]);
1241}
1242

Archive Download this file

Branches:
master



interactive