Merge branch 'master' into work
[xcircuit.git] / selection.c
blobaa1a65af7b2b328e261e7519bd80bf80a502b3a9
1 /*-------------------------------------------------------------------------*/
2 /* selection.c --- xcircuit routines handling element selection etc. */
3 /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */
4 /*-------------------------------------------------------------------------*/
6 /*-------------------------------------------------------------------------*/
7 /* written by Tim Edwards, 8/13/93 */
8 /*-------------------------------------------------------------------------*/
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <math.h>
15 #ifndef XC_WIN32
16 #include <X11/Intrinsic.h>
17 #include <X11/StringDefs.h>
18 #endif
20 #ifdef TCL_WRAPPER
21 #include <tk.h>
22 #else
23 #ifndef XC_WIN32
24 #include "Xw/Xw.h"
25 #endif
26 #endif
28 /*-------------------------------------------------------------------------*/
29 /* Local includes */
30 /*-------------------------------------------------------------------------*/
32 #include "xcircuit.h"
33 #include "colordefs.h"
35 /*----------------------------------------------------------------------*/
36 /* Function prototype declarations */
37 /*----------------------------------------------------------------------*/
38 #include "prototypes.h"
40 /*----------------------------------------------------------------------*/
41 /* Exported Variable definitions */
42 /*----------------------------------------------------------------------*/
44 extern Display *dpy;
45 extern Cursor appcursors[NUM_CURSORS];
46 extern XCWindowData *areawin;
47 extern Globaldata xobjs;
48 extern char _STR[150];
49 extern int number_colors;
50 extern colorindex *colorlist;
52 #ifdef TCL_WRAPPER
53 extern Tcl_Interp *xcinterp;
54 #endif
56 /*----------------------------------------------------------------------*/
57 /* Prevent a list of elements from being selected. */
58 /*----------------------------------------------------------------------*/
60 void disable_selects(objectptr thisobject, short *selectlist, int selects)
62 genericptr genptr;
63 short *i;
65 for (i = selectlist; i < selectlist + selects; i++) {
66 genptr = *(thisobject->plist + *i);
67 genptr->type |= SELECT_HIDE;
71 /*----------------------------------------------------------------------*/
72 /* Allow a list of elements to be selected, if they were disabled using */
73 /* the disable_selects() routine. */
74 /*----------------------------------------------------------------------*/
76 void enable_selects(objectptr thisobject, short *selectlist, int selects)
78 genericptr genptr;
79 short *i;
81 for (i = selectlist; i < selectlist + selects; i++) {
82 genptr = *(thisobject->plist + *i);
83 genptr->type &= ~(SELECT_HIDE);
87 /*----------------------------------------------------------------------*/
88 /* Change filter to determine what types can be selected */
89 /*----------------------------------------------------------------------*/
91 void selectfilter(xcWidget w, pointertype value, caddr_t calldata)
93 short bitwise = (short)value;
94 Boolean bval = (areawin->filter & bitwise) ? True : False;
96 if (bval)
97 areawin->filter &= ~bitwise;
98 else
99 areawin->filter |= bitwise;
101 #ifndef TCL_WRAPPER
102 toggle(w, (pointertype)&bval, calldata);
103 #endif
106 /*----------------------------------------------------------------------*/
107 /* Look at select stack to see if there are any selects; call select, */
108 /* if not. If draw_selected is True, then the selected items are drawn */
109 /* in the select color. Otherwise, they are not redrawn. */
110 /*----------------------------------------------------------------------*/
112 Boolean checkselect_draw(short value, Boolean draw_selected)
114 short *check;
116 value &= areawin->filter; /* apply the selection filter */
118 if (areawin->selects == 0) {
119 if (draw_selected)
120 select_element(value);
121 else {
122 Boolean save_redraw = areawin->redraw_needed;
123 select_element(value);
124 areawin->redraw_needed = save_redraw;
127 if (areawin->selects == 0) return False;
128 for (check = areawin->selectlist; check < areawin->selectlist +
129 areawin->selects; check++)
130 if (SELECTTYPE(check) & value) break;
131 if (check == areawin->selectlist + areawin->selects) return False;
132 else return True;
135 /*----------------------------------------------------------------------------*/
136 /* Look at select stack to see if there are any selects; call select, if not. */
137 /*----------------------------------------------------------------------------*/
139 Boolean checkselect(short value)
141 return checkselect_draw(value, False);
144 /*--------------------------------------------------------------*/
145 /* Select list numbering revision when an element is deleted */
146 /* from an object. */
147 /*--------------------------------------------------------------*/
149 void reviseselect(short *slist, int selects, short *removed)
151 short *chkselect;
153 for (chkselect = slist; chkselect < slist + selects; chkselect++)
154 if (*chkselect > *removed) (*chkselect)--;
157 /*----------------------*/
158 /* Draw a selected item */
159 /*----------------------*/
161 void geneasydraw(short instance, int mode, objectptr curobj, objinstptr curinst)
163 genericptr elementptr = *(curobj->plist + instance);
165 #ifdef HAVE_CAIRO
166 cairo_save(areawin->cr);
167 cairo_reset_clip(areawin->cr);
168 #else
169 // Note: Setting areawin->clipped to -1 prevents the clipmask from being
170 // applied to elements,
171 areawin->clipped = -1;
172 #endif /* HAVE_CAIRO */
174 switch (ELEMENTTYPE(*(curobj->plist + instance))) {
175 case ARC:
176 UDrawArc((arcptr)elementptr, xobjs.pagelist[areawin->page]->wirewidth);
177 break;
178 case POLYGON:
179 UDrawPolygon((polyptr)elementptr, xobjs.pagelist[areawin->page]->wirewidth);
180 break;
181 case SPLINE:
182 UDrawSpline((splineptr)elementptr, xobjs.pagelist[areawin->page]->wirewidth);
183 break;
184 case PATH:
185 UDrawPath((pathptr)elementptr, xobjs.pagelist[areawin->page]->wirewidth);
186 break;
187 case LABEL:
188 UDrawString((labelptr)elementptr, mode, curinst);
189 break;
190 case OBJINST:
191 UDrawObject((objinstptr)elementptr, SINGLE, mode,
192 xobjs.pagelist[areawin->page]->wirewidth, NULL);
193 break;
194 case GRAPHIC:
195 UDrawGraphic((graphicptr)elementptr);
196 break;
198 #ifdef HAVE_CAIRO
199 cairo_restore(areawin->cr);
200 #else
201 areawin->clipped = 0;
202 #endif /* HAVE_CAIRO */
205 /*-------------------------------------------------*/
206 /* Draw a selected item, including selection color */
207 /*-------------------------------------------------*/
209 void gendrawselected(short *newselect, objectptr curobj, objinstptr curinst)
211 if (*newselect >= curobj->parts) return; // Safety check
213 XcSetForeground(SELECTCOLOR);
214 geneasydraw(*newselect, DOFORALL, curobj, curinst);
216 SetForeground(dpy, areawin->gc, AUXCOLOR);
217 indicateparams(*(curobj->plist + *newselect));
219 SetForeground(dpy, areawin->gc, areawin->gccolor);
222 /*---------------------------------------------------*/
223 /* Allocate or reallocate memory for a new selection */
224 /*---------------------------------------------------*/
226 short *allocselect()
228 short *newselect;
230 if (areawin->selects == 0)
231 areawin->selectlist = (short *) malloc(sizeof(short));
232 else
233 areawin->selectlist = (short *) realloc(areawin->selectlist,
234 (areawin->selects + 1) * sizeof(short));
236 newselect = areawin->selectlist + areawin->selects;
237 areawin->selects++;
239 return newselect;
242 /*-------------------------------------------------*/
243 /* Set Options menu according to 1st selection */
244 /*-------------------------------------------------*/
246 void setoptionmenu()
248 short *mselect;
249 labelptr mlabel;
251 if (areawin->selects == 0) {
252 setallstylemarks(areawin->style);
253 setcolormark(areawin->color);
254 setdefaultfontmarks();
255 setparammarks(NULL);
256 return;
259 for (mselect = areawin->selectlist; mselect < areawin->selectlist +
260 areawin->selects; mselect++) {
261 setcolormark(SELTOCOLOR(mselect));
262 setparammarks(SELTOGENERIC(mselect));
263 switch(SELECTTYPE(mselect)) {
264 case ARC:
265 setallstylemarks(SELTOARC(mselect)->style);
266 return;
267 case POLYGON:
268 setallstylemarks(SELTOPOLY(mselect)->style);
269 return;
270 case SPLINE:
271 setallstylemarks(SELTOSPLINE(mselect)->style);
272 return;
273 case PATH:
274 setallstylemarks(SELTOPATH(mselect)->style);
275 return;
276 case LABEL:
277 mlabel = SELTOLABEL(mselect);
278 setfontmarks(mlabel->string->data.font, mlabel->anchor);
279 return;
284 /*-------------------------------------*/
285 /* Test of point being inside of a box */
286 /*-------------------------------------*/
288 int test_insideness(int tx, int ty, XPoint *boxpoints)
290 int i, stval = 0;
291 XPoint *pt1, *pt2;
292 int stdir;
294 for (i = 0; i < 4; i++) {
295 pt1 = boxpoints + i;
296 pt2 = boxpoints + ((i + 1) % 4);
297 stdir = (pt2->x - pt1->x) * (ty - pt1->y)
298 - (pt2->y - pt1->y) * (tx - pt1->x);
299 stval += sign(stdir);
301 return (abs(stval) == 4) ? 1 : 0;
304 /*--------------------------------------------*/
305 /* Search for selection among path components */
306 /*--------------------------------------------*/
308 #define RANGE_NARROW 11.5
309 #define RANGE_WIDE 50
311 Boolean pathselect(genericptr *curgen, short class, float range)
313 /*----------------------------------------------------------------------*/
314 /* wirelim is the distance, in user-space units, at which an element is */
315 /* considered for selection. */
316 /* */
317 /* wirelim = A + B / (scale + C) */
318 /* */
319 /* where A = minimum possible distance (expands range at close scale) */
320 /* C = minimum possible scale (contract range at far scale) */
321 /* B makes wirelim approx. 25 at default scale of 0.5, which */
322 /* is an empirical result. */
323 /*----------------------------------------------------------------------*/
325 float wirelim = 2 + range / (areawin->vscale + 0.05);
326 long sqrwirelim = (int)(wirelim * wirelim);
328 long newdist;
329 Boolean selected = False;
331 class &= areawin->filter; /* apply the selection filter */
333 if ((*curgen)->type == (class & ARC)) {
335 /* look among the arcs */
337 fpointlist currentpt;
338 XPoint nearpt[3];
340 nearpt[2].x = nearpt[0].x = (short)(TOARC(curgen)->points[0].x);
341 nearpt[2].y = nearpt[0].y = (short)(TOARC(curgen)->points[0].y);
342 for (currentpt = TOARC(curgen)->points + 1; currentpt < TOARC(curgen)->points
343 + TOARC(curgen)->number; currentpt++) {
344 nearpt[1].x = nearpt[0].x;
345 nearpt[1].y = nearpt[0].y;
346 nearpt[0].x = (short)(currentpt->x);
347 nearpt[0].y = (short)(currentpt->y);
348 newdist = finddist(&nearpt[0], &nearpt[1], &areawin->save);
349 if (newdist <= sqrwirelim) break;
351 if ((!(TOARC(curgen)->style & UNCLOSED)) && (newdist > sqrwirelim))
352 newdist = finddist(&nearpt[0], &nearpt[2], &areawin->save);
354 if (newdist <= sqrwirelim) selected = True;
357 else if ((*curgen)->type == (class & SPLINE)) {
359 /* look among the splines --- look at polygon representation */
361 fpointlist currentpt;
362 XPoint nearpt[2];
364 nearpt[0].x = (short)(TOSPLINE(curgen)->points[0].x);
365 nearpt[0].y = (short)(TOSPLINE(curgen)->points[0].y);
366 newdist = finddist(&(TOSPLINE(curgen)->ctrl[0]), &(nearpt[0]),
367 &areawin->save);
368 if (newdist > sqrwirelim) {
369 for (currentpt = TOSPLINE(curgen)->points; currentpt <
370 TOSPLINE(curgen)->points + INTSEGS; currentpt++) {
371 nearpt[1].x = nearpt[0].x;
372 nearpt[1].y = nearpt[0].y;
373 nearpt[0].x = (short)(currentpt->x);
374 nearpt[0].y = (short)(currentpt->y);
375 newdist = finddist(&nearpt[0], &nearpt[1], &areawin->save);
376 if (newdist <= sqrwirelim) break;
378 if (newdist > sqrwirelim) {
379 newdist = finddist(&nearpt[0], &(TOSPLINE(curgen)->ctrl[3]),
380 &areawin->save);
381 if ((!(TOSPLINE(curgen)->style & UNCLOSED)) && (newdist > sqrwirelim))
382 newdist = finddist(&(TOSPLINE(curgen)->ctrl[0]),
383 &(TOSPLINE(curgen)->ctrl[3]), &areawin->save);
387 if (newdist <= sqrwirelim) selected = True;
390 else if ((*curgen)->type == (class & POLYGON)) {
392 /* finally, look among the polygons */
394 pointlist currentpt;
396 for (currentpt = TOPOLY(curgen)->points; currentpt < TOPOLY(curgen)
397 ->points + TOPOLY(curgen)->number - 1; currentpt++) {
398 newdist = finddist(currentpt, currentpt + 1, &areawin->save);
399 if (newdist <= sqrwirelim) break;
401 if ((!(TOPOLY(curgen)->style & UNCLOSED)) && (newdist > sqrwirelim))
402 newdist = finddist(currentpt, TOPOLY(curgen)->points,
403 &areawin->save);
405 if (newdist <= sqrwirelim) selected = True;
407 return selected;
410 /*------------------------------------------------------*/
411 /* Check to see if any selection has registered cycles */
412 /*------------------------------------------------------*/
414 Boolean checkforcycles(short *selectlist, int selects)
416 genericptr pgen;
417 pointselect *cycptr;
418 short *ssel;
420 for (ssel = selectlist; ssel < selectlist + selects; ssel++) {
421 pgen = SELTOGENERIC(ssel);
422 switch(pgen->type) {
423 case POLYGON:
424 cycptr = ((polyptr)pgen)->cycle;
425 break;
426 case ARC:
427 cycptr = ((arcptr)pgen)->cycle;
428 break;
429 case SPLINE:
430 cycptr = ((splineptr)pgen)->cycle;
431 break;
432 case LABEL:
433 cycptr = ((labelptr)pgen)->cycle;
434 break;
436 if (cycptr != NULL)
437 if (cycptr->number != -1)
438 return True;
440 return False;
443 /*--------------------------------------------------------------*/
444 /* Copy a cycle selection list from one element to another */
445 /*--------------------------------------------------------------*/
447 void copycycles(pointselect **new, pointselect **old)
449 pointselect *pptr;
450 short cycles = 0;
452 if (*old == NULL) {
453 *new = NULL;
454 return;
457 for (pptr = *old; !(pptr->flags & LASTENTRY); pptr++, cycles++);
458 cycles += 2;
459 *new = (pointselect *)malloc(cycles * sizeof(pointselect));
460 memcpy(*new, *old, cycles * sizeof(pointselect));
463 /*--------------------------------------------------------------*/
464 /* Create a selection record of selected points in an element. */
465 /* If a record already exists, and "cycle" is not already in */
466 /* the list, add it. */
467 /* "flags" may be set to EDITX or EDITY. If "flags" is zero, */
468 /* then flags = EDITX | EDITY is assumed. */
469 /*--------------------------------------------------------------*/
471 pointselect *addcycle(genericptr *pgen, short cycle, u_char flags)
473 polyptr ppoly;
474 arcptr parc;
475 splineptr pspline;
476 labelptr plabel;
477 pointselect *pptr, **cycptr;
478 short cycles = 0;
480 switch((*pgen)->type) {
481 case POLYGON:
482 ppoly = TOPOLY(pgen);
483 cycptr = &ppoly->cycle;
484 break;
485 case ARC:
486 parc = TOARC(pgen);
487 cycptr = &parc->cycle;
488 break;
489 case SPLINE:
490 pspline = TOSPLINE(pgen);
491 cycptr = &pspline->cycle;
492 break;
493 case LABEL:
494 plabel = TOLABEL(pgen);
495 cycptr = &plabel->cycle;
496 break;
499 switch((*pgen)->type) {
500 case POLYGON:
501 case ARC:
502 case SPLINE:
503 case LABEL: // To-do: Handle labels separately
505 if (*cycptr == NULL) {
506 *cycptr = (pointselect *)malloc(sizeof(pointselect));
507 pptr = *cycptr;
508 pptr->number = cycle;
509 pptr->flags = (flags == 0) ? EDITX | EDITY : flags;
510 pptr->flags |= LASTENTRY;
512 else {
513 for (pptr = *cycptr; !(pptr->flags & LASTENTRY); pptr++, cycles++) {
514 if (pptr->number == cycle)
515 break;
516 pptr->flags &= ~LASTENTRY;
518 if (pptr->number != cycle) {
519 pptr->flags &= ~LASTENTRY;
520 *cycptr = (pointselect *)realloc(*cycptr,
521 (cycles + 2) * sizeof(pointselect));
522 pptr = *cycptr + cycles + 1;
523 pptr->number = cycle;
524 pptr->flags = (flags == 0) ? EDITX | EDITY : flags;
525 pptr->flags |= LASTENTRY;
527 else {
528 pptr->flags |= (flags == 0) ? EDITX | EDITY : flags;
531 break;
533 return pptr;
536 /*--------------------------------------------------------------*/
537 /* If we edit the position of a control point, and the global */
538 /* pathedit mode is set to TANGENTS, then we track the angle of */
539 /* the adjoining curve, if there is one, by settings its cycle */
540 /* flags to ANTIXY. */
541 /*--------------------------------------------------------------*/
543 void addanticycle(pathptr thispath, splineptr thisspline, short cycle)
545 genericptr *ggen, *rgen;
546 splineptr otherspline;
548 if (areawin->pathedit == TANGENTS) {
549 for (ggen = thispath->plist; ggen < thispath->plist + thispath->parts;
550 ggen++)
551 if (*ggen == (genericptr)thisspline) break;
553 if (*ggen != (genericptr)thisspline) return; /* Something went wrong */
555 if (cycle == 1) {
556 if (ggen > thispath->plist) {
557 if (ELEMENTTYPE(*(ggen - 1)) == SPLINE) {
558 addcycle(ggen - 1, 2, ANTIXY);
561 else if (!(thispath->style & UNCLOSED)) {
562 rgen = thispath->plist + thispath->parts - 1;
563 if (ELEMENTTYPE(*rgen) == SPLINE) {
564 otherspline = TOSPLINE(rgen);
565 if (thisspline->ctrl[0].x == otherspline->ctrl[3].x &&
566 thisspline->ctrl[0].y == otherspline->ctrl[3].y)
567 addcycle(rgen, 2, ANTIXY);
571 else if (cycle == 2) { /* cycle should be only 1 or 2 */
572 if (ggen < thispath->plist + thispath->parts - 1) {
573 if (ELEMENTTYPE(*(ggen + 1)) == SPLINE) {
574 addcycle(ggen + 1, 1, ANTIXY);
577 else if (!(thispath->style & UNCLOSED)) {
578 rgen = thispath->plist;
579 if (ELEMENTTYPE(*rgen) == SPLINE) {
580 otherspline = TOSPLINE(rgen);
581 if (thisspline->ctrl[3].x == otherspline->ctrl[0].x &&
582 thisspline->ctrl[3].y == otherspline->ctrl[0].y)
583 addcycle(rgen, 1, ANTIXY);
590 /*--------------------------------------------------------------*/
591 /* Find the cycle numbered "cycle", and mark it as the */
592 /* reference point. */
593 /*--------------------------------------------------------------*/
595 void makerefcycle(pointselect *cycptr, short cycle)
597 pointselect *pptr, *sptr;
599 for (pptr = cycptr;; pptr++) {
600 if (pptr->flags & REFERENCE) {
601 pptr->flags &= ~REFERENCE;
602 break;
604 if (pptr->flags & LASTENTRY) break;
607 for (sptr = cycptr;; sptr++) {
608 if (sptr->number == cycle) {
609 sptr->flags |= REFERENCE;
610 break;
612 if (sptr->flags & LASTENTRY) break;
615 /* If something went wrong, revert to the original reference */
617 if (!(sptr->flags & REFERENCE)) {
618 pptr->flags |= REFERENCE;
622 /* Original routine, used 1st entry as reference (deprecated) */
624 void makefirstcycle(pointselect *cycptr, short cycle)
626 pointselect *pptr, tmpp;
628 for (pptr = cycptr;; pptr++) {
629 if (pptr->number == cycle) {
630 /* swap with first entry */
631 tmpp = *cycptr;
632 *cycptr = *pptr;
633 *pptr = tmpp;
634 if (cycptr->flags & LASTENTRY) {
635 cycptr->flags &= ~LASTENTRY;
636 pptr->flags |= LASTENTRY;
638 return;
640 if (pptr->flags & LASTENTRY) break;
644 /*------------------------------------------------------------------------------*/
645 /* Advance a cycle (point) selection from value "cycle" to value "newvalue" */
646 /* If "newvalue" is < 0 then remove the cycle. */
647 /* */
648 /* If there is only one cycle point on the element, then advance its point */
649 /* number. If there are multiple points on the element, then change the */
650 /* reference point by moving the last item in the list to the front. */
651 /*------------------------------------------------------------------------------*/
653 void advancecycle(genericptr *pgen, short newvalue)
655 polyptr ppoly;
656 arcptr parc;
657 splineptr pspline;
658 labelptr plabel;
659 pointselect *pptr, *endptr, *fcycle, **cycptr, tmpcyc;
660 short cycles = 0;
662 if (newvalue < 0) {
663 removecycle(pgen);
664 return;
667 switch((*pgen)->type) {
668 case POLYGON:
669 ppoly = TOPOLY(pgen);
670 cycptr = &ppoly->cycle;
671 break;
672 case ARC:
673 parc = TOARC(pgen);
674 cycptr = &parc->cycle;
675 break;
676 case SPLINE:
677 pspline = TOSPLINE(pgen);
678 cycptr = &pspline->cycle;
679 break;
680 case LABEL:
681 plabel = TOLABEL(pgen);
682 cycptr = &plabel->cycle;
683 break;
685 if (*cycptr == NULL) return;
687 /* Remove any cycles that have only X or Y flags set. */
688 /* "Remove" them by shuffling them to the end of the list, */
689 /* and marking the one in front as the last entry. */
691 for (endptr = *cycptr; !(endptr->flags & LASTENTRY); endptr++);
692 pptr = *cycptr;
693 while (pptr < endptr) {
694 if ((pptr->flags & (EDITX | EDITY)) != (EDITX | EDITY)) {
695 tmpcyc = *endptr;
696 *endptr = *pptr;
697 *pptr = tmpcyc;
698 pptr->flags &= ~LASTENTRY;
699 endptr->number = -1;
700 endptr--;
701 endptr->flags |= LASTENTRY;
703 else
704 pptr++;
707 if (pptr->flags & LASTENTRY) {
708 if ((pptr->flags & (EDITX | EDITY)) != (EDITX | EDITY)) {
709 pptr->flags &= ~LASTENTRY;
710 pptr->number = -1;
711 endptr--;
712 endptr->flags |= LASTENTRY;
716 /* Now advance the cycle */
718 pptr = *cycptr;
719 if (pptr->flags & LASTENTRY) {
720 pptr->number = newvalue;
722 else {
723 fcycle = *cycptr;
724 for (pptr = fcycle + 1;; pptr++) {
725 if (pptr->flags & (EDITX | EDITY))
726 fcycle = pptr;
727 if (pptr->flags & LASTENTRY) break;
729 makerefcycle(*cycptr, fcycle->number);
733 /*--------------------------------------*/
734 /* Remove a cycle (point) selection */
735 /*--------------------------------------*/
737 void removecycle(genericptr *pgen)
739 polyptr ppoly;
740 pathptr ppath;
741 arcptr parc;
742 splineptr pspline;
743 labelptr plabel;
744 pointselect *pptr, **cycptr = NULL;
745 genericptr *pathgen;
747 switch((*pgen)->type) {
748 case POLYGON:
749 ppoly = TOPOLY(pgen);
750 cycptr = &ppoly->cycle;
751 break;
752 case ARC:
753 parc = TOARC(pgen);
754 cycptr = &parc->cycle;
755 break;
756 case SPLINE:
757 pspline = TOSPLINE(pgen);
758 cycptr = &pspline->cycle;
759 break;
760 case LABEL:
761 plabel = TOLABEL(pgen);
762 cycptr = &plabel->cycle;
763 break;
764 case PATH:
765 ppath = TOPATH(pgen);
766 for (pathgen = ppath->plist; pathgen < ppath->plist + ppath->parts;
767 pathgen++)
768 removecycle(pathgen);
769 break;
771 if (cycptr == NULL) return;
772 if (*cycptr == NULL) return;
773 free(*cycptr);
774 *cycptr = NULL;
777 /*--------------------------------------*/
778 /* Remove cycles from all parts of a */
779 /* path other than the one passed */
780 /*--------------------------------------*/
782 void removeothercycles(pathptr ppath, genericptr pathpart)
784 genericptr *pathgen;
785 for (pathgen = ppath->plist; pathgen < ppath->plist + ppath->parts;
786 pathgen++)
787 if (*pathgen != pathpart)
788 removecycle(pathgen);
791 /*--------------------------------------*/
792 /* Select one of the elements on-screen */
793 /*--------------------------------------*/
795 selection *genselectelement(short class, u_char mode, objectptr selobj,
796 objinstptr selinst)
798 selection *rselect = NULL;
799 /* short *newselect; (jdk) */
800 genericptr *curgen;
801 XPoint newboxpts[4];
802 Boolean selected;
803 float range = RANGE_NARROW;
805 if (mode == MODE_RECURSE_WIDE)
806 range = RANGE_WIDE;
808 /* Loop through all elements found underneath the cursor */
810 for (curgen = selobj->plist; curgen < selobj->plist + selobj->parts; curgen++) {
812 selected = False;
814 /* Check among polygons, arcs, and curves */
816 if (((*curgen)->type == (class & POLYGON)) ||
817 ((*curgen)->type == (class & ARC)) ||
818 ((*curgen)->type == (class & SPLINE))) {
819 selected = pathselect(curgen, class, range);
822 else if ((*curgen)->type == (class & LABEL)) {
824 /* Look among the labels */
826 labelptr curlab = TOLABEL(curgen);
828 /* Don't select temporary labels from schematic capture system */
829 if (curlab->string->type != FONT_NAME) continue;
831 labelbbox(curlab, newboxpts, selinst);
833 /* Need to check for zero-size boxes or test_insideness() */
834 /* fails. Zero-size boxes happen when labels are parameters */
835 /* set to a null string. */
837 if ((newboxpts[0].x != newboxpts[1].x) || (newboxpts[0].y !=
838 newboxpts[1].y)) {
840 /* check for point inside bounding box, as for objects */
842 selected = test_insideness(areawin->save.x, areawin->save.y,
843 newboxpts);
844 if (selected) areawin->textpos = areawin->textend = 0;
848 else if ((*curgen)->type == (class & GRAPHIC)) {
850 /* Look among the graphic images */
852 graphicptr curg = TOGRAPHIC(curgen);
853 graphicbbox(curg, newboxpts);
855 /* check for point inside bounding box, as for objects */
856 selected = test_insideness(areawin->save.x, areawin->save.y,
857 newboxpts);
860 else if ((*curgen)->type == (class & PATH)) {
862 /* Check among the paths */
864 genericptr *pathp;
866 /* Accept path if any subcomponent of the path is accepted */
868 for (pathp = TOPATH(curgen)->plist; pathp < TOPATH(curgen)->plist +
869 TOPATH(curgen)->parts; pathp++)
870 if (pathselect(pathp, SPLINE|ARC|POLYGON, range)) {
871 selected = True;
872 break;
876 else if ((*curgen)->type == (class & OBJINST)) {
878 objinstbbox(TOOBJINST(curgen), newboxpts, range);
880 /* Look for an intersect of the boundingbox and pointer position. */
881 /* This is a simple matter of rotating the pointer position with */
882 /* respect to the origin of the bounding box segment, as if the */
883 /* segment were rotated to 0 degrees. The sign of the resulting */
884 /* point's y-position is the same for all bbox segments if the */
885 /* pointer position is inside the bounding box. */
887 selected = test_insideness(areawin->save.x, areawin->save.y,
888 newboxpts);
891 /* Add this object to the list of things found under the cursor */
893 if (selected) {
894 if (rselect == NULL) {
895 rselect = (selection *)malloc(sizeof(selection));
896 rselect->selectlist = (short *)malloc(sizeof(short));
897 rselect->selects = 0;
898 rselect->thisinst = selinst;
899 rselect->next = NULL;
901 else {
902 rselect->selectlist = (short *)realloc(rselect->selectlist,
903 (rselect->selects + 1) * sizeof(short));
905 *(rselect->selectlist + rselect->selects) = (short)(curgen -
906 selobj->plist);
907 rselect->selects++;
910 return rselect;
913 /*----------------------------------------------------------------*/
914 /* select arc, curve, and polygon objects from a defined box area */
915 /*----------------------------------------------------------------*/
917 Boolean areaelement(genericptr *curgen, XPoint *boxpts, Boolean is_path, short level)
919 Boolean selected;
920 pointlist currentpt;
921 short cycle;
923 switch(ELEMENTTYPE(*curgen)) {
925 case(ARC):
926 /* check center of arcs */
928 selected = test_insideness(TOARC(curgen)->position.x,
929 TOARC(curgen)->position.y, boxpts);
930 break;
932 case(POLYGON):
933 /* check each point of the polygons */
935 selected = False;
936 cycle = 0;
937 for (currentpt = TOPOLY(curgen)->points; currentpt <
938 TOPOLY(curgen)->points + TOPOLY(curgen)->number;
939 currentpt++, cycle++) {
940 if (test_insideness(currentpt->x, currentpt->y, boxpts)) {
941 selected = True;
942 if (level == 0) addcycle(curgen, cycle, 0);
945 break;
947 case(SPLINE):
948 /* check each control point of the spline */
950 selected = False;
951 if (test_insideness(TOSPLINE(curgen)->ctrl[0].x,
952 TOSPLINE(curgen)->ctrl[0].y, boxpts)) {
953 selected = True;
954 if (level == 0) addcycle(curgen, 0, 0);
957 if (test_insideness(TOSPLINE(curgen)->ctrl[3].x,
958 TOSPLINE(curgen)->ctrl[3].y, boxpts)) {
959 selected = True;
960 if (level == 0) addcycle(curgen, 3, 0);
962 break;
964 return selected;
967 /*--------------------------------------------*/
968 /* select all objects from a defined box area */
969 /*--------------------------------------------*/
971 Boolean selectarea(objectptr selobj, XPoint *boxpts, short level)
973 short *newselect;
974 genericptr *curgen, *pathgen;
975 Boolean selected;
976 stringpart *strptr;
977 int locpos, cx, cy, hwidth, hheight;
978 objinstptr curinst;
979 XPoint newboxpts[4];
981 if (selobj == topobject) {
982 areawin->textpos = areawin->textend = 0;
985 for (curgen = selobj->plist; curgen < selobj->plist + selobj->parts; curgen++) {
987 /* apply the selection filter */
988 if (!((*curgen)->type & areawin->filter)) continue;
990 switch(ELEMENTTYPE(*curgen)) {
991 case(OBJINST):
992 curinst = TOOBJINST(curgen);
994 /* An object instance is selected if any part of it is */
995 /* selected on a recursive area search. */
997 InvTransformPoints(boxpts, newboxpts, 4, curinst->position,
998 curinst->scale, curinst->rotation);
999 selected = selectarea(curinst->thisobject, newboxpts, level + 1);
1000 break;
1002 case(GRAPHIC):
1003 /* check for graphic image center point inside area box */
1004 selected = test_insideness(TOGRAPHIC(curgen)->position.x,
1005 TOGRAPHIC(curgen)->position.y, boxpts);
1006 break;
1008 case(LABEL): {
1009 XPoint adj;
1010 labelptr slab = TOLABEL(curgen);
1011 short j, state, isect, tmpl1, tmpl2;
1012 int padding;
1013 TextExtents tmpext;
1014 TextLinesInfo tlinfo;
1016 selected = False;
1018 /* Ignore temporary labels created by the netlist generator */
1019 if (slab->string->type != FONT_NAME) break;
1021 /* Ignore info and pin labels that are not on the top level */
1022 if ((selobj != topobject) && (slab->pin != False)) break;
1024 /* translate select box into the coordinate system of the label */
1025 InvTransformPoints(boxpts, newboxpts, 4, slab->position,
1026 slab->scale, slab->rotation);
1028 if (slab->pin) {
1029 for (j = 0; j < 4; j++)
1030 pinadjust(slab->anchor, &(newboxpts[j].x),
1031 &(newboxpts[j].y), -1);
1034 tlinfo.dostop = 0;
1035 tlinfo.tbreak = NULL;
1036 tlinfo.padding = NULL;
1038 tmpext = ULength(slab, areawin->topinstance, &tlinfo);
1039 adj.x = (slab->anchor & NOTLEFT ? (slab->anchor & RIGHT ?
1040 tmpext.maxwidth : tmpext.maxwidth >> 1) : 0);
1041 adj.y = (slab->anchor & NOTBOTTOM ? (slab->anchor & TOP ?
1042 tmpext.ascent : (tmpext.ascent + tmpext.base) >> 1)
1043 : tmpext.base);
1045 /* Label selection: For each character in the label string, */
1046 /* do an insideness test with the select box. */
1048 state = tmpl2 = 0;
1049 for (j = 0; j < stringlength(slab->string, True, areawin->topinstance); j++) {
1050 strptr = findstringpart(j, &locpos, slab->string, areawin->topinstance);
1051 if (locpos < 0) continue; /* only look at printable characters */
1052 if (strptr->type == RETURN) tmpl2 = 0;
1053 tmpl1 = tmpl2;
1054 tlinfo.dostop = j + 1;
1055 tmpext = ULength(slab, areawin->topinstance, &tlinfo);
1056 tmpl2 = tmpext.maxwidth;
1057 if ((slab->anchor & JUSTIFYRIGHT) && tlinfo.padding)
1058 padding = (int)tlinfo.padding[tlinfo.line];
1059 else if ((slab->anchor & TEXTCENTERED) && tlinfo.padding)
1060 padding = (int)(0.5 * tlinfo.padding[tlinfo.line]);
1061 else
1062 padding = 0;
1063 isect = test_insideness(((tmpl1 + tmpl2) >> 1) - adj.x + padding,
1064 (tmpext.base + (BASELINE >> 1)) - adj.y, newboxpts);
1066 /* tiny state machine */
1067 if (state == 0) {
1068 if (isect) {
1069 state = 1;
1070 selected = True;
1071 areawin->textend = j;
1072 if ((areawin->textend > 1) && strptr->type != TEXT_STRING)
1073 areawin->textend--;
1076 else {
1077 if (!isect) {
1078 areawin->textpos = j;
1079 state = 2;
1080 break;
1084 if (state == 1) areawin->textpos = j; /* selection goes to end of string */
1086 if (tlinfo.padding != NULL) free(tlinfo.padding);
1088 /* If a label happens to be empty (can happen in the case of */
1089 /* a label with parameters that are all empty strings), then */
1090 /* check if the bounding box surrounds the label position. */
1092 else if (tmpext.width == 0) {
1093 isect = test_insideness(0, 0, newboxpts);
1094 if (isect) {
1095 selected = True;
1096 areawin->textend = 1;
1100 } break;
1102 case(PATH):
1103 /* check position point of each subpart of the path */
1105 selected = False;
1106 for (pathgen = TOPATH(curgen)->plist; pathgen < TOPATH(curgen)->plist
1107 + TOPATH(curgen)->parts; pathgen++) {
1108 if (areaelement(pathgen, boxpts, True, level)) selected = True;
1110 break;
1112 default:
1113 selected = areaelement(curgen, boxpts, False, level);
1114 break;
1117 /* on recursive searches, return as soon as we find something */
1119 if ((selobj != topobject) && selected) return TRUE;
1121 /* check if this part has already been selected */
1123 if (selected)
1124 for (newselect = areawin->selectlist; newselect <
1125 areawin->selectlist + areawin->selects; newselect++)
1126 if (*newselect == (short)(curgen - topobject->plist))
1127 selected = False;
1129 /* add to list of selections */
1131 if (selected) {
1132 newselect = allocselect();
1133 *newselect = (short)(curgen - topobject->plist);
1136 if (selobj != topobject) return FALSE;
1137 setoptionmenu();
1139 /* if none or > 1 label has been selected, cancel any textpos placement */
1141 if (!checkselect(LABEL) || areawin->selects != 1 ||
1142 (areawin->selects == 1 && SELECTTYPE(areawin->selectlist) != LABEL)) {
1143 areawin->textpos = areawin->textend = 0;
1146 /* Register the selection as an undo event */
1147 register_for_undo(XCF_Select, UNDO_DONE, areawin->topinstance,
1148 areawin->selectlist, areawin->selects);
1150 /* Drawing of selected objects will take place when drawarea() is */
1151 /* executed after the button release. */
1153 #ifdef TCL_WRAPPER
1154 if (xobjs.suspend < 0)
1155 XcInternalTagCall(xcinterp, 2, "select", "here");
1156 #endif
1158 return selected;
1161 /*------------------------*/
1162 /* start deselection mode */
1163 /*------------------------*/
1165 void startdesel(xcWidget w, caddr_t clientdata, caddr_t calldata)
1167 if (eventmode == NORMAL_MODE) {
1168 if (areawin->selects == 0)
1169 Wprintf("Nothing to deselect!");
1170 else if (areawin->selects == 1)
1171 unselect_all();
1175 /*------------------------------------------------------*/
1176 /* Redraw all the selected objects in the select color. */
1177 /*------------------------------------------------------*/
1179 void draw_all_selected()
1181 int j;
1183 if (areawin->hierstack != NULL) return;
1185 for (j = 0; j < areawin->selects; j++)
1186 gendrawselected(areawin->selectlist + j, topobject, areawin->topinstance);
1189 /*---------------------------------------------------------*/
1190 /* Redraw all the selected objects in their normal colors. */
1191 /*---------------------------------------------------------*/
1193 void draw_normal_selected(objectptr thisobj, objinstptr thisinst)
1195 short saveselects;
1197 if (areawin->selects == 0) return;
1198 else if (areawin->hierstack != NULL) return;
1200 saveselects = areawin->selects;
1202 areawin->selects = 0;
1203 drawarea(NULL, NULL, NULL);
1204 areawin->selects = saveselects;
1207 /*----------------------------------------------------------------------*/
1208 /* Free a selection linked-list structure */
1209 /* (don't confuse with freeselects) */
1210 /*----------------------------------------------------------------------*/
1212 static void freeselection(selection *rselect)
1214 selection *nextselect;
1216 while (rselect != NULL) {
1217 nextselect = rselect->next;
1218 free(rselect->selectlist);
1219 free(rselect);
1220 rselect = nextselect;
1224 /*--------------------------------------------------------------*/
1225 /* Free memory from the previous selection list, copy the */
1226 /* current selection list to the previous selection list, and */
1227 /* zero out the current selection list. */
1228 /* Normally one would use clearselects(); use freeselects() */
1229 /* only if the menu/toolbars are going to be updated later in */
1230 /* the call. */
1231 /*--------------------------------------------------------------*/
1233 void freeselects()
1235 if (areawin->selects > 0) {
1236 free(areawin->selectlist);
1237 areawin->redraw_needed =True;
1239 areawin->selects = 0;
1240 free_stack(&areawin->hierstack);
1243 /*--------------------------------------------------------------*/
1244 /* Free memory from the selection list and set menu/toolbar */
1245 /* items back to default values. */
1246 /*--------------------------------------------------------------*/
1248 void clearselects_noundo()
1250 if (areawin->selects > 0) {
1251 reset_cycles();
1252 freeselects();
1253 if (xobjs.suspend < 0) {
1254 setallstylemarks(areawin->style);
1255 setcolormark(areawin->color);
1256 setdefaultfontmarks();
1257 setparammarks(NULL);
1260 #ifdef TCL_WRAPPER
1261 if (xobjs.suspend < 0)
1262 XcInternalTagCall(xcinterp, 2, "unselect", "all");
1263 #endif
1267 /*--------------------------------------------------------------*/
1268 /* Same as above, but registers an undo event. */
1269 /*--------------------------------------------------------------*/
1271 void clearselects()
1273 if (areawin->selects > 0) {
1274 register_for_undo(XCF_Select, UNDO_DONE, areawin->topinstance,
1275 NULL, 0);
1276 clearselects_noundo();
1280 /*--------------------------------------------------------------*/
1281 /* Unselect all the selected elements and free memory from the */
1282 /* selection list. */
1283 /*--------------------------------------------------------------*/
1285 void unselect_all()
1287 if (xobjs.suspend < 0)
1288 draw_normal_selected(topobject, areawin->topinstance);
1289 clearselects();
1292 /*----------------------------------------------------------------------*/
1293 /* Select the nearest element, searching the hierarchy if necessary. */
1294 /* Return an pushlist pointer to a linked list containing the */
1295 /* hierarchy of objects, with the topmost pushlist also containing a */
1296 /* pointer to the polygon found. */
1297 /* Allocates memory for the returned linked list which must be freed by */
1298 /* the calling routine. */
1299 /*----------------------------------------------------------------------*/
1301 selection *recurselect(short class, u_char mode, pushlistptr *seltop)
1303 selection *rselect, *rcheck, *lastselect;
1304 genericptr rgen;
1305 short i;
1306 objectptr selobj;
1307 objinstptr selinst;
1308 XPoint savesave, tmppt;
1309 pushlistptr selnew;
1310 short j, unselects;
1311 u_char locmode = (mode == MODE_CONNECT) ? UNDO_DONE : mode;
1312 u_char recmode = (mode != MODE_CONNECT) ? MODE_RECURSE_WIDE : MODE_RECURSE_NARROW;
1314 if (*seltop == NULL) {
1315 Fprintf(stderr, "Error: recurselect called with NULL pushlist pointer\n");
1316 return NULL;
1319 selinst = (*seltop)->thisinst;
1320 selobj = selinst->thisobject;
1322 class &= areawin->filter; /* apply the selection filter */
1324 unselects = 0;
1325 rselect = genselectelement(class, locmode, selobj, selinst);
1326 if (rselect == NULL) return NULL;
1328 for (i = 0; i < rselect->selects; i++) {
1329 rgen = *(selobj->plist + (*(rselect->selectlist + i)));
1330 if (rgen->type == OBJINST) {
1331 selinst = TOOBJINST(selobj->plist + (*(rselect->selectlist + i)));
1333 /* Link hierarchy information to the pushlist linked list */
1334 selnew = (pushlistptr)malloc(sizeof(pushlist));
1335 selnew->thisinst = selinst;
1336 selnew->next = NULL;
1337 (*seltop)->next = selnew;
1339 /* Translate areawin->save into object's own coordinate system */
1340 savesave.x = areawin->save.x;
1341 savesave.y = areawin->save.y;
1342 InvTransformPoints(&areawin->save, &tmppt, 1, selinst->position,
1343 selinst->scale, selinst->rotation);
1344 areawin->save.x = tmppt.x;
1345 areawin->save.y = tmppt.y;
1346 /* Fprintf(stdout, "objinst %s found in object %s; searching recursively\n",
1347 selinst->thisobject->name, selobj->name); */
1348 /* Fprintf(stdout, "cursor position originally (%d, %d); "
1349 "in new object is (%d, %d)\n",
1350 savesave.x, savesave.y,
1351 areawin->save.x, areawin->save.y); */
1353 rcheck = recurselect(ALL_TYPES, recmode, &selnew);
1354 areawin->save.x = savesave.x;
1355 areawin->save.y = savesave.y;
1357 /* If rgen is NULL, remove selected object from the list, and */
1358 /* remove the last entry from the pushlist stack. */
1360 if (rcheck == NULL) {
1361 *(rselect->selectlist + i) = -1;
1362 unselects++;
1363 (*seltop)->next = NULL;
1364 if (selnew->next != NULL)
1365 Fprintf(stderr, "Error: pushstack was freed, but was not empty!\n");
1366 free(selnew);
1368 else {
1369 for (lastselect = rselect; lastselect->next != NULL; lastselect =
1370 lastselect->next);
1371 lastselect->next = rcheck;
1376 /* Modify the selection list */
1378 for (i = 0, j = 0; i < rselect->selects; i++) {
1379 if (*(rselect->selectlist + i) >= 0) {
1380 if (i != j)
1381 *(rselect->selectlist + j) = *(rselect->selectlist + i);
1382 j++;
1385 rselect->selects -= unselects;
1386 if (rselect->selects == 0) {
1387 freeselection(rselect);
1388 rselect = NULL;
1390 return rselect;
1393 /*----------------------------------*/
1394 /* Start drawing a select area box. */
1395 /*----------------------------------*/
1397 void startselect()
1399 eventmode = SELAREA_MODE;
1400 areawin->origin.x = areawin->save.x;
1401 areawin->origin.y = areawin->save.y;
1402 selarea_mode_draw(xcDRAW_INIT, NULL);
1404 #ifdef TCL_WRAPPER
1405 Tk_CreateEventHandler(areawin->area, ButtonMotionMask |
1406 PointerMotionMask, (Tk_EventProc *)xctk_drag,
1407 NULL);
1408 #else
1409 xcAddEventHandler(areawin->area, ButtonMotionMask |
1410 PointerMotionMask, False, (xcEventHandler)xlib_drag,
1411 NULL);
1412 #endif
1416 /*-------------------------*/
1417 /* Track a select area box */
1418 /*-------------------------*/
1420 void trackselarea()
1422 XPoint newpos;
1423 /* u_int nullui; (jdk) */
1425 newpos = UGetCursorPos();
1426 if (newpos.x == areawin->save.x && newpos.y == areawin->save.y) return;
1428 areawin->save.x = newpos.x;
1429 areawin->save.y = newpos.y;
1430 selarea_mode_draw(xcDRAW_EDIT, NULL);
1433 /*----------------------*/
1434 /* Track a rescale box */
1435 /*----------------------*/
1437 void trackrescale()
1439 XPoint newpos;
1441 newpos = UGetCursorPos();
1442 if (newpos.x == areawin->save.x && newpos.y == areawin->save.y) return;
1444 areawin->save.x = newpos.x;
1445 areawin->save.y = newpos.y;
1446 rescale_mode_draw(xcDRAW_EDIT, NULL);
1449 /*----------------------------------------------------------------------*/
1450 /* Polygon distance comparison function for qsort */
1451 /*----------------------------------------------------------------------*/
1453 int dcompare(const void *a, const void *b)
1455 XPoint cpt;
1456 genericptr agen, bgen;
1457 short j, k, adist, bdist;
1459 cpt.x = areawin->save.x;
1460 cpt.y = areawin->save.y;
1462 j = *((short *)a);
1463 k = *((short *)b);
1465 agen = *(topobject->plist + j);
1466 bgen = *(topobject->plist + k);
1468 if (agen->type != POLYGON || bgen->type != POLYGON) return 0;
1470 adist = closedistance((polyptr)agen, &cpt);
1471 bdist = closedistance((polyptr)bgen, &cpt);
1473 if (adist == bdist) return 0;
1474 return (adist < bdist) ? 1 : -1;
1477 /*----------------------------------------------------------------------*/
1478 /* Compare two selection linked lists */
1479 /*----------------------------------------------------------------------*/
1481 Boolean compareselection(selection *sa, selection *sb)
1483 int i, j, match;
1484 short n1, n2;
1486 if ((sa == NULL) || (sb == NULL)) return False;
1487 if (sa->selects != sb->selects) return False;
1488 match = 0;
1489 for (i = 0; i < sa->selects; i++) {
1490 n1 = *(sa->selectlist + i);
1491 for (j = 0; j < sb->selects; j++) {
1492 n2 = *(sb->selectlist + j);
1493 if (n1 == n2) {
1494 match++;
1495 break;
1499 return (match == sa->selects) ? True : False;
1502 /*----------------------------------------------------------------------*/
1503 /* Add pin cycles connected to selected labels */
1504 /*----------------------------------------------------------------------*/
1506 void label_connect_cycles(labelptr thislab)
1508 genericptr *pgen;
1509 Boolean is_selected;
1510 XPoint *testpt;
1511 polyptr cpoly;
1512 short *stest, cycle;
1514 if (thislab->pin == LOCAL || thislab->pin == GLOBAL) {
1515 for (pgen = topobject->plist; pgen < topobject->plist +
1516 topobject->parts; pgen++) {
1517 /* Ignore any wires that are already selected */
1518 is_selected = FALSE;
1519 for (stest = areawin->selectlist; stest < areawin->selectlist +
1520 areawin->selects; stest++) {
1521 if (SELTOGENERIC(stest) == *pgen) {
1522 is_selected = TRUE;
1523 break;
1526 if (ELEMENTTYPE(*pgen) == POLYGON) {
1527 cpoly = TOPOLY(pgen);
1528 if (!is_selected) {
1529 cycle = 0;
1530 for (testpt = cpoly->points; testpt < cpoly->points +
1531 cpoly->number; testpt++) {
1532 if (testpt->x == thislab->position.x
1533 && testpt->y == thislab->position.y) {
1534 addcycle(pgen, cycle, 0);
1535 break;
1537 else
1538 cycle++;
1541 else {
1542 /* Make sure that this polygon's cycle is not set! */
1543 removecycle(pgen);
1550 /*----------------------------------------------------------------------*/
1551 /* Add pin cycles connected to selected instances */
1552 /*----------------------------------------------------------------------*/
1554 void inst_connect_cycles(objinstptr thisinst)
1556 genericptr *ggen, *pgen;
1557 Boolean is_selected;
1558 XPoint refpoint, *testpt;
1559 labelptr clab;
1560 polyptr cpoly;
1561 short *stest, cycle;
1562 objectptr thisobj = thisinst->thisobject;
1564 for (ggen = thisobj->plist; ggen < thisobj->plist + thisobj->parts; ggen++) {
1565 if (ELEMENTTYPE(*ggen) == LABEL) {
1566 clab = TOLABEL(ggen);
1567 if (clab->pin == LOCAL || clab->pin == GLOBAL) {
1568 ReferencePosition(thisinst, &clab->position, &refpoint);
1569 for (pgen = topobject->plist; pgen < topobject->plist +
1570 topobject->parts; pgen++) {
1571 /* Ignore any wires that are already selected */
1572 is_selected = FALSE;
1573 for (stest = areawin->selectlist; stest < areawin->selectlist +
1574 areawin->selects; stest++) {
1575 if (SELTOGENERIC(stest) == *pgen) {
1576 is_selected = TRUE;
1577 break;
1580 if (ELEMENTTYPE(*pgen) == POLYGON) {
1581 cpoly = TOPOLY(pgen);
1582 if (!is_selected) {
1583 cycle = 0;
1584 for (testpt = cpoly->points; testpt < cpoly->points +
1585 cpoly->number; testpt++) {
1586 if (testpt->x == refpoint.x && testpt->y == refpoint.y) {
1587 addcycle(pgen, cycle, 0);
1588 break;
1590 else
1591 cycle++;
1594 else {
1595 /* Make sure that this polygon's cycle is not set! */
1596 removecycle(pgen);
1605 /*----------------------------------------------------------------------*/
1606 /* Select connected pins on all selected object instances and labels */
1607 /*----------------------------------------------------------------------*/
1609 void select_connected_pins()
1611 short *selptr;
1612 objinstptr selinst;
1613 labelptr sellab;
1615 if (!areawin->pinattach) return;
1617 for (selptr = areawin->selectlist; selptr < areawin->selectlist +
1618 areawin->selects; selptr++) {
1619 switch (SELECTTYPE(selptr)) {
1620 case LABEL:
1621 sellab = SELTOLABEL(selptr);
1622 label_connect_cycles(sellab);
1623 break;
1624 case OBJINST:
1625 selinst = SELTOOBJINST(selptr);
1626 inst_connect_cycles(selinst);
1627 break;
1632 /*----------------------------------------------------------------------*/
1633 /* Reset all polygon cycles flagged during a move (polygon wires */
1634 /* connected to pins of an object instance). */
1635 /*----------------------------------------------------------------------*/
1637 void reset_cycles()
1639 polyptr cpoly;
1640 genericptr *pgen;
1642 for (pgen = topobject->plist; pgen < topobject->plist +
1643 topobject->parts; pgen++)
1644 removecycle(pgen);
1647 /*----------------------------------------------------------------------*/
1648 /* Recursive selection mechanism */
1649 /*----------------------------------------------------------------------*/
1651 short *recurse_select_element(short class, u_char mode) {
1652 pushlistptr seltop, nextptr;
1653 selection *rselect;
1654 short *newselect, localpick; /* *desel, (jdk) */
1655 static short pick = 0;
1656 static selection *saveselect = NULL;
1657 int i, j, k, ilast, jlast;
1658 Boolean unselect = False;
1660 seltop = (pushlistptr)malloc(sizeof(pushlist));
1661 seltop->thisinst = areawin->topinstance;
1662 seltop->next = NULL;
1664 /* Definition for unselecting an element */
1666 if (class < 0) {
1667 unselect = True;
1668 class = -class;
1670 rselect = recurselect(class, mode, &seltop);
1672 if (rselect) {
1673 /* Order polygons according to nearest point distance. */
1674 qsort((void *)rselect->selectlist, (size_t)rselect->selects,
1675 sizeof(short), dcompare);
1677 if (compareselection(rselect, saveselect))
1678 pick++;
1679 else
1680 pick = 0;
1682 localpick = pick % rselect->selects;
1685 /* Mechanism for unselecting elements under the cursor */
1686 /* (Unselect all picked objects) */
1688 if (rselect && unselect) {
1690 ilast = -1;
1691 k = 0;
1692 for (i = 0; i < rselect->selects; i++) {
1693 for (j = 0; j < areawin->selects; j++) {
1694 if (*(areawin->selectlist + j) == *(rselect->selectlist + i)) {
1695 jlast = j;
1696 ilast = i;
1697 if (++k == localpick)
1698 break;
1701 if (j < areawin->selects) break;
1703 if (ilast >= 0) {
1704 newselect = rselect->selectlist + ilast;
1705 areawin->redraw_needed = True;
1706 areawin->selects--;
1707 for (k = jlast; k < areawin->selects; k++)
1708 *(areawin->selectlist + k) = *(areawin->selectlist + k + 1);
1710 if (areawin->selects == 0) freeselects();
1712 /* Register the selection as an undo event */
1713 register_for_undo(XCF_Select, mode, areawin->topinstance,
1714 areawin->selectlist, areawin->selects);
1718 else if (rselect) {
1720 /* Mechanism for selecting objects: */
1721 /* Count all elements from rselect that are part of */
1722 /* the current selection. Pick the "pick"th item (modulo */
1723 /* total number of items). */
1725 ilast = -1;
1726 k = 0;
1727 for (i = 0; i < rselect->selects; i++) {
1728 for (j = 0; j < areawin->selects; j++) {
1729 if (*(areawin->selectlist + j) == *(rselect->selectlist + i))
1730 break;
1732 if (j == areawin->selects) {
1733 ilast = i;
1734 if (++k == localpick)
1735 break;
1739 if (ilast >= 0) {
1740 newselect = allocselect();
1741 *newselect = *(rselect->selectlist + ilast);
1742 areawin->redraw_needed = True;
1743 setoptionmenu();
1744 u2u_snap(&areawin->save);
1746 /* Register the selection as an undo event */
1747 /* (only if selection changed) */
1749 register_for_undo(XCF_Select, mode, areawin->topinstance,
1750 areawin->selectlist, areawin->selects);
1754 /* Cleanup */
1756 while (seltop != NULL) {
1757 nextptr = seltop->next;
1758 free(seltop);
1759 seltop = nextptr;
1762 freeselection(saveselect);
1763 saveselect = rselect;
1765 #ifdef TCL_WRAPPER
1766 if (xobjs.suspend < 0)
1767 XcInternalTagCall(xcinterp, 2, "select", "here");
1768 #endif
1770 return areawin->selectlist;