1 /*-------------------------------------------------------------------------*/
2 /* selection.c --- xcircuit routines handling element selection etc.	   */
3 /* Copyright (c) 2002  Tim Edwards, Johns Hopkins University       	   */
4 /*-------------------------------------------------------------------------*/
5 
6 /*-------------------------------------------------------------------------*/
7 /*      written by Tim Edwards, 8/13/93    				   */
8 /*-------------------------------------------------------------------------*/
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <math.h>
14 
15 #ifndef XC_WIN32
16 #include <X11/Intrinsic.h>
17 #include <X11/StringDefs.h>
18 #endif
19 
20 #ifdef TCL_WRAPPER
21 #include <tk.h>
22 #else
23 #ifndef XC_WIN32
24 #include "Xw/Xw.h"
25 #endif
26 #endif
27 
28 /*-------------------------------------------------------------------------*/
29 /* Local includes							   */
30 /*-------------------------------------------------------------------------*/
31 
32 #include "xcircuit.h"
33 #include "colordefs.h"
34 
35 /*----------------------------------------------------------------------*/
36 /* Function prototype declarations                                      */
37 /*----------------------------------------------------------------------*/
38 #include "prototypes.h"
39 
40 /*----------------------------------------------------------------------*/
41 /* Exported Variable definitions					*/
42 /*----------------------------------------------------------------------*/
43 
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;
51 
52 #ifdef TCL_WRAPPER
53 extern Tcl_Interp *xcinterp;
54 #endif
55 
56 /*----------------------------------------------------------------------*/
57 /* Prevent a list of elements from being selected.			*/
58 /*----------------------------------------------------------------------*/
59 
disable_selects(objectptr thisobject,short * selectlist,int selects)60 void disable_selects(objectptr thisobject, short *selectlist, int selects)
61 {
62    genericptr genptr;
63    short *i;
64 
65    for (i = selectlist; i < selectlist + selects; i++) {
66       genptr = *(thisobject->plist + *i);
67       genptr->type |= SELECT_HIDE;
68    }
69 }
70 
71 /*----------------------------------------------------------------------*/
72 /* Allow a list of elements to be selected, if they were disabled using	*/
73 /* the disable_selects() routine.					*/
74 /*----------------------------------------------------------------------*/
75 
enable_selects(objectptr thisobject,short * selectlist,int selects)76 void enable_selects(objectptr thisobject, short *selectlist, int selects)
77 {
78    genericptr genptr;
79    short *i;
80 
81    for (i = selectlist; i < selectlist + selects; i++) {
82       genptr = *(thisobject->plist + *i);
83       genptr->type &= ~(SELECT_HIDE);
84    }
85 }
86 
87 /*----------------------------------------------------------------------*/
88 /* Change filter to determine what types can be selected		*/
89 /*----------------------------------------------------------------------*/
90 
selectfilter(xcWidget w,pointertype value,caddr_t calldata)91 void selectfilter(xcWidget w, pointertype value, caddr_t calldata)
92 {
93    short bitwise = (short)value;
94    Boolean bval = (areawin->filter & bitwise) ? True : False;
95 
96    if (bval)
97       areawin->filter &= ~bitwise;
98    else
99       areawin->filter |= bitwise;
100 
101 #ifndef TCL_WRAPPER
102    toggle(w, (pointertype)&bval, calldata);
103 #endif
104 }
105 
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 /*----------------------------------------------------------------------*/
111 
checkselect_draw(short value,Boolean draw_selected)112 Boolean checkselect_draw(short value, Boolean draw_selected)
113 {
114    short *check;
115 
116    value &= areawin->filter;	/* apply the selection filter */
117 
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;
125       }
126    }
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;
133 }
134 
135 /*----------------------------------------------------------------------------*/
136 /* Look at select stack to see if there are any selects; call select, if not. */
137 /*----------------------------------------------------------------------------*/
138 
checkselect(short value)139 Boolean checkselect(short value)
140 {
141     return checkselect_draw(value, False);
142 }
143 
144 /*--------------------------------------------------------------*/
145 /* Select list numbering revision when an element is deleted	*/
146 /* from an object. 						*/
147 /*--------------------------------------------------------------*/
148 
reviseselect(short * slist,int selects,short * removed)149 void reviseselect(short *slist, int selects, short *removed)
150 {
151    short *chkselect;
152 
153    for (chkselect = slist; chkselect < slist + selects; chkselect++)
154       if (*chkselect > *removed) (*chkselect)--;
155 }
156 
157 /*----------------------*/
158 /* Draw a selected item */
159 /*----------------------*/
160 
geneasydraw(short instance,int mode,objectptr curobj,objinstptr curinst)161 void geneasydraw(short instance, int mode, objectptr curobj, objinstptr curinst)
162 {
163    genericptr elementptr = *(curobj->plist + instance);
164 
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 */
173 
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;
197    }
198 #ifdef HAVE_CAIRO
199    cairo_restore(areawin->cr);
200 #else
201    areawin->clipped = 0;
202 #endif /* HAVE_CAIRO */
203 }
204 
205 /*-------------------------------------------------*/
206 /* Draw a selected item, including selection color */
207 /*-------------------------------------------------*/
208 
gendrawselected(short * newselect,objectptr curobj,objinstptr curinst)209 void gendrawselected(short *newselect, objectptr curobj, objinstptr curinst)
210 {
211    if (*newselect >= curobj->parts) return;	// Safety check
212 
213    XcSetForeground(SELECTCOLOR);
214    geneasydraw(*newselect, DOFORALL, curobj, curinst);
215 
216    SetForeground(dpy, areawin->gc, AUXCOLOR);
217    indicateparams(*(curobj->plist + *newselect));
218 
219    SetForeground(dpy, areawin->gc, areawin->gccolor);
220 }
221 
222 /*---------------------------------------------------*/
223 /* Allocate or reallocate memory for a new selection */
224 /*---------------------------------------------------*/
225 
allocselect()226 short *allocselect()
227 {
228    short *newselect;
229 
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));
235 
236    newselect = areawin->selectlist + areawin->selects;
237    areawin->selects++;
238 
239    return newselect;
240 }
241 
242 /*-------------------------------------------------*/
243 /* Set Options menu according to 1st selection	   */
244 /*-------------------------------------------------*/
245 
setoptionmenu()246 void setoptionmenu()
247 {
248    short      *mselect;
249    labelptr   mlabel;
250 
251    if (areawin->selects == 0) {
252       setallstylemarks(areawin->style);
253       setcolormark(areawin->color);
254       setdefaultfontmarks();
255       setparammarks(NULL);
256       return;
257    }
258 
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;
280       }
281    }
282 }
283 
284 /*-------------------------------------*/
285 /* Test of point being inside of a box */
286 /*-------------------------------------*/
287 
test_insideness(int tx,int ty,XPoint * boxpoints)288 int test_insideness(int tx, int ty, XPoint *boxpoints)
289 {
290    int i, stval = 0;
291    XPoint *pt1, *pt2;
292    int stdir;
293 
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);
300    }
301    return (abs(stval) == 4) ? 1 : 0;
302 }
303 
304 /*--------------------------------------------*/
305 /* Search for selection among path components */
306 /*--------------------------------------------*/
307 
308 #define RANGE_NARROW 11.5
309 #define RANGE_WIDE 50
310 
pathselect(genericptr * curgen,short class,float range)311 Boolean pathselect(genericptr *curgen, short class, float range)
312 {
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    /*----------------------------------------------------------------------*/
324 
325    float	wirelim = 2 + range / (areawin->vscale + 0.05);
326    long		sqrwirelim = (int)(wirelim * wirelim);
327 
328    long		newdist;
329    Boolean	selected = False;
330 
331    class &= areawin->filter;	/* apply the selection filter */
332 
333    if ((*curgen)->type == (class & ARC)) {
334 
335       /* look among the arcs */
336 
337       fpointlist currentpt;
338       XPoint nearpt[3];
339 
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;
350       }
351       if ((!(TOARC(curgen)->style & UNCLOSED)) && (newdist > sqrwirelim))
352 	 newdist = finddist(&nearpt[0], &nearpt[2], &areawin->save);
353 
354       if (newdist <= sqrwirelim) selected = True;
355    }
356 
357    else if ((*curgen)->type == (class & SPLINE)) {
358 
359       /* look among the splines --- look at polygon representation */
360 
361       fpointlist currentpt;
362       XPoint nearpt[2];
363 
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;
377 	 }
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);
384 	 }
385       }
386 
387       if (newdist <= sqrwirelim) selected = True;
388    }
389 
390    else if ((*curgen)->type == (class & POLYGON)) {
391 
392       /* finally, look among the polygons */
393 
394       pointlist currentpt;
395 
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;
400       }
401       if ((!(TOPOLY(curgen)->style & UNCLOSED)) && (newdist > sqrwirelim))
402 	 newdist = finddist(currentpt, TOPOLY(curgen)->points,
403 		&areawin->save);
404 
405       if (newdist <= sqrwirelim) selected = True;
406    }
407    return selected;
408 }
409 
410 /*------------------------------------------------------*/
411 /* Check to see if any selection has registered cycles	*/
412 /*------------------------------------------------------*/
413 
checkforcycles(short * selectlist,int selects)414 Boolean  checkforcycles(short *selectlist, int selects)
415 {
416    genericptr pgen;
417    pointselect *cycptr;
418    short *ssel;
419 
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;
435       }
436       if (cycptr != NULL)
437 	 if (cycptr->number != -1)
438 	    return True;
439    }
440    return False;
441 }
442 
443 /*--------------------------------------------------------------*/
444 /* Copy a cycle selection list from one element to another	*/
445 /*--------------------------------------------------------------*/
446 
copycycles(pointselect ** new,pointselect ** old)447 void copycycles(pointselect **new, pointselect **old)
448 {
449    pointselect *pptr;
450    short cycles = 0;
451 
452    if (*old == NULL) {
453       *new = NULL;
454       return;
455    }
456 
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));
461 }
462 
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 /*--------------------------------------------------------------*/
470 
addcycle(genericptr * pgen,short cycle,u_char flags)471 pointselect *addcycle(genericptr *pgen, short cycle, u_char flags)
472 {
473    polyptr ppoly;
474    arcptr parc;
475    splineptr pspline;
476    labelptr plabel;
477    pointselect *pptr, **cycptr;
478    short cycles = 0;
479 
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;
497    }
498 
499    switch((*pgen)->type) {
500       case POLYGON:
501       case ARC:
502       case SPLINE:
503       case LABEL:		// To-do:  Handle labels separately
504 
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;
511 	 }
512 	 else {
513 	    for (pptr = *cycptr; !(pptr->flags & LASTENTRY); pptr++, cycles++) {
514 	       if (pptr->number == cycle)
515 		  break;
516 	       pptr->flags &= ~LASTENTRY;
517 	    }
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;
526 	    }
527 	    else {
528 	       pptr->flags |= (flags == 0) ? EDITX | EDITY : flags;
529 	    }
530 	 }
531          break;
532    }
533    return pptr;
534 }
535 
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 /*--------------------------------------------------------------*/
542 
addanticycle(pathptr thispath,splineptr thisspline,short cycle)543 void addanticycle(pathptr thispath, splineptr thisspline, short cycle)
544 {
545    genericptr *ggen, *rgen;
546    splineptr otherspline;
547 
548    if (areawin->pathedit == TANGENTS) {
549       for (ggen = thispath->plist; ggen < thispath->plist + thispath->parts;
550 		ggen++)
551 	 if (*ggen == (genericptr)thisspline) break;
552 
553       if (*ggen != (genericptr)thisspline) return;	/* Something went wrong */
554 
555       if (cycle == 1) {
556 	 if (ggen > thispath->plist) {
557 	    if (ELEMENTTYPE(*(ggen - 1)) == SPLINE) {
558 	       addcycle(ggen - 1, 2, ANTIXY);
559 	    }
560 	 }
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);
568 	    }
569 	 }
570       }
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);
575 	    }
576 	 }
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);
584 	    }
585 	 }
586       }
587    }
588 }
589 
590 /*--------------------------------------------------------------*/
591 /* Find the cycle numbered "cycle", and mark it as the		*/
592 /* reference point.						*/
593 /*--------------------------------------------------------------*/
594 
makerefcycle(pointselect * cycptr,short cycle)595 void makerefcycle(pointselect *cycptr, short cycle)
596 {
597    pointselect *pptr, *sptr;
598 
599    for (pptr = cycptr;; pptr++) {
600       if (pptr->flags & REFERENCE) {
601 	 pptr->flags &= ~REFERENCE;
602 	 break;
603       }
604       if (pptr->flags & LASTENTRY) break;
605    }
606 
607    for (sptr = cycptr;; sptr++) {
608       if (sptr->number == cycle) {
609 	 sptr->flags |= REFERENCE;
610 	 break;
611       }
612       if (sptr->flags & LASTENTRY) break;
613    }
614 
615    /* If something went wrong, revert to the original reference */
616 
617    if (!(sptr->flags & REFERENCE)) {
618       pptr->flags |= REFERENCE;
619    }
620 }
621 
622 /* Original routine, used 1st entry as reference (deprecated) */
623 
makefirstcycle(pointselect * cycptr,short cycle)624 void makefirstcycle(pointselect *cycptr, short cycle)
625 {
626    pointselect *pptr, tmpp;
627 
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;
637 	 }
638 	 return;
639       }
640       if (pptr->flags & LASTENTRY) break;
641    }
642 }
643 
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 /*------------------------------------------------------------------------------*/
652 
advancecycle(genericptr * pgen,short newvalue)653 void advancecycle(genericptr *pgen, short newvalue)
654 {
655    polyptr ppoly;
656    arcptr parc;
657    splineptr pspline;
658    labelptr plabel;
659    pointselect *pptr, *endptr, *fcycle, **cycptr, tmpcyc;
660    short cycles = 0;
661 
662    if (newvalue < 0) {
663       removecycle(pgen);
664       return;
665    }
666 
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;
684    }
685    if (*cycptr == NULL) return;
686 
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.		*/
690 
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;
702       }
703       else
704 	 pptr++;
705    }
706 
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;
713       }
714    }
715 
716    /* Now advance the cycle */
717 
718    pptr = *cycptr;
719    if (pptr->flags & LASTENTRY) {
720       pptr->number = newvalue;
721    }
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;
728       }
729       makerefcycle(*cycptr, fcycle->number);
730    }
731 }
732 
733 /*--------------------------------------*/
734 /* Remove a cycle (point) selection	*/
735 /*--------------------------------------*/
736 
removecycle(genericptr * pgen)737 void removecycle(genericptr *pgen)
738 {
739    polyptr ppoly;
740    pathptr ppath;
741    arcptr parc;
742    splineptr pspline;
743    labelptr plabel;
744    pointselect *pptr, **cycptr = NULL;
745    genericptr *pathgen;
746 
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;
770    }
771    if (cycptr == NULL) return;
772    if (*cycptr == NULL) return;
773    free(*cycptr);
774    *cycptr = NULL;
775 }
776 
777 /*--------------------------------------*/
778 /* Remove cycles from all parts of a	*/
779 /* path other than the one passed	*/
780 /*--------------------------------------*/
781 
removeothercycles(pathptr ppath,genericptr pathpart)782 void removeothercycles(pathptr ppath, genericptr pathpart)
783 {
784    genericptr *pathgen;
785    for (pathgen = ppath->plist; pathgen < ppath->plist + ppath->parts;
786 		pathgen++)
787       if (*pathgen != pathpart)
788 	 removecycle(pathgen);
789 }
790 
791 /*--------------------------------------*/
792 /* Select one of the elements on-screen */
793 /*--------------------------------------*/
794 
genselectelement(short class,u_char mode,objectptr selobj,objinstptr selinst)795 selection *genselectelement(short class, u_char mode, objectptr selobj,
796 		objinstptr selinst)
797 {
798    selection	*rselect = NULL;
799    /* short	*newselect; (jdk) */
800    genericptr	*curgen;
801    XPoint 	newboxpts[4];
802    Boolean	selected;
803    float	range = RANGE_NARROW;
804 
805    if (mode == MODE_RECURSE_WIDE)
806       range = RANGE_WIDE;
807 
808    /* Loop through all elements found underneath the cursor */
809 
810    for (curgen = selobj->plist; curgen < selobj->plist + selobj->parts; curgen++) {
811 
812       selected = False;
813 
814       /* Check among polygons, arcs, and curves */
815 
816       if (((*curgen)->type == (class & POLYGON)) ||
817 		((*curgen)->type == (class & ARC)) ||
818 		((*curgen)->type == (class & SPLINE))) {
819 	  selected = pathselect(curgen, class, range);
820       }
821 
822       else if ((*curgen)->type == (class & LABEL)) {
823 
824          /* Look among the labels */
825 
826 	 labelptr curlab = TOLABEL(curgen);
827 
828 	 /* Don't select temporary labels from schematic capture system */
829 	 if (curlab->string->type != FONT_NAME) continue;
830 
831 	 labelbbox(curlab, newboxpts, selinst);
832 
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.					*/
836 
837 	 if ((newboxpts[0].x != newboxpts[1].x) || (newboxpts[0].y !=
838 		newboxpts[1].y)) {
839 
840             /* check for point inside bounding box, as for objects */
841 
842 	    selected = test_insideness(areawin->save.x, areawin->save.y,
843 		newboxpts);
844 	    if (selected) areawin->textpos = areawin->textend = 0;
845 	 }
846       }
847 
848       else if ((*curgen)->type == (class & GRAPHIC)) {
849 
850          /* Look among the graphic images */
851 
852 	 graphicptr curg = TOGRAPHIC(curgen);
853 	 graphicbbox(curg, newboxpts);
854 
855          /* check for point inside bounding box, as for objects */
856 	 selected = test_insideness(areawin->save.x, areawin->save.y,
857 		newboxpts);
858       }
859 
860       else if ((*curgen)->type == (class & PATH)) {
861 
862          /* Check among the paths */
863 
864 	 genericptr *pathp;
865 
866 	 /* Accept path if any subcomponent of the path is accepted */
867 
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;
873 	    }
874       }
875 
876       else if ((*curgen)->type == (class & OBJINST)) {
877 
878 	 objinstbbox(TOOBJINST(curgen), newboxpts, range);
879 
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.		   */
886 
887 	 selected = test_insideness(areawin->save.x, areawin->save.y,
888 		newboxpts);
889       }
890 
891       /* Add this object to the list of things found under the cursor */
892 
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;
900 	 }
901          else {
902             rselect->selectlist = (short *)realloc(rselect->selectlist,
903 			(rselect->selects + 1) * sizeof(short));
904 	 }
905 	 *(rselect->selectlist + rselect->selects) = (short)(curgen -
906 		selobj->plist);
907 	 rselect->selects++;
908       }
909    }
910    return rselect;
911 }
912 
913 /*----------------------------------------------------------------*/
914 /* select arc, curve, and polygon objects from a defined box area */
915 /*----------------------------------------------------------------*/
916 
areaelement(genericptr * curgen,XPoint * boxpts,Boolean is_path,short level)917 Boolean areaelement(genericptr *curgen, XPoint *boxpts, Boolean is_path, short level)
918 {
919    Boolean selected;
920    pointlist    currentpt;
921    short	cycle;
922 
923    switch(ELEMENTTYPE(*curgen)) {
924 
925       case(ARC):
926 	   /* check center of arcs */
927 
928 	   selected = test_insideness(TOARC(curgen)->position.x,
929 		TOARC(curgen)->position.y, boxpts);
930 	   break;
931 
932       case(POLYGON):
933 	   /* check each point of the polygons */
934 
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);
943 	      }
944 	   }
945 	   break;
946 
947       case(SPLINE):
948 	   /* check each control point of the spline */
949 
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);
955 	   }
956 
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);
961 	   }
962 	   break;
963    }
964    return selected;
965 }
966 
967 /*--------------------------------------------*/
968 /* select all objects from a defined box area */
969 /*--------------------------------------------*/
970 
selectarea(objectptr selobj,XPoint * boxpts,short level)971 Boolean selectarea(objectptr selobj, XPoint *boxpts, short level)
972 {
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];
980 
981    if (selobj == topobject) {
982       areawin->textpos = areawin->textend = 0;
983    }
984 
985    for (curgen = selobj->plist; curgen < selobj->plist + selobj->parts; curgen++) {
986 
987       /* apply the selection filter */
988       if (!((*curgen)->type & areawin->filter)) continue;
989 
990       switch(ELEMENTTYPE(*curgen)) {
991 	case(OBJINST):
992 	    curinst = TOOBJINST(curgen);
993 
994 	    /* An object instance is selected if any part of it is	*/
995 	    /* selected on a recursive area search.			*/
996 
997             InvTransformPoints(boxpts, newboxpts, 4, curinst->position,
998 			curinst->scale, curinst->rotation);
999             selected = selectarea(curinst->thisobject, newboxpts, level + 1);
1000 	    break;
1001 
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;
1007 
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;
1015 
1016 	   selected = False;
1017 
1018 	   /* Ignore temporary labels created by the netlist generator */
1019 	   if (slab->string->type != FONT_NAME) break;
1020 
1021 	   /* Ignore info and pin labels that are not on the top level */
1022 	   if ((selobj != topobject) && (slab->pin != False)) break;
1023 
1024    	   /* translate select box into the coordinate system of the label */
1025 	   InvTransformPoints(boxpts, newboxpts, 4, slab->position,
1026 		  slab->scale, slab->rotation);
1027 
1028 	   if (slab->pin) {
1029 	      for (j = 0; j < 4; j++)
1030 		 pinadjust(slab->anchor, &(newboxpts[j].x),
1031 			&(newboxpts[j].y), -1);
1032 	   }
1033 
1034 	   tlinfo.dostop = 0;
1035 	   tlinfo.tbreak = NULL;
1036 	   tlinfo.padding = NULL;
1037 
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);
1044 
1045 	   /* Label selection:  For each character in the label string, */
1046 	   /* do an insideness test with the select box.		*/
1047 
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);
1065 
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--;
1074 		 }
1075 	      }
1076 	      else {
1077 		 if (!isect) {
1078 		    areawin->textpos = j;
1079 		    state = 2;
1080 		    break;
1081 		 }
1082 	      }
1083 	   }
1084 	   if (state == 1) areawin->textpos = j;   /* selection goes to end of string */
1085 
1086 	   if (tlinfo.padding != NULL) free(tlinfo.padding);
1087 
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.	*/
1091 
1092 	   else if (tmpext.width == 0) {
1093 	      isect = test_insideness(0, 0, newboxpts);
1094 	      if (isect) {
1095 		 selected = True;
1096 		 areawin->textend = 1;
1097 	      }
1098 	   }
1099 
1100 	   } break;
1101 
1102 	case(PATH):
1103 	   /* check position point of each subpart of the path */
1104 
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;
1109 	   }
1110 	   break;
1111 
1112 	default:
1113 	   selected = areaelement(curgen, boxpts, False, level);
1114 	   break;
1115       }
1116 
1117       /* on recursive searches, return as soon as we find something */
1118 
1119       if ((selobj != topobject) && selected) return TRUE;
1120 
1121       /* check if this part has already been selected */
1122 
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;
1128 
1129       /* add to list of selections */
1130 
1131       if (selected) {
1132          newselect = allocselect();
1133          *newselect = (short)(curgen - topobject->plist);
1134       }
1135    }
1136    if (selobj != topobject) return FALSE;
1137    setoptionmenu();
1138 
1139    /* if none or > 1 label has been selected, cancel any textpos placement */
1140 
1141    if (!checkselect(LABEL) || areawin->selects != 1 ||
1142 	(areawin->selects == 1 && SELECTTYPE(areawin->selectlist) != LABEL)) {
1143       areawin->textpos = areawin->textend = 0;
1144    }
1145 
1146    /* Register the selection as an undo event */
1147    register_for_undo(XCF_Select, UNDO_DONE, areawin->topinstance,
1148 		areawin->selectlist, areawin->selects);
1149 
1150    /* Drawing of selected objects will take place when drawarea() is */
1151    /* executed after the button release.			     */
1152 
1153 #ifdef TCL_WRAPPER
1154    if (xobjs.suspend < 0)
1155       XcInternalTagCall(xcinterp, 2, "select", "here");
1156 #endif
1157 
1158    return selected;
1159 }
1160 
1161 /*------------------------*/
1162 /* start deselection mode */
1163 /*------------------------*/
1164 
startdesel(xcWidget w,caddr_t clientdata,caddr_t calldata)1165 void startdesel(xcWidget w, caddr_t clientdata, caddr_t calldata)
1166 {
1167    if (eventmode == NORMAL_MODE) {
1168       if (areawin->selects == 0)
1169 	 Wprintf("Nothing to deselect!");
1170       else if (areawin->selects == 1)
1171 	 unselect_all();
1172    }
1173 }
1174 
1175 /*------------------------------------------------------*/
1176 /* Redraw all the selected objects in the select color.	*/
1177 /*------------------------------------------------------*/
1178 
draw_all_selected()1179 void draw_all_selected()
1180 {
1181    int j;
1182 
1183    if (areawin->hierstack != NULL) return;
1184 
1185    for (j = 0; j < areawin->selects; j++)
1186       gendrawselected(areawin->selectlist + j, topobject, areawin->topinstance);
1187 }
1188 
1189 /*---------------------------------------------------------*/
1190 /* Redraw all the selected objects in their normal colors. */
1191 /*---------------------------------------------------------*/
1192 
draw_normal_selected(objectptr thisobj,objinstptr thisinst)1193 void draw_normal_selected(objectptr thisobj, objinstptr thisinst)
1194 {
1195    short saveselects;
1196 
1197    if (areawin->selects == 0) return;
1198    else if (areawin->hierstack != NULL) return;
1199 
1200    saveselects = areawin->selects;
1201 
1202    areawin->selects = 0;
1203    drawarea(NULL, NULL, NULL);
1204    areawin->selects = saveselects;
1205 }
1206 
1207 /*----------------------------------------------------------------------*/
1208 /* Free a selection linked-list structure				*/
1209 /* (don't confuse with freeselects)					*/
1210 /*----------------------------------------------------------------------*/
1211 
freeselection(selection * rselect)1212 static void freeselection(selection *rselect)
1213 {
1214    selection *nextselect;
1215 
1216    while (rselect != NULL) {
1217       nextselect = rselect->next;
1218       free(rselect->selectlist);
1219       free(rselect);
1220       rselect = nextselect;
1221    }
1222 }
1223 
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 /*--------------------------------------------------------------*/
1232 
freeselects()1233 void freeselects()
1234 {
1235    if (areawin->selects > 0) {
1236       free(areawin->selectlist);
1237       areawin->redraw_needed =True;
1238    }
1239    areawin->selects = 0;
1240    free_stack(&areawin->hierstack);
1241 }
1242 
1243 /*--------------------------------------------------------------*/
1244 /* Free memory from the selection list and set menu/toolbar	*/
1245 /* items back to default values.				*/
1246 /*--------------------------------------------------------------*/
1247 
clearselects_noundo()1248 void clearselects_noundo()
1249 {
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);
1258       }
1259 
1260 #ifdef TCL_WRAPPER
1261       if (xobjs.suspend < 0)
1262          XcInternalTagCall(xcinterp, 2, "unselect", "all");
1263 #endif
1264    }
1265 }
1266 
1267 /*--------------------------------------------------------------*/
1268 /* Same as above, but registers an undo event.			*/
1269 /*--------------------------------------------------------------*/
1270 
clearselects()1271 void clearselects()
1272 {
1273    if (areawin->selects > 0) {
1274       register_for_undo(XCF_Select, UNDO_DONE, areawin->topinstance,
1275 		NULL, 0);
1276       clearselects_noundo();
1277    }
1278 }
1279 
1280 /*--------------------------------------------------------------*/
1281 /* Unselect all the selected elements and free memory from the	*/
1282 /* selection list.						*/
1283 /*--------------------------------------------------------------*/
1284 
unselect_all()1285 void unselect_all()
1286 {
1287    if (xobjs.suspend < 0)
1288       draw_normal_selected(topobject, areawin->topinstance);
1289    clearselects();
1290 }
1291 
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 /*----------------------------------------------------------------------*/
1300 
recurselect(short class,u_char mode,pushlistptr * seltop)1301 selection *recurselect(short class, u_char mode, pushlistptr *seltop)
1302 {
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;
1313 
1314    if (*seltop == NULL) {
1315       Fprintf(stderr, "Error: recurselect called with NULL pushlist pointer\n");
1316       return NULL;
1317    }
1318 
1319    selinst = (*seltop)->thisinst;
1320    selobj = selinst->thisobject;
1321 
1322    class &= areawin->filter;		/* apply the selection filter */
1323 
1324    unselects = 0;
1325    rselect = genselectelement(class, locmode, selobj, selinst);
1326    if (rselect == NULL) return NULL;
1327 
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)));
1332 
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;
1338 
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); */
1352 
1353          rcheck = recurselect(ALL_TYPES, recmode, &selnew);
1354          areawin->save.x = savesave.x;
1355          areawin->save.y = savesave.y;
1356 
1357 	 /* If rgen is NULL, remove selected object from the list, and	*/
1358 	 /* remove the last entry from the pushlist stack.		*/
1359 
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);
1367 	 }
1368 	 else {
1369 	    for (lastselect = rselect; lastselect->next != NULL; lastselect =
1370 			lastselect->next);
1371 	    lastselect->next = rcheck;
1372 	 }
1373       }
1374    }
1375 
1376    /* Modify the selection list */
1377 
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++;
1383       }
1384    }
1385    rselect->selects -= unselects;
1386    if (rselect->selects == 0) {
1387       freeselection(rselect);
1388       rselect = NULL;
1389    }
1390    return rselect;
1391 }
1392 
1393 /*----------------------------------*/
1394 /* Start drawing a select area box. */
1395 /*----------------------------------*/
1396 
startselect()1397 void startselect()
1398 {
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);
1403 
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
1413 
1414 }
1415 
1416 /*-------------------------*/
1417 /* Track a select area box */
1418 /*-------------------------*/
1419 
trackselarea()1420 void trackselarea()
1421 {
1422    XPoint newpos;
1423    /* u_int  nullui; (jdk) */
1424 
1425    newpos = UGetCursorPos();
1426    if (newpos.x == areawin->save.x && newpos.y == areawin->save.y) return;
1427 
1428    areawin->save.x = newpos.x;
1429    areawin->save.y = newpos.y;
1430    selarea_mode_draw(xcDRAW_EDIT, NULL);
1431 }
1432 
1433 /*----------------------*/
1434 /* Track a rescale box	*/
1435 /*----------------------*/
1436 
trackrescale()1437 void trackrescale()
1438 {
1439    XPoint newpos;
1440 
1441    newpos = UGetCursorPos();
1442    if (newpos.x == areawin->save.x && newpos.y == areawin->save.y) return;
1443 
1444    areawin->save.x = newpos.x;
1445    areawin->save.y = newpos.y;
1446    rescale_mode_draw(xcDRAW_EDIT, NULL);
1447 }
1448 
1449 /*----------------------------------------------------------------------*/
1450 /* Polygon distance comparison function for qsort			*/
1451 /*----------------------------------------------------------------------*/
1452 
dcompare(const void * a,const void * b)1453 int dcompare(const void *a, const void *b)
1454 {
1455    XPoint cpt;
1456    genericptr agen, bgen;
1457    short j, k, adist, bdist;
1458 
1459    cpt.x = areawin->save.x;
1460    cpt.y = areawin->save.y;
1461 
1462    j = *((short *)a);
1463    k = *((short *)b);
1464 
1465    agen = *(topobject->plist + j);
1466    bgen = *(topobject->plist + k);
1467 
1468    if (agen->type != POLYGON || bgen->type != POLYGON) return 0;
1469 
1470    adist = closedistance((polyptr)agen, &cpt);
1471    bdist = closedistance((polyptr)bgen, &cpt);
1472 
1473    if (adist == bdist) return 0;
1474    return (adist < bdist) ? 1 : -1;
1475 }
1476 
1477 /*----------------------------------------------------------------------*/
1478 /* Compare two selection linked lists					*/
1479 /*----------------------------------------------------------------------*/
1480 
compareselection(selection * sa,selection * sb)1481 Boolean compareselection(selection *sa, selection *sb)
1482 {
1483    int i, j, match;
1484    short n1, n2;
1485 
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;
1496 	 }
1497       }
1498    }
1499    return (match == sa->selects) ? True : False;
1500 }
1501 
1502 /*----------------------------------------------------------------------*/
1503 /* Add pin cycles connected to selected labels				*/
1504 /*----------------------------------------------------------------------*/
1505 
label_connect_cycles(labelptr thislab)1506 void label_connect_cycles(labelptr thislab)
1507 {
1508    genericptr *pgen;
1509    Boolean is_selected;
1510    XPoint *testpt;
1511    polyptr cpoly;
1512    short *stest, cycle;
1513 
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;
1524 	    }
1525 	 }
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;
1536 		  }
1537 		  else
1538 		     cycle++;
1539 	       }
1540 	    }
1541 	    else {
1542 	       /* Make sure that this polygon's cycle is not set! */
1543 	       removecycle(pgen);
1544 	    }
1545 	 }
1546       }
1547    }
1548 }
1549 
1550 /*----------------------------------------------------------------------*/
1551 /* Add pin cycles connected to selected instances			*/
1552 /*----------------------------------------------------------------------*/
1553 
inst_connect_cycles(objinstptr thisinst)1554 void inst_connect_cycles(objinstptr thisinst)
1555 {
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;
1563 
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;
1578 		  }
1579 	       }
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;
1589 		        }
1590 		        else
1591 		           cycle++;
1592 		     }
1593 		  }
1594 	          else {
1595 		     /* Make sure that this polygon's cycle is not set! */
1596 		     removecycle(pgen);
1597 		  }
1598 	       }
1599 	    }
1600 	 }
1601       }
1602    }
1603 }
1604 
1605 /*----------------------------------------------------------------------*/
1606 /* Select connected pins on all selected object instances and labels	*/
1607 /*----------------------------------------------------------------------*/
1608 
select_connected_pins()1609 void select_connected_pins()
1610 {
1611    short *selptr;
1612    objinstptr selinst;
1613    labelptr sellab;
1614 
1615    if (!areawin->pinattach) return;
1616 
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;
1628       }
1629    }
1630 }
1631 
1632 /*----------------------------------------------------------------------*/
1633 /* Reset all polygon cycles flagged during a move (polygon wires	*/
1634 /* connected to pins of an object instance).				*/
1635 /*----------------------------------------------------------------------*/
1636 
reset_cycles()1637 void reset_cycles()
1638 {
1639    polyptr cpoly;
1640    genericptr *pgen;
1641 
1642    for (pgen = topobject->plist; pgen < topobject->plist +
1643 			topobject->parts; pgen++)
1644       removecycle(pgen);
1645 }
1646 
1647 /*----------------------------------------------------------------------*/
1648 /* Recursive selection mechanism					*/
1649 /*----------------------------------------------------------------------*/
1650 
recurse_select_element(short class,u_char mode)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;
1659 
1660    seltop = (pushlistptr)malloc(sizeof(pushlist));
1661    seltop->thisinst = areawin->topinstance;
1662    seltop->next = NULL;
1663 
1664    /* Definition for unselecting an element */
1665 
1666    if (class < 0) {
1667       unselect = True;
1668       class = -class;
1669    }
1670    rselect = recurselect(class, mode, &seltop);
1671 
1672    if (rselect) {
1673       /* Order polygons according to nearest point distance. */
1674       qsort((void *)rselect->selectlist, (size_t)rselect->selects,
1675 		sizeof(short), dcompare);
1676 
1677       if (compareselection(rselect, saveselect))
1678 	 pick++;
1679       else
1680 	 pick = 0;
1681 
1682       localpick = pick % rselect->selects;
1683    }
1684 
1685    /* Mechanism for unselecting elements under the cursor	*/
1686    /* (Unselect all picked objects)				*/
1687 
1688    if (rselect && unselect) {
1689 
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;
1699 	    }
1700 	 }
1701 	 if (j < areawin->selects) break;
1702       }
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);
1709 
1710          if (areawin->selects == 0) freeselects();
1711 
1712          /* Register the selection as an undo event */
1713          register_for_undo(XCF_Select, mode, areawin->topinstance,
1714 		areawin->selectlist, areawin->selects);
1715       }
1716    }
1717 
1718    else if (rselect) {
1719 
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).				*/
1724 
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;
1731 	 }
1732 	 if (j == areawin->selects) {
1733 	    ilast = i;
1734 	    if (++k == localpick)
1735 	       break;
1736 	 }
1737       }
1738 
1739       if (ilast >= 0) {
1740          newselect = allocselect();
1741          *newselect = *(rselect->selectlist + ilast);
1742 	 areawin->redraw_needed = True;
1743          setoptionmenu();
1744          u2u_snap(&areawin->save);
1745 
1746          /* Register the selection as an undo event	*/
1747 	 /* (only if selection changed)			*/
1748 
1749          register_for_undo(XCF_Select, mode, areawin->topinstance,
1750 		areawin->selectlist, areawin->selects);
1751       }
1752    }
1753 
1754    /* Cleanup */
1755 
1756    while (seltop != NULL) {
1757       nextptr = seltop->next;
1758       free(seltop);
1759       seltop = nextptr;
1760    }
1761 
1762    freeselection(saveselect);
1763    saveselect = rselect;
1764 
1765 #ifdef TCL_WRAPPER
1766    if (xobjs.suspend < 0)
1767       XcInternalTagCall(xcinterp, 2, "select", "here");
1768 #endif
1769 
1770    return areawin->selectlist;
1771 }
1772