Root/
Source at commit 1bc94d04454b6c5ef2b37d0b09eace711007884b created 11 years 3 months ago. By Werner Almesberger, Merge branch 'master' of projects.qi-hardware.com:fped | |
---|---|
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 | |
51 | static GtkWidget *ev_point, *ev_delete; |
52 | static GtkWidget *active_tool; |
53 | static struct tool_ops *active_ops = NULL; |
54 | static struct inst *hover_inst = NULL; |
55 | static GtkWidget *delete_image[2]; |
56 | |
57 | static 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 | |
68 | static struct coord last_canvas_pos; |
69 | |
70 | |
71 | static 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 | |
87 | struct 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 | |
102 | void 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 | |
112 | static 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 | |
125 | struct 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 | |
150 | static 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 | |
177 | static 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 | |
192 | static 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 | |
203 | static struct tool_ops delete_ops = { |
204 | .tool_selected = tool_selected_delete, |
205 | }; |
206 | |
207 | |
208 | void tool_selected_inst(struct inst *inst) |
209 | { |
210 | set_image(ev_delete, delete_image[inst != NULL]); |
211 | } |
212 | |
213 | |
214 | /* ----- vec --------------------------------------------------------------- */ |
215 | |
216 | |
217 | static 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 | |
245 | static 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 | |
276 | struct 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 | |
283 | struct 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 | |
290 | static 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 | |
315 | static 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 | |
324 | struct 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 | |
337 | struct 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 | |
343 | static 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 | |
356 | static struct tool_ops line_ops = { |
357 | .drag_new = drag_new_line, |
358 | .end_new = end_new_line, |
359 | }; |
360 | |
361 | |
362 | /* ----- rect -------------------------------------------------------------- */ |
363 | |
364 | |
365 | static 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 | |
380 | struct 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 | |
386 | static 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 | |
399 | static struct tool_ops rect_ops = { |
400 | .drag_new = drag_new_rect, |
401 | .end_new = end_new_rect, |
402 | }; |
403 | |
404 | |
405 | /* ----- pad --------------------------------------------------------------- */ |
406 | |
407 | |
408 | static 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 | |
423 | struct 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 | |
429 | static 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 | |
438 | static 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 | |
453 | struct 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 | |
459 | static struct tool_ops rpad_ops = { |
460 | .drag_new = drag_new_rect, |
461 | .end_new = end_new_rpad, |
462 | }; |
463 | |
464 | |
465 | /* ----- hole -------------------------------------------------------------- */ |
466 | |
467 | |
468 | static 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 | |
480 | struct 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 | |
486 | static struct tool_ops hole_ops = { |
487 | .drag_new = drag_new_rect, |
488 | .end_new = end_new_hole, |
489 | }; |
490 | |
491 | |
492 | /* ----- circ -------------------------------------------------------------- */ |
493 | |
494 | |
495 | static 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 | |
510 | static 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 | |
524 | struct 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 | |
577 | void 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 | |
598 | static 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 | |
607 | int 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 | |
624 | static struct frame *locked_frame = NULL; |
625 | |
626 | |
627 | struct 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 | |
639 | struct 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 | |
646 | static 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 | |
663 | static 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 |
674 | static 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 | |
691 | static 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 | |
703 | static 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 | |
724 | static 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 | |
734 | static struct pix_buf *hover_save_and_draw(void *user) |
735 | { |
736 | return inst_hover(hover_inst); |
737 | } |
738 | |
739 | |
740 | void 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 | |
766 | static 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 | |
797 | int 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 | } |
815 | got: |
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 | |
841 | static struct tool_ops *pushed_ops; |
842 | |
843 | |
844 | void 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 | |
856 | static 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 | |
875 | int 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 | |
886 | void 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 | |
903 | const 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 | |
928 | static 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 | |
950 | int 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 | |
999 | void tool_drag(struct coord to) |
1000 | { |
1001 | last_canvas_pos = translate(to); |
1002 | over_move(to); |
1003 | } |
1004 | |
1005 | |
1006 | void 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 | |
1019 | int 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 | |
1053 | void 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 | |
1070 | GtkWidget *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 | |
1128 | static 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 | |
1148 | void tool_reset(void) |
1149 | { |
1150 | over_reset(); |
1151 | hover_inst = NULL; |
1152 | tool_select(ev_point, NULL); |
1153 | } |
1154 | |
1155 | |
1156 | static 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 | |
1168 | static 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 | |
1178 | GtkWidget *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 | |
1237 | void gui_cleanup_tools(void) |
1238 | { |
1239 | g_object_unref(delete_image[0]); |
1240 | g_object_unref(delete_image[1]); |
1241 | } |
1242 |
Branches:
master