Root/
Source at commit 228f7b47f7d97c7aa4ce8b072f3575226ec294ce created 8 years 6 days ago. By Werner Almesberger, Makefile (man): generate and display the man page | |
---|---|
1 | /* |
2 | * inst.c - Instance structures |
3 | * |
4 | * Written 2009-2012, 2015 by Werner Almesberger |
5 | * Copyright 2009-2012, 2015 by Werner Almesberger |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | */ |
12 | |
13 | |
14 | #include <stdlib.h> |
15 | #include <stdio.h> |
16 | #include <math.h> |
17 | |
18 | #include "util.h" |
19 | #include "error.h" |
20 | #include "coord.h" |
21 | #include "expr.h" |
22 | #include "layer.h" |
23 | #include "obj.h" |
24 | #include "delete.h" |
25 | #include "gui_util.h" |
26 | #include "gui_status.h" |
27 | #include "gui_canvas.h" |
28 | #include "gui_tool.h" |
29 | #include "gui_meas.h" |
30 | #include "gui_inst.h" |
31 | #include "gui_frame.h" |
32 | #include "gui.h" |
33 | #include "inst.h" |
34 | |
35 | |
36 | struct inst *selected_inst = NULL; |
37 | struct bbox active_frame_bbox; |
38 | struct pkg *pkgs, *active_pkg, *curr_pkg; |
39 | struct pkg *reachable_pkg = NULL; |
40 | struct inst *frame_instantiating = NULL; |
41 | |
42 | static struct pkg *prev_pkgs, *prev_reachable_pkg; |
43 | |
44 | static unsigned long active_set = 0; |
45 | |
46 | static struct inst_ops vec_ops; |
47 | static struct inst_ops frame_ops; |
48 | static struct inst_ops meas_ops; |
49 | |
50 | |
51 | #define IS_ACTIVE ((active_set & 1)) |
52 | |
53 | |
54 | /* ----- selective visibility ---------------------------------------------- */ |
55 | |
56 | |
57 | static int show(enum inst_prio prio) |
58 | { |
59 | switch (prio) { |
60 | case ip_vec: |
61 | case ip_frame: |
62 | return show_stuff; |
63 | case ip_meas: |
64 | return show_meas; |
65 | default: |
66 | return 1; |
67 | } |
68 | } |
69 | |
70 | |
71 | int bright(const struct inst *inst) |
72 | { |
73 | if (!show_bright) |
74 | return 0; |
75 | return inst->ops != &vec_ops && inst->ops != &frame_ops && |
76 | inst->ops != &meas_ops; |
77 | } |
78 | |
79 | |
80 | static int show_this(const struct inst *inst) |
81 | { |
82 | if (show_all) |
83 | return 1; |
84 | if (inst->ops == &frame_ops && inst->u.frame.ref == active_frame) |
85 | return 1; |
86 | if (!inst->outer) |
87 | return active_frame == frames; |
88 | return inst->outer->u.frame.ref == active_frame; |
89 | } |
90 | |
91 | |
92 | /* ----- selection of items not on the canvas ------------------------------ */ |
93 | |
94 | |
95 | static void *selected_outside = NULL; |
96 | static void (*outside_deselect)(void *item); |
97 | |
98 | |
99 | static void deselect_outside(void) |
100 | { |
101 | if (selected_outside && outside_deselect) |
102 | outside_deselect(selected_outside); |
103 | selected_outside = NULL; |
104 | } |
105 | |
106 | |
107 | void inst_select_outside(void *item, void (*deselect)(void *item)) |
108 | { |
109 | if (item == selected_outside) |
110 | return; |
111 | deselect_outside(); |
112 | inst_deselect(); |
113 | selected_outside = item; |
114 | outside_deselect = deselect; |
115 | } |
116 | |
117 | |
118 | /* ----- check connectedness ----------------------------------------------- */ |
119 | |
120 | |
121 | /* |
122 | * After an instantiation failure, the instances can get out of sync with the |
123 | * object tree, and attempts to select an item on the canvas can cause accesses |
124 | * to objects that aren't there anymore. So we need to check if we can still |
125 | * reach the corresponding object. |
126 | * |
127 | * Note: even this isn't bullet-proof. Theoretically, we may get a new object |
128 | * in the old place. However, this probably doesn't do any serious damage. |
129 | */ |
130 | |
131 | |
132 | static int inst_connected(const struct inst *inst) |
133 | { |
134 | const struct frame *frame; |
135 | const struct vec *vec; |
136 | const struct obj *obj; |
137 | |
138 | for (frame = frames; frame; frame = frame->next) { |
139 | if (inst->ops == &vec_ops) { |
140 | for (vec = frame->vecs; vec; vec = vec->next) |
141 | if (vec == inst->vec) |
142 | return 1; |
143 | } else { |
144 | for (obj = frame->objs; obj; obj = obj->next) |
145 | if (obj == inst->obj) |
146 | return 1; |
147 | } |
148 | } |
149 | return 0; |
150 | } |
151 | |
152 | |
153 | /* ----- selection --------------------------------------------------------- */ |
154 | |
155 | |
156 | static void inst_select_inst(struct inst *inst) |
157 | { |
158 | selected_inst = inst; |
159 | tool_selected_inst(inst); |
160 | gui_frame_select_inst(inst); |
161 | if (inst->ops->select) |
162 | selected_inst->ops->select(inst); |
163 | status_set_icon(get_icon_by_inst(inst)); |
164 | } |
165 | |
166 | |
167 | /* |
168 | * @@@ This logic is overly complicated and should be simplified. The general |
169 | * idea was to avoid making unnecessary changes to the user's selections, but |
170 | * that risk doesn't exist. Furthermore, the way activate_item is used, its |
171 | * preconditions aren't met. It works anyway but it could be simpler as a |
172 | * consequence. |
173 | * |
174 | * activate_item tries to activate the path through the frame references, |
175 | * leading to a specific instance. It returns whether this is failed or whether |
176 | * it may have been successful. |
177 | * |
178 | * The initial condition is that we want to activate an item on a frame |
179 | * instance that's not active. Since the frame has been instantiated, there |
180 | * must be a way to activate it. We just have to find out how. |
181 | * |
182 | * The first test eliminates the root frame. If we're at the root frame and |
183 | * still haven't figured out what to do, something is wrong and we give up. |
184 | * |
185 | * The next test skips references that are already right. Since we know that |
186 | * there must be at least one reference that leads elsewhere, and we haven't |
187 | * found it yet, the recursion will tell us whether it can find it at all. |
188 | * |
189 | * Finally, if we've found a mismatch, we correct it. We then try to fix any |
190 | * further mismatches. Since we've made progress, we return 1, even if the |
191 | * other fixes should fail (or reach the root frame). |
192 | * |
193 | */ |
194 | |
195 | static int activate_item(struct inst *inst) |
196 | { |
197 | if (!inst->outer) |
198 | return 0; |
199 | if (inst->outer->u.frame.ref->active_ref == inst->outer->obj) |
200 | return activate_item(inst->outer); |
201 | inst->outer->u.frame.ref->active_ref = inst->outer->obj; |
202 | activate_item(inst->outer); |
203 | return 1; |
204 | } |
205 | |
206 | |
207 | static int __inst_select(struct coord pos, int tries) |
208 | { |
209 | enum inst_prio prio; |
210 | const struct inst *prev; |
211 | struct inst *inst; |
212 | struct inst *first = NULL; /* first active item */ |
213 | struct inst *next = NULL; /* active item after currently sel. */ |
214 | struct inst *any_first = NULL; /* first item, active or inactive */ |
215 | struct inst *any_same_frame = NULL; /* first item on active frame */ |
216 | struct frame *frame; |
217 | int best_dist = 0; /* keep gcc happy */ |
218 | int select_next; |
219 | int dist, i; |
220 | |
221 | if (!tries) { |
222 | fprintf(stderr, "__inst_select: tries exhausted\n"); |
223 | return 0; |
224 | } |
225 | prev = selected_inst; |
226 | deselect_outside(); |
227 | edit_nothing(); |
228 | if (selected_inst) { |
229 | gui_frame_deselect_inst(selected_inst); |
230 | tool_selected_inst(NULL); |
231 | } |
232 | inst_deselect(); |
233 | select_next = 0; |
234 | FOR_INST_PRIOS_DOWN(prio) { |
235 | if (!show(prio)) |
236 | continue; |
237 | FOR_ALL_INSTS(i, prio, inst) { |
238 | if (!show_this(inst)) |
239 | continue; |
240 | if (!inst->ops->distance) |
241 | continue; |
242 | if (!inst_connected(inst)) |
243 | continue; |
244 | dist = inst->ops->distance(inst, pos, draw_ctx.scale); |
245 | if (dist >= 0) { |
246 | if (!any_first) |
247 | any_first = inst; |
248 | if (!any_same_frame && inst->outer && |
249 | inst->outer->u.frame.ref == active_frame) |
250 | any_same_frame = inst; |
251 | if (!inst->active) |
252 | continue; |
253 | if (!first) |
254 | first = inst; |
255 | if (!next && select_next) |
256 | next = inst; |
257 | if (inst == prev) |
258 | select_next = 1; |
259 | if (!selected_inst || best_dist > dist) { |
260 | selected_inst = inst; |
261 | best_dist = dist; |
262 | } |
263 | } |
264 | } |
265 | } |
266 | if (select_next) { |
267 | selected_inst = next ? next : first; |
268 | goto selected; |
269 | } |
270 | if (selected_inst) |
271 | goto selected; |
272 | |
273 | /* give vectors a second chance */ |
274 | |
275 | if (show_stuff) { |
276 | FOR_ALL_INSTS(i, ip_vec, inst) { |
277 | if (!inst->active) |
278 | continue; |
279 | if (!inst_connected(inst)) |
280 | continue; |
281 | dist = gui_dist_vec_fallback(inst, pos, draw_ctx.scale); |
282 | if (dist >= 0 && (!selected_inst || best_dist > dist)) { |
283 | selected_inst = inst; |
284 | best_dist = dist; |
285 | } |
286 | } |
287 | |
288 | if (selected_inst) |
289 | goto selected; |
290 | } |
291 | |
292 | if (!show_all) |
293 | return 0; |
294 | |
295 | if (any_same_frame) { |
296 | activate_item(any_same_frame); |
297 | search_inst(any_same_frame); |
298 | instantiate(); |
299 | change_world(); |
300 | return __inst_select(pos, tries-1); |
301 | } |
302 | if (any_first) { |
303 | frame = any_first->outer ? any_first->outer->u.frame.ref : NULL; |
304 | if (frame != active_frame) { |
305 | select_frame(frame); |
306 | return __inst_select(pos, tries-1); |
307 | } |
308 | } |
309 | |
310 | return 0; |
311 | |
312 | selected: |
313 | inst_select_inst(selected_inst); |
314 | return 1; |
315 | } |
316 | |
317 | |
318 | int inst_select(struct coord pos) |
319 | { |
320 | /* |
321 | * We shouldn't need more than 2 tries to select any item, so 5 is more |
322 | * than enough. This can still fail, but then it would for any number |
323 | * of tries. |
324 | */ |
325 | return __inst_select(pos, 5); |
326 | } |
327 | |
328 | |
329 | struct inst *inst_find_point(struct coord pos) |
330 | { |
331 | struct inst *inst, *found; |
332 | int best_dist = 0; /* keep gcc happy */ |
333 | int dist, i; |
334 | |
335 | found = NULL; |
336 | FOR_ALL_INSTS(i, ip_frame, inst) { |
337 | if (!inst->u.frame.active) |
338 | continue; |
339 | dist = gui_dist_frame_eye(inst, pos, draw_ctx.scale); |
340 | if (dist >= 0 && (!found || best_dist > dist)) { |
341 | found = inst; |
342 | best_dist = dist; |
343 | } |
344 | } |
345 | if (found) |
346 | return found; |
347 | |
348 | FOR_ALL_INSTS(i, ip_vec, inst) { |
349 | if (!inst->active || !inst->ops->distance) |
350 | continue; |
351 | dist = inst->ops->distance(inst, pos, draw_ctx.scale); |
352 | if (dist >= 0 && (!found || best_dist > dist)) { |
353 | found = inst; |
354 | best_dist = dist; |
355 | } |
356 | } |
357 | return found; |
358 | } |
359 | |
360 | |
361 | int inst_find_point_selected(struct coord pos, struct inst **res) |
362 | { |
363 | struct vec **anchors[3]; |
364 | int n, best_i, i; |
365 | struct inst *best = NULL; |
366 | struct inst *inst; |
367 | int d_min, d, j; |
368 | |
369 | assert(selected_inst); |
370 | n = inst_anchors(selected_inst, anchors); |
371 | for (i = 0; i != n; i++) { |
372 | if (*anchors[i]) { |
373 | FOR_ALL_INSTS(j, ip_vec, inst) { |
374 | if (inst->vec != *anchors[i]) |
375 | continue; |
376 | d = gui_dist_vec(inst, pos, draw_ctx.scale); |
377 | if (d != -1 && (!best || d < d_min)) { |
378 | best = inst; |
379 | best_i = i; |
380 | d_min = d; |
381 | } |
382 | } |
383 | } else { |
384 | FOR_ALL_INSTS(j, ip_frame, inst) { |
385 | if (inst != selected_inst->outer) |
386 | continue; |
387 | d = gui_dist_frame(inst, pos, draw_ctx.scale); |
388 | if (d != -1 && (!best || d < d_min)) { |
389 | best = inst; |
390 | best_i = i; |
391 | d_min = d; |
392 | } |
393 | } |
394 | } |
395 | } |
396 | if (!best) |
397 | return -1; |
398 | if (res) |
399 | *res = best; |
400 | return best_i; |
401 | } |
402 | |
403 | |
404 | struct coord inst_get_point(const struct inst *inst) |
405 | { |
406 | if (inst->ops == &vec_ops) |
407 | return inst->u.vec.end; |
408 | if (inst->ops == &frame_ops) |
409 | return inst->base; |
410 | abort(); |
411 | } |
412 | |
413 | |
414 | struct vec *inst_get_vec(const struct inst *inst) |
415 | { |
416 | if (inst->ops == &vec_ops) |
417 | return inst->vec; |
418 | if (inst->ops == &frame_ops) |
419 | return NULL; |
420 | abort(); |
421 | } |
422 | |
423 | |
424 | int inst_anchors(struct inst *inst, struct vec ***anchors) |
425 | { |
426 | if (inst->vec) { |
427 | anchors[0] = &inst->vec->base; |
428 | return 1; |
429 | } |
430 | return obj_anchors(inst->obj, anchors); |
431 | } |
432 | |
433 | |
434 | void inst_deselect(void) |
435 | { |
436 | if (selected_inst) { |
437 | tool_selected_inst(NULL); |
438 | gui_frame_deselect_inst(selected_inst); |
439 | } |
440 | deselect_outside(); |
441 | status_set_type_x(NULL, ""); |
442 | status_set_type_y(NULL, ""); |
443 | status_set_type_entry(NULL, ""); |
444 | status_set_name(NULL, ""); |
445 | status_set_x(NULL, ""); |
446 | status_set_y(NULL, ""); |
447 | status_set_r(NULL, ""); |
448 | status_set_angle(NULL, ""); |
449 | selected_inst = NULL; |
450 | edit_nothing(); |
451 | refresh_pos(); |
452 | status_set_icon(NULL); |
453 | } |
454 | |
455 | |
456 | /* ----- select instance by vector/object ---------------------------------- */ |
457 | |
458 | |
459 | static void vec_edit(struct vec *vec); |
460 | static void obj_edit(struct obj *obj); |
461 | |
462 | |
463 | void inst_select_vec(struct vec *vec) |
464 | { |
465 | struct inst *inst; |
466 | int i; |
467 | |
468 | if (vec->frame != active_frame) |
469 | select_frame(vec->frame); |
470 | FOR_ALL_INSTS(i, ip_vec, inst) |
471 | if (inst->vec == vec && inst->active) { |
472 | inst_deselect(); |
473 | inst_select_inst(inst); |
474 | return; |
475 | } |
476 | vec_edit(vec); |
477 | } |
478 | |
479 | |
480 | void inst_select_obj(struct obj *obj) |
481 | { |
482 | enum inst_prio prio; |
483 | struct inst *inst; |
484 | int i; |
485 | |
486 | if (obj->frame != active_frame) |
487 | select_frame(obj->frame); |
488 | FOR_INST_PRIOS_DOWN(prio) |
489 | FOR_ALL_INSTS(i, prio, inst) |
490 | if (inst->obj && inst->obj == obj && inst->active) |
491 | goto found; |
492 | obj_edit(obj); |
493 | return; |
494 | |
495 | found: |
496 | inst_deselect(); |
497 | inst_select_inst(inst); |
498 | } |
499 | |
500 | |
501 | /* ----- common status reporting ------------------------------------------- */ |
502 | |
503 | |
504 | static void rect_status(struct coord a, struct coord b, unit_type width, |
505 | int rounded) |
506 | { |
507 | const char *tip; |
508 | struct coord d = sub_vec(b, a); |
509 | double r; |
510 | unit_type diag; |
511 | |
512 | status_set_xy(d); |
513 | tip = "Angle of diagonal"; |
514 | if (!d.x && !d.y) { |
515 | status_set_angle(tip, "a = 0 deg"); |
516 | } else { |
517 | status_set_angle(tip, "a = %3.1f deg", theta(a, b)); |
518 | } |
519 | if (d.x < 0) |
520 | d.x = -d.x; |
521 | if (d.y < 0) |
522 | d.y = -d.y; |
523 | diag = hypot(d.x, d.y); |
524 | if (rounded) { |
525 | /* |
526 | * Only consider the part of the diagonal that is on the pad |
527 | * surface. |
528 | * |
529 | * The circle: (x-r)^2+(y-r)^2 = r^2 |
530 | * The diagonal: x = t*cos(theta), y = t*sin(theta) |
531 | * |
532 | * t is the distance from the corner of the surrounding |
533 | * rectangle to the half-circle: |
534 | * |
535 | * t = 2*r*(s+c-sqrt(2*s*c)) |
536 | * |
537 | * With s = sin(theta) and c = cos(theta). |
538 | * |
539 | * Since d.x = diag*cos(theta), we don't need to calculate the |
540 | * sinus and cosinus but can use d.x and d.y directly. |
541 | */ |
542 | r = (d.x > d.y ? d.y : d.x)/2; |
543 | diag -= 2*r*(d.x+d.y-sqrt(2*d.x*d.y))/diag; |
544 | } |
545 | set_with_units(status_set_r, "d = ", diag, "Length of diagonal"); |
546 | if (width != -1) { |
547 | status_set_type_entry(NULL, "width ="); |
548 | set_with_units(status_set_name, "", width, "Line width"); |
549 | } |
550 | } |
551 | |
552 | |
553 | static void rect_status_sort(struct coord a, struct coord b, unit_type width, |
554 | int rounded) |
555 | { |
556 | sort_coord(&a, &b); |
557 | rect_status(a, b, width, rounded); |
558 | } |
559 | |
560 | |
561 | /* ----- helper functions for instance creation ---------------------------- */ |
562 | |
563 | |
564 | static void update_bbox(struct bbox *bbox, struct coord coord) |
565 | { |
566 | if (bbox->min.x > coord.x) |
567 | bbox->min.x = coord.x; |
568 | if (bbox->max.x < coord.x) |
569 | bbox->max.x = coord.x; |
570 | if (bbox->min.y > coord.y) |
571 | bbox->min.y = coord.y; |
572 | if (bbox->max.y < coord.y) |
573 | bbox->max.y = coord.y; |
574 | } |
575 | |
576 | |
577 | static void propagate_bbox(const struct inst *inst) |
578 | { |
579 | struct inst *frame = frame_instantiating ? |
580 | frame_instantiating : curr_pkg->insts[ip_frame]; |
581 | |
582 | update_bbox(&frame->bbox, inst->bbox.min); |
583 | update_bbox(&frame->bbox, inst->bbox.max); |
584 | |
585 | if (curr_pkg->bbox.min.x || curr_pkg->bbox.min.y || |
586 | curr_pkg->bbox.max.x || curr_pkg->bbox.max.y) { |
587 | update_bbox(&curr_pkg->bbox, inst->bbox.min); |
588 | update_bbox(&curr_pkg->bbox, inst->bbox.max); |
589 | } else { |
590 | curr_pkg->bbox = inst->bbox; |
591 | } |
592 | } |
593 | |
594 | |
595 | static void grow_bbox_by_width(struct bbox *bbox, unit_type width) |
596 | { |
597 | bbox->min.x -= width/2; |
598 | bbox->min.y -= width/2; |
599 | bbox->max.x += width/2; |
600 | bbox->max.y += width/2; |
601 | } |
602 | |
603 | |
604 | static int zero_sized(struct coord a, struct coord b, const char *fmt, |
605 | const char *arg) |
606 | { |
607 | if (a.x == b.x && a.y == b.y) { |
608 | fail(fmt, "zero-sized", arg); |
609 | return 1; |
610 | } |
611 | if (a.x == b.x) { |
612 | fail(fmt, "zero-width", arg); |
613 | return 1; |
614 | } |
615 | if (a.y == b.y) { |
616 | fail(fmt, "zero-height", arg); |
617 | return 1; |
618 | } |
619 | return 0; |
620 | } |
621 | |
622 | |
623 | static struct inst *add_inst(const struct inst_ops *ops, enum inst_prio prio, |
624 | struct coord base) |
625 | { |
626 | struct inst *inst; |
627 | |
628 | inst = alloc_type(struct inst); |
629 | inst->ops = ops; |
630 | inst->prio = prio; |
631 | inst->vec = NULL; |
632 | inst->obj = NULL; |
633 | inst->base = inst->bbox.min = inst->bbox.max = base; |
634 | inst->outer = frame_instantiating; |
635 | inst->active = IS_ACTIVE; |
636 | inst->next = NULL; |
637 | *curr_pkg->next_inst[prio] = inst; |
638 | curr_pkg->next_inst[prio] = &inst->next; |
639 | return inst; |
640 | } |
641 | |
642 | |
643 | /* ----- vec --------------------------------------------------------------- */ |
644 | |
645 | |
646 | static int validate_vec_name(const char *s, void *ctx) |
647 | { |
648 | struct vec *vec = ctx; |
649 | const struct vec *walk; |
650 | |
651 | if (!is_id(s)) |
652 | return 0; |
653 | for (walk = vec->frame->vecs; walk; walk = walk->next) |
654 | if (walk->name && !strcmp(walk->name, s)) |
655 | return 0; |
656 | return 1; |
657 | } |
658 | |
659 | |
660 | static void vec_edit(struct vec *vec) |
661 | { |
662 | edit_x(&vec->x, "X distance"); |
663 | edit_y(&vec->y, "Y distance"); |
664 | edit_unique_null(&vec->name, validate_vec_name, vec, "Vector name"); |
665 | } |
666 | |
667 | |
668 | static void vec_op_select(struct inst *self) |
669 | { |
670 | status_set_type_entry(NULL, "ref ="); |
671 | status_set_name("Vector reference (name)", |
672 | "%s", self->vec->name ? self->vec->name : ""); |
673 | rect_status(self->base, self->u.vec.end, -1, 0); |
674 | vec_edit(self->vec); |
675 | } |
676 | |
677 | |
678 | /* |
679 | * @@@ The logic of gui_find_point_vec isn't great. Instead of selecting a |
680 | * point and then filtering, we should filter the candidates, so that a point |
681 | * that's close end eligible can win against one that's closer but not |
682 | * eligible. |
683 | */ |
684 | |
685 | static struct inst *find_point_vec(struct inst *self, struct coord pos) |
686 | { |
687 | struct inst *inst; |
688 | const struct vec *vec; |
689 | |
690 | inst = inst_find_point(pos); |
691 | if (!inst) |
692 | return NULL; |
693 | if (inst->ops == &frame_ops) |
694 | return inst; |
695 | for (vec = inst->vec; vec; vec = vec->base) |
696 | if (vec == self->vec) |
697 | return NULL; |
698 | return inst; |
699 | } |
700 | |
701 | |
702 | /* |
703 | * When instantiating and when dumping, we assume that bases appear in the |
704 | * frame->vecs list before vectors using them. A move may change this order. |
705 | * We therefore have to sort the list after the move. |
706 | * |
707 | * Since the list is already ordered, cleaning it up is just O(n). |
708 | */ |
709 | |
710 | |
711 | static void do_move_to_vec(struct inst *inst, struct inst *to, int i) |
712 | { |
713 | struct vec *to_vec = inst_get_vec(to); |
714 | struct vec *vec = inst->vec; |
715 | struct frame *frame = vec->frame; |
716 | struct vec *v, **anchor, **walk; |
717 | |
718 | assert(!i); |
719 | vec->base = to_vec; |
720 | |
721 | /* |
722 | * Mark the vector that's being rebased and all vectors that |
723 | * (recursively) depend on it. |
724 | * |
725 | * We're mainly interested in the range between the vector being moved |
726 | * and the new base. If the vector follows the base, the list is |
727 | * already in the correct order and nothing needs moving. |
728 | */ |
729 | for (v = frame->vecs; v != vec; v = v->next) |
730 | v->mark = 0; |
731 | vec->mark = 1; |
732 | for (v = vec->next; v && v != to_vec; v = v->next) |
733 | v->mark = v->base ? v->base->mark : 0; |
734 | if (!v) |
735 | return; |
736 | |
737 | /* |
738 | * All the marked vectors appearing on the list before the new base |
739 | * are moved after the new base, preserving their order. |
740 | * |
741 | * Start at frame->vecs, not "vec", so that we move the the vector |
742 | * being rebased as well. |
743 | */ |
744 | anchor = &to_vec->next; |
745 | walk = &frame->vecs; |
746 | while (*walk != to_vec) { |
747 | v = *walk; |
748 | if (!v->mark) { |
749 | walk = &v->next; |
750 | } else { |
751 | *walk = v->next; |
752 | v->next = *anchor; |
753 | *anchor = v; |
754 | anchor = &v->next; |
755 | } |
756 | } |
757 | } |
758 | |
759 | |
760 | static struct inst_ops vec_ops = { |
761 | .draw = gui_draw_vec, |
762 | .hover = gui_hover_vec, |
763 | .distance = gui_dist_vec, |
764 | .select = vec_op_select, |
765 | .find_point = find_point_vec, |
766 | .draw_move = draw_move_vec, |
767 | .do_move_to = do_move_to_vec, |
768 | }; |
769 | |
770 | |
771 | int inst_vec(struct vec *vec, struct coord base) |
772 | { |
773 | struct inst *inst; |
774 | |
775 | inst = add_inst(&vec_ops, ip_vec, base); |
776 | inst->vec = vec; |
777 | inst->u.vec.end = vec->pos; |
778 | find_inst(inst); |
779 | update_bbox(&inst->bbox, vec->pos); |
780 | propagate_bbox(inst); |
781 | return 1; |
782 | } |
783 | |
784 | |
785 | /* ----- line -------------------------------------------------------------- */ |
786 | |
787 | |
788 | static void obj_line_edit(struct obj *obj) |
789 | { |
790 | edit_dist_expr(&obj->u.line.width, "Line width"); |
791 | } |
792 | |
793 | |
794 | static void line_op_select(struct inst *self) |
795 | { |
796 | rect_status_sort(self->base, self->u.rect.end, self->u.rect.width, 0); |
797 | obj_line_edit(self->obj); |
798 | } |
799 | |
800 | |
801 | static struct inst_ops line_ops = { |
802 | .draw = gui_draw_line, |
803 | .distance = gui_dist_line, |
804 | .select = line_op_select, |
805 | .draw_move = draw_move_line, |
806 | }; |
807 | |
808 | |
809 | int inst_line(struct obj *obj, struct coord a, struct coord b, unit_type width) |
810 | { |
811 | struct inst *inst; |
812 | |
813 | inst = add_inst(&line_ops, ip_line, a); |
814 | inst->obj = obj; |
815 | inst->u.rect.end = b; |
816 | inst->u.rect.width = width; |
817 | find_inst(inst); |
818 | update_bbox(&inst->bbox, b); |
819 | grow_bbox_by_width(&inst->bbox, width); |
820 | propagate_bbox(inst); |
821 | return 1; |
822 | } |
823 | |
824 | |
825 | /* ----- rect -------------------------------------------------------------- */ |
826 | |
827 | |
828 | static void obj_rect_edit(struct obj *obj) |
829 | { |
830 | edit_dist_expr(&obj->u.rect.width, "Line width"); |
831 | } |
832 | |
833 | |
834 | static void rect_op_select(struct inst *self) |
835 | { |
836 | rect_status_sort(self->base, self->u.rect.end, self->u.rect.width, 0); |
837 | obj_rect_edit(self->obj); |
838 | } |
839 | |
840 | |
841 | static struct inst_ops rect_ops = { |
842 | .draw = gui_draw_rect, |
843 | .distance = gui_dist_rect, |
844 | .select = rect_op_select, |
845 | .draw_move = draw_move_rect, |
846 | }; |
847 | |
848 | |
849 | int inst_rect(struct obj *obj, struct coord a, struct coord b, unit_type width) |
850 | { |
851 | struct inst *inst; |
852 | |
853 | inst = add_inst(&rect_ops, ip_rect, a); |
854 | inst->obj = obj; |
855 | inst->u.rect.end = b; |
856 | inst->u.rect.width = width; |
857 | find_inst(inst); |
858 | update_bbox(&inst->bbox, b); |
859 | grow_bbox_by_width(&inst->bbox, width); |
860 | propagate_bbox(inst); |
861 | return 1; |
862 | } |
863 | |
864 | |
865 | /* ----- pad / rpad -------------------------------------------------------- */ |
866 | |
867 | |
868 | static int validate_pad_name(const char *s, void *ctx) |
869 | { |
870 | char *tmp; |
871 | |
872 | status_begin_reporting(); |
873 | tmp = expand(s, NULL); |
874 | if (!tmp) |
875 | return 0; |
876 | free(tmp); |
877 | return 1; |
878 | } |
879 | |
880 | |
881 | static void obj_pad_edit(struct obj *obj) |
882 | { |
883 | edit_pad_type(&obj->u.pad.type); |
884 | edit_name(&obj->u.pad.name, validate_pad_name, NULL, |
885 | "Pad name (template)"); |
886 | } |
887 | |
888 | |
889 | static void pad_op_select(struct inst *self) |
890 | { |
891 | status_set_type_entry(NULL, "label ="); |
892 | status_set_name("Pad name (actual)", "%s", self->u.pad.name); |
893 | rect_status_sort(self->base, self->u.pad.other, -1, 0); |
894 | obj_pad_edit(self->obj); |
895 | } |
896 | |
897 | |
898 | static struct inst_ops pad_ops = { |
899 | .draw = gui_draw_pad, |
900 | .distance = gui_dist_pad, |
901 | .select = pad_op_select, |
902 | .draw_move = draw_move_pad, |
903 | }; |
904 | |
905 | |
906 | static void rpad_op_select(struct inst *self) |
907 | { |
908 | status_set_type_entry(NULL, "label ="); |
909 | status_set_name("Pad name (actual)", "%s", self->u.pad.name); |
910 | rect_status_sort(self->base, self->u.pad.other, -1, 1); |
911 | obj_pad_edit(self->obj); |
912 | } |
913 | |
914 | |
915 | static struct inst_ops rpad_ops = { |
916 | .draw = gui_draw_rpad, |
917 | .distance = gui_dist_pad, /* @@@ */ |
918 | .select = rpad_op_select, |
919 | .draw_move = draw_move_rpad, |
920 | }; |
921 | |
922 | |
923 | int inst_pad(struct obj *obj, const char *name, struct coord a, struct coord b) |
924 | { |
925 | struct inst *inst; |
926 | |
927 | if (zero_sized(a, b, "%s pad \"%s\"", name)) |
928 | return 0; |
929 | inst = add_inst(obj->u.pad.rounded ? &rpad_ops : &pad_ops, |
930 | obj->u.pad.type == pt_normal || obj->u.pad.type == pt_bare || |
931 | obj->u.pad.type == pt_trace ? |
932 | ip_pad_copper : ip_pad_special, a); |
933 | inst->obj = obj; |
934 | inst->u.pad.name = stralloc(name); |
935 | inst->u.pad.other = b; |
936 | inst->u.pad.layers = pad_type_to_layers(obj->u.pad.type); |
937 | find_inst(inst); |
938 | update_bbox(&inst->bbox, b); |
939 | propagate_bbox(inst); |
940 | return 1; |
941 | } |
942 | |
943 | |
944 | /* ----- hole -------------------------------------------------------------- */ |
945 | |
946 | |
947 | static void hole_op_select(struct inst *self) |
948 | { |
949 | rect_status_sort(self->base, self->u.hole.other, -1, 1); |
950 | } |
951 | |
952 | |
953 | static struct inst_ops hole_ops = { |
954 | .draw = gui_draw_hole, |
955 | .distance = gui_dist_hole, |
956 | .select = hole_op_select, |
957 | .draw_move = draw_move_hole, |
958 | }; |
959 | |
960 | |
961 | int inst_hole(struct obj *obj, struct coord a, struct coord b) |
962 | { |
963 | struct inst *inst; |
964 | |
965 | if (zero_sized(a, b, "%s hole", NULL)) |
966 | return 0; |
967 | inst = add_inst(&hole_ops, ip_hole, a); |
968 | inst->obj = obj; |
969 | inst->u.hole.other = b; |
970 | inst->u.hole.layers = mech_hole_layers(); |
971 | find_inst(inst); |
972 | update_bbox(&inst->bbox, b); |
973 | propagate_bbox(inst); |
974 | return 1; |
975 | } |
976 | |
977 | |
978 | /* ----- arc --------------------------------------------------------------- */ |
979 | |
980 | |
981 | static void obj_arc_edit(struct obj *obj) |
982 | { |
983 | edit_dist_expr(&obj->u.arc.width, "Line width"); |
984 | } |
985 | |
986 | |
987 | static void arc_op_select(struct inst *self) |
988 | { |
989 | status_set_xy(self->base); |
990 | status_set_angle("Angle", "a = %3.1f deg", |
991 | self->u.arc.a1 == self->u.arc.a2 ? 360 : |
992 | self->u.arc.a2-self->u.arc.a1); |
993 | set_with_units(status_set_r, "r = ", self->u.arc.r, "Radius"); |
994 | status_set_type_entry(NULL, "width ="); |
995 | set_with_units(status_set_name, "", self->u.arc.width, "Line width"); |
996 | obj_arc_edit(self->obj); |
997 | } |
998 | |
999 | |
1000 | static struct inst_ops arc_ops = { |
1001 | .draw = gui_draw_arc, |
1002 | .distance = gui_dist_arc, |
1003 | .select = arc_op_select, |
1004 | .draw_move = draw_move_arc, |
1005 | .do_move_to = do_move_to_arc, |
1006 | }; |
1007 | |
1008 | |
1009 | int inst_arc(struct obj *obj, struct coord center, struct coord start, |
1010 | struct coord end, unit_type width) |
1011 | { |
1012 | struct inst *inst; |
1013 | double r, a1, a2; |
1014 | |
1015 | a1 = theta(center, start); |
1016 | a2 = theta(center, end); |
1017 | inst = add_inst(&arc_ops, |
1018 | fmod(a1, 360) == fmod(a2, 360) ? ip_circ : ip_arc, center); |
1019 | inst->obj = obj; |
1020 | r = hypot(start.x-center.x, start.y-center.y); |
1021 | inst->u.arc.r = r; |
1022 | inst->u.arc.a1 = a1; |
1023 | inst->u.arc.a2 = a2; |
1024 | inst->u.arc.width = width; |
1025 | inst->bbox.min.x = center.x-r; |
1026 | inst->bbox.max.x = center.x+r; |
1027 | inst->bbox.min.y = center.y-r; |
1028 | inst->bbox.max.y = center.y+r; |
1029 | find_inst(inst); |
1030 | grow_bbox_by_width(&inst->bbox, width); |
1031 | propagate_bbox(inst); |
1032 | return 1; |
1033 | } |
1034 | |
1035 | |
1036 | /* ----- measurement ------------------------------------------------------- */ |
1037 | |
1038 | |
1039 | static void obj_meas_edit(struct obj *obj) |
1040 | { |
1041 | edit_dist_expr(&obj->u.meas.offset, "Measurement line offset"); |
1042 | } |
1043 | |
1044 | |
1045 | static void meas_op_select(struct inst *self) |
1046 | { |
1047 | rect_status_sort(self->base, self->u.meas.end, -1, 0); |
1048 | status_set_type_entry(NULL, "offset ="); |
1049 | set_with_units(status_set_name, "", self->u.meas.offset, |
1050 | "Measurement line offset"); |
1051 | obj_meas_edit(self->obj); |
1052 | } |
1053 | |
1054 | |
1055 | static struct inst_ops meas_ops = { |
1056 | .draw = gui_draw_meas, |
1057 | .distance = gui_dist_meas, |
1058 | .select = meas_op_select, |
1059 | .begin_drag_move= begin_drag_move_meas, |
1060 | .find_point = find_point_meas_move, |
1061 | .draw_move = draw_move_meas, |
1062 | .end_drag_move = end_drag_move_meas, |
1063 | .do_move_to = do_move_to_meas, |
1064 | }; |
1065 | |
1066 | |
1067 | struct inst *find_meas_hint(const struct obj *obj) |
1068 | { |
1069 | struct inst *inst; |
1070 | |
1071 | for (inst = curr_pkg->insts[ip_meas]; inst; inst = inst->next) |
1072 | if (inst->obj == obj) |
1073 | break; |
1074 | return inst; |
1075 | } |
1076 | |
1077 | |
1078 | int inst_meas(struct obj *obj, struct coord from, struct coord to) |
1079 | { |
1080 | struct inst *inst; |
1081 | struct coord a1, b1; |
1082 | |
1083 | inst = find_meas_hint(obj); |
1084 | assert(inst); |
1085 | inst->base = from; |
1086 | inst->u.meas.end = to; |
1087 | inst->u.meas.valid = 1; |
1088 | /* @@@ we still need to consider the text size as well */ |
1089 | update_bbox(&inst->bbox, from); |
1090 | update_bbox(&inst->bbox, to); |
1091 | project_meas(inst, &a1, &b1); |
1092 | update_bbox(&inst->bbox, a1); |
1093 | update_bbox(&inst->bbox, b1); |
1094 | propagate_bbox(inst); |
1095 | return 1; |
1096 | } |
1097 | |
1098 | |
1099 | void inst_meas_hint(struct obj *obj, unit_type offset) |
1100 | { |
1101 | static const struct coord zero = { 0, 0 }; |
1102 | struct inst *inst; |
1103 | |
1104 | inst = find_meas_hint(obj); |
1105 | if (inst) |
1106 | return; |
1107 | inst = add_inst(&meas_ops, ip_meas, zero); |
1108 | inst->obj = obj; |
1109 | inst->u.meas.offset = offset; |
1110 | inst->u.meas.valid = 0; |
1111 | inst->active = 1; /* measurements are always active */ |
1112 | } |
1113 | |
1114 | |
1115 | /* ----- direct editing of objects ----------------------------------------- */ |
1116 | |
1117 | |
1118 | static void obj_edit(struct obj *obj) |
1119 | { |
1120 | switch (obj->type) { |
1121 | case ot_frame: |
1122 | break; |
1123 | case ot_line: |
1124 | obj_line_edit(obj); |
1125 | break; |
1126 | case ot_rect: |
1127 | obj_rect_edit(obj); |
1128 | break; |
1129 | case ot_arc: |
1130 | obj_arc_edit(obj); |
1131 | break; |
1132 | case ot_pad: |
1133 | obj_pad_edit(obj); |
1134 | break; |
1135 | case ot_meas: |
1136 | obj_meas_edit(obj); |
1137 | break; |
1138 | default: |
1139 | abort(); |
1140 | } |
1141 | } |
1142 | |
1143 | |
1144 | /* ----- active instance --------------------------------------------------- */ |
1145 | |
1146 | |
1147 | void inst_begin_active(int active) |
1148 | { |
1149 | active_set = (active_set << 1) | active; |
1150 | } |
1151 | |
1152 | |
1153 | void inst_end_active(void) |
1154 | { |
1155 | active_set >>= 1; |
1156 | } |
1157 | |
1158 | |
1159 | /* ----- frame ------------------------------------------------------------- */ |
1160 | |
1161 | |
1162 | static void frame_op_select(struct inst *self) |
1163 | { |
1164 | rect_status(self->bbox.min, self->bbox.max, -1, 0); |
1165 | status_set_type_entry(NULL, "name ="); |
1166 | status_set_name("Frame name", "%s", self->u.frame.ref->name); |
1167 | } |
1168 | |
1169 | |
1170 | static struct inst_ops frame_ops = { |
1171 | .draw = gui_draw_frame, |
1172 | .hover = gui_hover_frame, |
1173 | .distance = gui_dist_frame, |
1174 | .select = frame_op_select, |
1175 | .draw_move = draw_move_frame, |
1176 | }; |
1177 | |
1178 | |
1179 | void inst_begin_frame(struct obj *obj, struct frame *frame, |
1180 | struct coord base, int active, int is_active_frame) |
1181 | { |
1182 | struct inst *inst; |
1183 | |
1184 | inst = add_inst(&frame_ops, ip_frame, base); |
1185 | inst->obj = obj; |
1186 | inst->u.frame.ref = frame; |
1187 | inst->u.frame.active = is_active_frame; |
1188 | inst->active = active; |
1189 | find_inst(inst); |
1190 | frame_instantiating = inst; |
1191 | } |
1192 | |
1193 | |
1194 | void inst_end_frame(const struct frame *frame) |
1195 | { |
1196 | struct inst *inst = frame_instantiating; |
1197 | |
1198 | frame_instantiating = frame_instantiating->outer; |
1199 | if (frame_instantiating) |
1200 | propagate_bbox(inst); |
1201 | if (inst->u.frame.active && frame == active_frame) |
1202 | active_frame_bbox = inst->bbox; |
1203 | } |
1204 | |
1205 | |
1206 | /* ----- package ----------------------------------------------------------- */ |
1207 | |
1208 | |
1209 | void inst_select_pkg(const char *name, int active) |
1210 | { |
1211 | struct pkg **pkg; |
1212 | enum inst_prio prio; |
1213 | |
1214 | name = name ? unique(name) : NULL; |
1215 | for (pkg = &pkgs; *pkg; pkg = &(*pkg)->next) |
1216 | if ((*pkg)->name == name) |
1217 | break; |
1218 | if (!*pkg) { |
1219 | *pkg = zalloc_type(struct pkg); |
1220 | (*pkg)->name = name; |
1221 | FOR_INST_PRIOS_UP(prio) |
1222 | (*pkg)->next_inst[prio] = &(*pkg)->insts[prio]; |
1223 | (*pkg)->samples = |
1224 | zalloc_size(sizeof(struct sample *)*n_samples); |
1225 | (*pkg)->n_samples = n_samples; |
1226 | } |
1227 | curr_pkg = *pkg; |
1228 | if (active && name) |
1229 | reachable_pkg = curr_pkg; |
1230 | } |
1231 | |
1232 | |
1233 | /* ----- misc. ------------------------------------------------------------- */ |
1234 | |
1235 | |
1236 | struct bbox inst_get_bbox(const struct pkg *pkg) |
1237 | { |
1238 | if (pkg) |
1239 | return pkg->bbox; |
1240 | else |
1241 | return pkgs->insts[ip_frame]->bbox; |
1242 | } |
1243 | |
1244 | |
1245 | static void cleanup_inst(enum inst_prio prio, const struct inst *inst) |
1246 | { |
1247 | switch (prio) { |
1248 | case ip_pad_copper: |
1249 | case ip_pad_special: |
1250 | free(inst->u.pad.name); |
1251 | break; |
1252 | default: |
1253 | break; |
1254 | } |
1255 | } |
1256 | |
1257 | |
1258 | static void free_pkgs(struct pkg *pkg) |
1259 | { |
1260 | enum inst_prio prio; |
1261 | struct pkg *next_pkg; |
1262 | struct inst *inst, *next; |
1263 | |
1264 | while (pkg) { |
1265 | next_pkg = pkg->next; |
1266 | FOR_INST_PRIOS_UP(prio) |
1267 | for (inst = pkg->insts[prio]; inst; inst = next) { |
1268 | next = inst->next; |
1269 | cleanup_inst(prio, inst); |
1270 | free(inst); |
1271 | } |
1272 | reset_samples(pkg->samples, pkg->n_samples); |
1273 | free(pkg->samples); |
1274 | free(pkg); |
1275 | pkg = next_pkg; |
1276 | } |
1277 | } |
1278 | |
1279 | |
1280 | void inst_start(void) |
1281 | { |
1282 | static struct bbox bbox_zero = { { 0, 0 }, { 0, 0 }}; |
1283 | |
1284 | active_frame_bbox = bbox_zero; |
1285 | prev_pkgs = pkgs; |
1286 | prev_reachable_pkg = reachable_pkg; |
1287 | pkgs = NULL; |
1288 | reachable_pkg = NULL; |
1289 | inst_select_pkg(NULL, 0); |
1290 | curr_pkg = pkgs; |
1291 | frame_instantiating = NULL; |
1292 | } |
1293 | |
1294 | |
1295 | void inst_commit(void) |
1296 | { |
1297 | struct pkg *pkg; |
1298 | |
1299 | if (active_pkg) { |
1300 | for (pkg = pkgs; pkg && pkg->name != active_pkg->name; |
1301 | pkg = pkg->next); |
1302 | active_pkg = pkg; |
1303 | } |
1304 | if (!active_pkg) |
1305 | active_pkg = pkgs->next; |
1306 | free_pkgs(prev_pkgs); |
1307 | } |
1308 | |
1309 | |
1310 | void inst_revert(void) |
1311 | { |
1312 | free_pkgs(pkgs); |
1313 | pkgs = prev_pkgs; |
1314 | reachable_pkg = prev_reachable_pkg; |
1315 | } |
1316 | |
1317 | |
1318 | void inst_draw(void) |
1319 | { |
1320 | enum inst_prio prio; |
1321 | struct inst *inst; |
1322 | int i; |
1323 | |
1324 | FOR_INST_PRIOS_UP(prio) |
1325 | FOR_ALL_INSTS(i, prio, inst) |
1326 | if (show_this(inst)) |
1327 | if (show(prio) && !inst->active && |
1328 | inst->ops->draw) |
1329 | inst->ops->draw(inst); |
1330 | FOR_INST_PRIOS_UP(prio) |
1331 | FOR_ALL_INSTS(i, prio, inst) |
1332 | if (show(prio) && prio != ip_frame && inst->active && |
1333 | inst != selected_inst && inst->ops->draw) |
1334 | inst->ops->draw(inst); |
1335 | if (show_stuff) |
1336 | FOR_ALL_INSTS(i, ip_frame, inst) |
1337 | if (inst->active && inst != selected_inst && |
1338 | inst->ops->draw) |
1339 | inst->ops->draw(inst); |
1340 | if (selected_inst && selected_inst->ops->draw) |
1341 | selected_inst->ops->draw(selected_inst); |
1342 | } |
1343 | |
1344 | |
1345 | void inst_highlight_vecs(int (*pick)(struct inst *inst, void *user), void *user) |
1346 | { |
1347 | struct inst *inst; |
1348 | int i; |
1349 | |
1350 | FOR_ALL_INSTS(i, ip_vec, inst) { |
1351 | inst->u.vec.highlighted = pick(inst, user); |
1352 | if (inst->u.vec.highlighted) |
1353 | gui_highlight_vec(inst); |
1354 | } |
1355 | } |
1356 | |
1357 | |
1358 | struct inst *inst_find_vec(struct coord pos, |
1359 | int (*pick)(struct inst *inst, void *user), void *user) |
1360 | { |
1361 | struct inst *inst, *found; |
1362 | int best_dist = 0; /* keep gcc happy */ |
1363 | int dist, i; |
1364 | |
1365 | found = NULL; |
1366 | FOR_ALL_INSTS(i, ip_vec, inst) { |
1367 | if (!inst->ops->distance) |
1368 | continue; |
1369 | dist = inst->ops->distance(inst, pos, draw_ctx.scale); |
1370 | if (dist < 0 || (found && best_dist <= dist)) |
1371 | continue; |
1372 | if (!pick(inst, user)) |
1373 | continue; |
1374 | found = inst; |
1375 | best_dist = dist; |
1376 | } |
1377 | return found; |
1378 | } |
1379 | |
1380 | |
1381 | struct inst *insts_ip_vec(void) |
1382 | { |
1383 | return active_pkg->insts[ip_vec]; |
1384 | } |
1385 | |
1386 | |
1387 | struct pix_buf *inst_draw_move(struct inst *inst, struct coord pos, int i) |
1388 | { |
1389 | return inst->ops->draw_move(inst, pos, i); |
1390 | } |
1391 | |
1392 | |
1393 | int inst_do_move_to(struct inst *inst, struct inst *to, int i) |
1394 | { |
1395 | if (!inst->ops->do_move_to) |
1396 | return 0; |
1397 | inst->ops->do_move_to(inst, to, i); |
1398 | return 1; |
1399 | } |
1400 | |
1401 | |
1402 | struct pix_buf *inst_hover(struct inst *inst) |
1403 | { |
1404 | if (!inst->ops->hover) |
1405 | return NULL; |
1406 | return inst->ops->hover(inst); |
1407 | } |
1408 | |
1409 | |
1410 | void inst_begin_drag_move(struct inst *inst, int i) |
1411 | { |
1412 | if (inst->ops->begin_drag_move) |
1413 | inst->ops->begin_drag_move(inst, i); |
1414 | } |
1415 | |
1416 | |
1417 | void inst_delete(struct inst *inst) |
1418 | { |
1419 | if (inst->ops == &vec_ops) |
1420 | delete_vec(inst->vec); |
1421 | else |
1422 | delete_obj(inst->obj); |
1423 | } |
1424 |
Branches:
master